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

github-merge-queue[bot] pushed a commit to branch 
gh-readonly-queue/main/pr-5699-debfc0935f8a58985defbf7a8c4fdf34a2a5431e
in repository https://gitbox.apache.org/repos/asf/texera.git

commit 85d76de8cfb0a0957175ab26dc95d4d6d01bf7f9
Author: Xinyuan Lin <[email protected]>
AuthorDate: Tue Jun 16 13:42:17 2026 -0700

    test(workflow-core): add unit test coverage for storage result + dataset 
models (#5699)
    
    ### What changes were proposed in this PR?
    
    Pin behavior of two previously-uncovered storage-layer model classes in
    `common/workflow-core`. No production-code changes.
    
    | Spec | Source class | Tests |
    | --- | --- | --- |
    | `OperatorResultMetadataSpec` | `OperatorResultMetadata` | 7 |
    | `WorkflowResultStoreSpec` | `WorkflowResultStore` | 7 |
    
    Both spec files follow the `<srcClassName>Spec.scala` one-to-one
    convention.
    
    > **Note:** an earlier revision also added `OnDatasetSpec`. Per review,
    it was dropped — `OnDataset` is a pure abstract trait (three abstract
    accessors, no behavior), so the spec only exercised test-only stub
    subclasses rather than any production code. Only real implementations
    warrant tests.
    
    **Behavior pinned — `OperatorResultMetadata`**
    
    | Surface | Contract |
    | --- | --- |
    | `OperatorResultMetadata()` | defaults to `tupleCount == 0` and
    `changeDetector == ""` |
    | Custom-constructor values | preserved on both fields |
    | Equality | case-class equality compares both fields (differing values
    break it) |
    | `hashCode` | matches across equal instances |
    | `copy` | replaces only the field supplied, preserves the rest |
    
    **Behavior pinned — `WorkflowResultStore`**
    
    | Surface | Contract |
    | --- | --- |
    | `WorkflowResultStore()` | defaults `resultInfo` to `Map.empty` |
    | Custom map | every `(OperatorIdentity, OperatorResultMetadata)` entry
    preserved; missing keys read as `None` |
    | Equality | value-based on the inner Map; differing inner metadata
    breaks equality |
    | `copy(resultInfo = …)` | replaces the map without mutating the
    original (immutable case-class contract) |
    | `WorkflowResultStore()` vs `WorkflowResultStore(Map.empty)` | equal |
    
    ### Any related issues, documentation, discussions?
    
    Closes #5696.
    
    ### How was this PR tested?
    
    Pure unit-test additions; verified locally with:
    
    - `sbt "WorkflowCore/testOnly
    org.apache.texera.amber.core.storage.result.OperatorResultMetadataSpec
    org.apache.texera.amber.core.storage.result.WorkflowResultStoreSpec"` —
    14 tests, all green
    - `sbt scalafmtCheckAll` — clean
    - CI to confirm
    
    ### Was this PR authored or co-authored using generative AI tooling?
    
    Generated-by: Claude Code (Opus 4.7 [1M context])
---
 .../result/OperatorResultMetadataSpec.scala        |  81 ++++++++++++++++
 .../storage/result/WorkflowResultStoreSpec.scala   | 102 +++++++++++++++++++++
 2 files changed, 183 insertions(+)

diff --git 
a/common/workflow-core/src/test/scala/org/apache/texera/amber/core/storage/result/OperatorResultMetadataSpec.scala
 
b/common/workflow-core/src/test/scala/org/apache/texera/amber/core/storage/result/OperatorResultMetadataSpec.scala
new file mode 100644
index 0000000000..abb6100157
--- /dev/null
+++ 
b/common/workflow-core/src/test/scala/org/apache/texera/amber/core/storage/result/OperatorResultMetadataSpec.scala
@@ -0,0 +1,81 @@
+/*
+ * 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.core.storage.result
+
+import org.scalatest.flatspec.AnyFlatSpec
+
+class OperatorResultMetadataSpec extends AnyFlatSpec {
+
+  // 
---------------------------------------------------------------------------
+  // Defaults
+  // 
---------------------------------------------------------------------------
+
+  "OperatorResultMetadata()" should "default tupleCount to 0" in {
+    assert(OperatorResultMetadata().tupleCount == 0)
+  }
+
+  it should "default changeDetector to the empty string" in {
+    assert(OperatorResultMetadata().changeDetector == "")
+  }
+
+  // 
---------------------------------------------------------------------------
+  // Custom constructor values
+  // 
---------------------------------------------------------------------------
+
+  "OperatorResultMetadata(...)" should "preserve a custom tupleCount" in {
+    assert(OperatorResultMetadata(tupleCount = 42).tupleCount == 42)
+  }
+
+  it should "preserve a custom changeDetector" in {
+    assert(OperatorResultMetadata(changeDetector = "abc").changeDetector == 
"abc")
+  }
+
+  it should "preserve both fields together" in {
+    val m = OperatorResultMetadata(tupleCount = 7, changeDetector = "hash-x")
+    assert(m.tupleCount == 7)
+    assert(m.changeDetector == "hash-x")
+  }
+
+  // 
---------------------------------------------------------------------------
+  // Equality / hashCode (case class semantics)
+  // 
---------------------------------------------------------------------------
+
+  "OperatorResultMetadata equality" should "compare both fields" in {
+    val a = OperatorResultMetadata(1, "x")
+    val b = OperatorResultMetadata(1, "x")
+    val c = OperatorResultMetadata(1, "y")
+    val d = OperatorResultMetadata(2, "x")
+    assert(a == b)
+    assert(a.hashCode == b.hashCode)
+    assert(a != c, "differing changeDetector must break equality")
+    assert(a != d, "differing tupleCount must break equality")
+  }
+
+  // 
---------------------------------------------------------------------------
+  // copy semantics
+  // 
---------------------------------------------------------------------------
+
+  "OperatorResultMetadata.copy" should
+    "replace only the field that was supplied, preserving the rest" in {
+    val base = OperatorResultMetadata(5, "old-hash")
+    assert(base.copy(tupleCount = 10) == OperatorResultMetadata(10, 
"old-hash"))
+    assert(base.copy(changeDetector = "new-hash") == OperatorResultMetadata(5, 
"new-hash"))
+  }
+}
diff --git 
a/common/workflow-core/src/test/scala/org/apache/texera/amber/core/storage/result/WorkflowResultStoreSpec.scala
 
b/common/workflow-core/src/test/scala/org/apache/texera/amber/core/storage/result/WorkflowResultStoreSpec.scala
new file mode 100644
index 0000000000..55fb990544
--- /dev/null
+++ 
b/common/workflow-core/src/test/scala/org/apache/texera/amber/core/storage/result/WorkflowResultStoreSpec.scala
@@ -0,0 +1,102 @@
+/*
+ * 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.core.storage.result
+
+import org.apache.texera.amber.core.virtualidentity.OperatorIdentity
+import org.scalatest.flatspec.AnyFlatSpec
+
+class WorkflowResultStoreSpec extends AnyFlatSpec {
+
+  // 
---------------------------------------------------------------------------
+  // Fixtures
+  // 
---------------------------------------------------------------------------
+
+  private val opA = OperatorIdentity("op-a")
+  private val opB = OperatorIdentity("op-b")
+
+  // 
---------------------------------------------------------------------------
+  // Default state
+  // 
---------------------------------------------------------------------------
+
+  "WorkflowResultStore()" should "default resultInfo to Map.empty" in {
+    assert(WorkflowResultStore().resultInfo.isEmpty)
+  }
+
+  // 
---------------------------------------------------------------------------
+  // Custom map preserves entries
+  // 
---------------------------------------------------------------------------
+
+  "WorkflowResultStore(...)" should
+    "preserve a Map carrying metadata entries for multiple operators" in {
+    val store = WorkflowResultStore(
+      Map(
+        opA -> OperatorResultMetadata(10, "h-a"),
+        opB -> OperatorResultMetadata(20, "h-b")
+      )
+    )
+    assert(store.resultInfo.size == 2)
+    assert(store.resultInfo(opA) == OperatorResultMetadata(10, "h-a"))
+    assert(store.resultInfo(opB) == OperatorResultMetadata(20, "h-b"))
+  }
+
+  it should "preserve key identity (a missing key reads as None via .get)" in {
+    val store = WorkflowResultStore(Map(opA -> OperatorResultMetadata(1, "x")))
+    assert(store.resultInfo.get(opA).contains(OperatorResultMetadata(1, "x")))
+    assert(store.resultInfo.get(opB).isEmpty)
+  }
+
+  // 
---------------------------------------------------------------------------
+  // Equality / hashCode (case class semantics)
+  // 
---------------------------------------------------------------------------
+
+  "WorkflowResultStore equality" should
+    "compare resultInfo by value (two stores with the same Map are equal)" in {
+    val s1 = WorkflowResultStore(Map(opA -> OperatorResultMetadata(1, "x")))
+    val s2 = WorkflowResultStore(Map(opA -> OperatorResultMetadata(1, "x")))
+    val s3 = WorkflowResultStore(Map(opA -> OperatorResultMetadata(2, "x")))
+    val s4 = WorkflowResultStore(Map.empty)
+    assert(s1 == s2)
+    assert(s1.hashCode == s2.hashCode)
+    assert(s1 != s3, "differing inner metadata must break equality")
+    assert(s1 != s4, "differing map size must break equality")
+  }
+
+  // 
---------------------------------------------------------------------------
+  // copy semantics
+  // 
---------------------------------------------------------------------------
+
+  "WorkflowResultStore.copy" should "replace the resultInfo map" in {
+    val base = WorkflowResultStore(Map(opA -> OperatorResultMetadata(1, "x")))
+    val updated = base.copy(resultInfo = Map(opB -> OperatorResultMetadata(2, 
"y")))
+    assert(updated.resultInfo.keySet == Set(opB))
+    assert(updated.resultInfo(opB) == OperatorResultMetadata(2, "y"))
+    // Original is unchanged (immutable case-class semantics).
+    assert(base.resultInfo.keySet == Set(opA))
+  }
+
+  // 
---------------------------------------------------------------------------
+  // Default-arg construction
+  // 
---------------------------------------------------------------------------
+
+  "WorkflowResultStore (default-arg construction)" should
+    "equal WorkflowResultStore(Map.empty) — the default arg is `Map.empty`" in 
{
+    assert(WorkflowResultStore() == WorkflowResultStore(Map.empty))
+  }
+}

Reply via email to