This is an automated email from the ASF dual-hosted git repository.

aglinxinyuan pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/texera.git


The following commit(s) were added to refs/heads/main by this push:
     new aa8966250a test(amber): add unit test coverage for AddressInfo (#4916)
aa8966250a is described below

commit aa8966250a06c5d67e7d388f1c2f0a9646ca25b1
Author: Xinyuan Lin <[email protected]>
AuthorDate: Mon May 4 03:14:35 2026 -0700

    test(amber): add unit test coverage for AddressInfo (#4916)
    
    ### What changes were proposed in this PR?
    
    Adds `AddressInfoSpec` covering `AddressInfo`
    
(amber/src/main/scala/org/apache/texera/amber/engine/architecture/deploysemantics/AddressInfo.scala),
    the case class the cluster scheduler uses to describe worker /
    controller node addresses. It has no test coverage today.
    
    The new spec pins:
    - Both fields (`allAddresses`, `controllerAddress`) round-trip from the
    case-class constructor.
    - The order of `allAddresses` is preserved (the cluster scheduler picks
    workers based on this order, so any reorder is observable).
    - An empty `allAddresses` array is accepted (controller-only
    configurations).
    - The controller address may also appear in `allAddresses` (collocated
    mode).
    - `copy()` allows one field to change while preserving the other.
    - Case-class equality with an `Array` field is reference-based, not
    element-wise. This is locked down explicitly so a future change to (say)
    `Seq` doesn't silently flip the equality semantics for callers.
    
    No production code changed; this is test-only.
    
    ### Any related issues, documentation, discussions?
    
    Closes #4915
    
    ### How was this PR tested?
    
    Added 6 new unit tests in `AddressInfoSpec`. Verified locally:
    
    ```
    sbt 'WorkflowExecutionService/Test/testOnly 
org.apache.texera.amber.engine.architecture.deploysemantics.AddressInfoSpec'
    # → Tests: succeeded 6, failed 0
    
    sbt 'WorkflowExecutionService/Test/scalafmtCheck'
    # → clean
    ```
    
    ### Was this PR authored or co-authored using generative AI tooling?
    
    Generated-by: Claude Code
    
    ---------
    
    Co-authored-by: Claude Opus 4.7 (1M context) <[email protected]>
---
 .../deploysemantics/AddressInfoSpec.scala          | 84 ++++++++++++++++++++++
 1 file changed, 84 insertions(+)

diff --git 
a/amber/src/test/scala/org/apache/texera/amber/engine/architecture/deploysemantics/AddressInfoSpec.scala
 
b/amber/src/test/scala/org/apache/texera/amber/engine/architecture/deploysemantics/AddressInfoSpec.scala
new file mode 100644
index 0000000000..0ea16a47df
--- /dev/null
+++ 
b/amber/src/test/scala/org/apache/texera/amber/engine/architecture/deploysemantics/AddressInfoSpec.scala
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.texera.amber.engine.architecture.deploysemantics
+
+import org.apache.pekko.actor.Address
+import org.scalatest.flatspec.AnyFlatSpec
+
+class AddressInfoSpec extends AnyFlatSpec {
+
+  private def addr(host: String, port: Int): Address =
+    Address("pekko", "Amber", host, port)
+
+  "AddressInfo" should "expose the addresses it was constructed with" in {
+    val nodes = Array(addr("h1", 2552), addr("h2", 2552), addr("h3", 2552))
+    val controller = addr("ctrl", 2552)
+    val info = AddressInfo(nodes, controller)
+    assert(info.allAddresses.toList == nodes.toList)
+    assert(info.controllerAddress == controller)
+  }
+
+  it should "preserve the order of allAddresses" in {
+    // The cluster scheduler picks workers based on this list's order, so
+    // any reorder is observable.
+    val nodes = Array(addr("c", 1), addr("a", 2), addr("b", 3))
+    val info = AddressInfo(nodes, addr("ctrl", 0))
+    assert(info.allAddresses.map(_.host.get).toList == List("c", "a", "b"))
+  }
+
+  it should "accept an empty allAddresses array" in {
+    // Edge case: no worker nodes (e.g., controller-only configuration).
+    val info = AddressInfo(Array.empty[Address], addr("ctrl", 0))
+    assert(info.allAddresses.isEmpty)
+    assert(info.controllerAddress.host.contains("ctrl"))
+  }
+
+  it should "allow the controller to also appear in allAddresses (collocated)" 
in {
+    val controller = addr("ctrl", 2552)
+    val info = AddressInfo(Array(controller, addr("worker", 2552)), controller)
+    assert(info.allAddresses.contains(controller))
+    assert(info.controllerAddress == controller)
+  }
+
+  it should "support copy(), allowing one field to change while the other is 
preserved" in {
+    val a = AddressInfo(Array(addr("h1", 1)), addr("ctrl-a", 0))
+    val b = a.copy(controllerAddress = addr("ctrl-b", 0))
+    assert(b.controllerAddress.host.contains("ctrl-b"))
+    assert(b.allAddresses.toList == a.allAddresses.toList)
+    // original is unchanged
+    assert(a.controllerAddress.host.contains("ctrl-a"))
+  }
+
+  it should "use Array reference equality (not element-wise) for the 
allAddresses field" in {
+    // Case-class equality on `Array` fields uses array reference equality,
+    // not element-wise equality. Two AddressInfo values that hold the SAME
+    // array instance compare equal; two AddressInfo values that hold
+    // distinct arrays with the SAME elements do NOT. Lock this down so a
+    // future change to (say) Seq doesn't silently flip equality semantics
+    // for callers.
+    val nodes = Array(addr("h", 1))
+    val ctrl = addr("ctrl", 0)
+    val sameRef = AddressInfo(nodes, ctrl)
+    val sameRefAgain = AddressInfo(nodes, ctrl) // shares the same array 
reference
+    val differentRef = AddressInfo(Array(addr("h", 1)), ctrl) // different 
array reference
+    assert(sameRef == sameRefAgain, "shared Array reference → equal")
+    assert(sameRef != differentRef, "distinct Array references → not equal (no 
element-wise check)")
+  }
+}

Reply via email to