test(workflow-operator): add unit test coverage for OperatorDescriptorUtils + DistributedAggregation + URLFetchUtil - #5798
Conversation
…rUtils, DistributedAggregation, URLFetchUtil Pin the contract for three small utility classes/objects: - OperatorDescriptorUtils.equallyPartitionGoal: result size == workers; slots sum back to goal across goal=[0..20] x workers=[1..5]; even partition when divisible; remainder lands on the FIRST goal % workers slots; goal < workers edge case - OperatorDescriptorUtils.toImmutableMap: empty round-trip; preserves key/value pairs; static return type is scala.collection.immutable.Map - DistributedAggregation: case-class equality on same function refs; init produces zero partial; iterate folds one tuple in (sum, count); merge adds componentwise; finalAgg divides with zero-partial guard; end-to-end average of 1..5 == 3.0 single-node AND via merged-partial path - URLFetchUtil.getInputStreamFromURL: success returns Some(stream) with right bytes via file: URL; persistent-failure returns None at default retries, explicit retries=2, and retries=0; default retries == 5 verified via synthetic getInputStreamFromURL$default$2 accessor 23 new tests; sbt scalafmtCheck clean; no production-code changes.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #5798 +/- ##
============================================
- Coverage 53.30% 53.27% -0.04%
+ Complexity 2668 2663 -5
============================================
Files 1098 1098
Lines 42532 42532
Branches 4575 4575
============================================
- Hits 22673 22659 -14
- Misses 18530 18539 +9
- Partials 1329 1334 +5
*This pull request uses carry forward flags. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Pull request overview
Adds Scala unit-test coverage for three small utility surfaces in common/workflow-operator/, aiming to pin existing behavior without modifying production code.
Changes:
- Add
OperatorDescriptorUtilsSpecto validate partitioning behavior and Java→Scala map conversion. - Add
DistributedAggregationSpecto pin the case-class contract and a worked end-to-end average aggregation. - Add
URLFetchUtilSpecto cover success/failure paths and retry semantics for URL input streams.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| common/workflow-operator/src/test/scala/org/apache/texera/amber/operator/util/OperatorDescriptorUtilsSpec.scala | New spec for equallyPartitionGoal and toImmutableMap behavior. |
| common/workflow-operator/src/test/scala/org/apache/texera/amber/operator/aggregate/DistributedAggregationSpec.scala | New spec covering DistributedAggregation function members and average example. |
| common/workflow-operator/src/test/scala/org/apache/texera/amber/operator/source/fetcher/URLFetchUtilSpec.scala | New spec for URLFetchUtil.getInputStreamFromURL success/failure and retries. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
| config | throughput | MB/s | latency | max Δ latest / 7d | |
|---|---|---|---|---|---|
| 🔴 | bs=10 sw=10 sl=64 | 397 | 0.242 | 24,436/35,183/35,183 us | 🟢 -25.5% / ⚪ within ±5% |
| 🔴 | bs=100 sw=10 sl=64 | 830 | 0.506 | 116,595/154,145/154,145 us | 🔴 +9.1% / 🔴 +10.3% |
| ⚪ | bs=1000 sw=10 sl=64 | 948 | 0.578 | 1,058,901/1,093,153/1,093,153 us | ⚪ within ±5% / 🔴 -9.0% |
Baseline details
Latest main d513324 from same runner
| config | metric | PR | latest main | 7d avg | Δ latest | Δ 7d |
|---|---|---|---|---|---|---|
| bs=10 sw=10 sl=64 | throughput | 397 tuples/sec | 400 tuples/sec | 410.82 tuples/sec | -0.8% | -3.4% |
| bs=10 sw=10 sl=64 | MB/s | 0.242 MB/s | 0.244 MB/s | 0.251 MB/s | -0.8% | -3.5% |
| bs=10 sw=10 sl=64 | p50 | 24,436 us | 21,538 us | 23,785 us | +13.5% | +2.7% |
| bs=10 sw=10 sl=64 | p95 | 35,183 us | 47,232 us | 34,980 us | -25.5% | +0.6% |
| bs=10 sw=10 sl=64 | p99 | 35,183 us | 47,232 us | 34,980 us | -25.5% | +0.6% |
| bs=100 sw=10 sl=64 | throughput | 830 tuples/sec | 867 tuples/sec | 891.94 tuples/sec | -4.3% | -6.9% |
| bs=100 sw=10 sl=64 | MB/s | 0.506 MB/s | 0.529 MB/s | 0.544 MB/s | -4.3% | -7.1% |
| bs=100 sw=10 sl=64 | p50 | 116,595 us | 112,907 us | 112,277 us | +3.3% | +3.8% |
| bs=100 sw=10 sl=64 | p95 | 154,145 us | 141,286 us | 139,802 us | +9.1% | +10.3% |
| bs=100 sw=10 sl=64 | p99 | 154,145 us | 141,286 us | 139,802 us | +9.1% | +10.3% |
| bs=1000 sw=10 sl=64 | throughput | 948 tuples/sec | 959 tuples/sec | 1,041 tuples/sec | -1.1% | -8.9% |
| bs=1000 sw=10 sl=64 | MB/s | 0.578 MB/s | 0.585 MB/s | 0.635 MB/s | -1.2% | -9.0% |
| bs=1000 sw=10 sl=64 | p50 | 1,058,901 us | 1,046,928 us | 972,714 us | +1.1% | +8.9% |
| bs=1000 sw=10 sl=64 | p95 | 1,093,153 us | 1,074,763 us | 1,023,057 us | +1.7% | +6.9% |
| bs=1000 sw=10 sl=64 | p99 | 1,093,153 us | 1,074,763 us | 1,023,057 us | +1.7% | +6.9% |
Raw CSV
config_idx,batch_size,schema_width,string_len,num_batches,total_ms,total_tuples,total_bytes,tuples_per_sec,mb_per_sec,lat_p50_us,lat_p95_us,lat_p99_us
0,10,10,64,20,503.44,200,128000,397,0.242,24435.54,35182.71,35182.71
1,100,10,64,20,2410.62,2000,1280000,830,0.506,116595.48,154144.63,154144.63
2,1000,10,64,20,21107.21,20000,12800000,948,0.578,1058901.08,1093153.41,1093153.41…hUtilSpec; fix misleading comment Address Copilot review feedback on apache#5798: URLFetchUtilSpec — the failure/default tests only checked the final None, which would still pass if the retry loop ran the wrong number of times, and the default-retries test relied on Scala's synthetic getInputStreamFromURL$default$2 accessor name (compiler/version-specific). Replace both with a counting URLStreamHandler that records every openConnection call, then assert the EXACT number of attempts: - retries = 0 -> 0 connections opened (loop body never runs; proves no attempt) - retries = 1 -> exactly 1 - retries = 2 -> exactly 2 - default (no arg) -> exactly 5 (validates the default value behaviorally) - first-attempt success -> exactly 1 (proves early return, no extra connections) The realistic file:-URL success path is retained. OperatorDescriptorUtilsSpec — fix a comment that read '1 worker should get everything' on a call with totalNumWorkers = 4; it now reads '1 = 4*0 + 1 -> only the first slot gets the single unit'.
…ants (apache#5739) ### What changes were proposed in this PR? Adds a unit-test spec for `PropertyNameConstants` (the `@JsonProperty` wire-key constants shared across `LogicalOp` serialization). No production-code changes. | Spec | Source class | Tests | | --- | --- | --- | | `PropertyNameConstantsSpec` | `PropertyNameConstants` | 5 | > **Scope note:** this PR originally also bundled `OperatorDescriptorUtils` and `PortDescriptor`/`PortDescription` specs. Both have since been covered on `main` / by other open PRs: > - `OperatorDescriptorUtilsSpec` already merged via apache#5798 — dropped here (it was the merge conflict). > - `PortDescriptor` + `PortDescription` are covered by apache#5832 with one spec file per source class (the repo convention) — dropped here to avoid a duplicate `PortDescriptorSpec.scala`. > > This PR is now scoped to its unique contribution, `PropertyNameConstants`, and is rebased on current `main`. ### Any related issues, documentation, discussions? Follow-up test coverage; see apache#5798 and apache#5832 for the de-duplicated specs. ### How was this PR tested? - `sbt "WorkflowOperator/testOnly org.apache.texera.amber.operator.metadata.PropertyNameConstantsSpec"` — 5 tests, all green - `sbt "WorkflowOperator/Test/scalafmtCheck"` and `sbt "WorkflowOperator/Test/scalafix --check"` — clean - CI to confirm ### Was this PR authored or co-authored using generative AI tooling? Generated-by: Claude Code (Opus 4.8 [1M context])
…rUtils + DistributedAggregation + URLFetchUtil (apache#5798) ### What changes were proposed in this PR? Pin behavior of three small utility classes/objects in `common/workflow-operator/`. Each one is too thin to justify its own PR but cohesive as a bundle (utility surface). No production-code changes. | Spec | Source class | Tests | | --- | --- | --- | | `OperatorDescriptorUtilsSpec` | `OperatorDescriptorUtils` (object) | 8 | | `DistributedAggregationSpec` | `DistributedAggregation` (case class) | 9 | | `URLFetchUtilSpec` | `URLFetchUtil` (object) | 6 | All three spec files follow the `<srcClassName>Spec.scala` one-to-one convention. **Behavior pinned — `OperatorDescriptorUtils`** | Surface | Contract | | --- | --- | | `equallyPartitionGoal` size | result has exactly `totalNumWorkers` slots | | Sum invariant | slots sum back to `goal` across `goal ∈ [0..20]` × `workers ∈ [1..5]` | | Even partition | when `goal % workers == 0`, every slot is `goal / workers` | | Remainder placement | the first `goal % workers` slots get `+1` (in order) | | `goal < workers` edge case | first `goal` slots get `1`, the rest get `0` | | `toImmutableMap` empty | empty `java.util.Map` → empty Scala `Map` | | `toImmutableMap` preserves entries | round-trip preserves every key/value pair | | Return type | static type is `scala.collection.immutable.Map` (compile-time enforced) | **Behavior pinned — `DistributedAggregation`** | Surface | Contract | | --- | --- | | Case-class shape | all four function members reachable; equality on identical function refs | | `init` | produces the zero partial `(0L, 0L)` | | `iterate` | folds one tuple in: `sum += value`, `count += 1` | | `merge` | adds two partials componentwise | | `finalAgg` divides | `(15L, 5L) → 3.0d` | | `finalAgg` zero guard | `(0L, 0L) → 0.0d` (no divide-by-zero) | | End-to-end single-node | average of `1..5` via fold-left == `3.0` | | End-to-end with merge | same answer via two partial nodes + `merge` | **Behavior pinned — `URLFetchUtil`** | Surface | Contract | | --- | --- | | Success path | `getInputStreamFromURL(file:tempFile)` returns `Some(stream)` carrying the file's exact bytes | | Success with explicit retries | same, with `retries = 3` | | Failure path (default retries) | non-existent `file:` URL returns `None` | | Failure path (`retries = 0`) | loop iterates zero times → `None` immediately | | Failure path (`retries = 2`) | persistent failure exhausts retries → `None` | | Default arg value | `getInputStreamFromURL\$default\$2 == 5`, verified via Scala's synthetic default-accessor | The URLFetchUtil specs use the JVM's built-in `file:` URL handler against temporary files (success path) and non-existent paths (failure path) — no external network calls or process exec. ### Any related issues, documentation, discussions? Closes apache#5795. ### How was this PR tested? Pure unit-test additions; verified locally with: - `sbt \"WorkflowOperator/testOnly org.apache.texera.amber.operator.util.OperatorDescriptorUtilsSpec org.apache.texera.amber.operator.aggregate.DistributedAggregationSpec org.apache.texera.amber.operator.source.fetcher.URLFetchUtilSpec\"` — 23 tests, all green - `sbt \"WorkflowOperator/Test/scalafmtCheck\"` — clean - CI to confirm ### Was this PR authored or co-authored using generative AI tooling? Generated-by: Claude Code (Opus 4.7 [1M context])
…ants (apache#5739) ### What changes were proposed in this PR? Adds a unit-test spec for `PropertyNameConstants` (the `@JsonProperty` wire-key constants shared across `LogicalOp` serialization). No production-code changes. | Spec | Source class | Tests | | --- | --- | --- | | `PropertyNameConstantsSpec` | `PropertyNameConstants` | 5 | > **Scope note:** this PR originally also bundled `OperatorDescriptorUtils` and `PortDescriptor`/`PortDescription` specs. Both have since been covered on `main` / by other open PRs: > - `OperatorDescriptorUtilsSpec` already merged via apache#5798 — dropped here (it was the merge conflict). > - `PortDescriptor` + `PortDescription` are covered by apache#5832 with one spec file per source class (the repo convention) — dropped here to avoid a duplicate `PortDescriptorSpec.scala`. > > This PR is now scoped to its unique contribution, `PropertyNameConstants`, and is rebased on current `main`. ### Any related issues, documentation, discussions? Follow-up test coverage; see apache#5798 and apache#5832 for the de-duplicated specs. ### How was this PR tested? - `sbt "WorkflowOperator/testOnly org.apache.texera.amber.operator.metadata.PropertyNameConstantsSpec"` — 5 tests, all green - `sbt "WorkflowOperator/Test/scalafmtCheck"` and `sbt "WorkflowOperator/Test/scalafix --check"` — clean - CI to confirm ### Was this PR authored or co-authored using generative AI tooling? Generated-by: Claude Code (Opus 4.8 [1M context])
What changes were proposed in this PR?
Pin behavior of three small utility classes/objects in
common/workflow-operator/. Each one is too thin to justify its own PR but cohesive as a bundle (utility surface). No production-code changes.OperatorDescriptorUtilsSpecOperatorDescriptorUtils(object)DistributedAggregationSpecDistributedAggregation(case class)URLFetchUtilSpecURLFetchUtil(object)All three spec files follow the
<srcClassName>Spec.scalaone-to-one convention.Behavior pinned —
OperatorDescriptorUtilsequallyPartitionGoalsizetotalNumWorkersslotsgoalacrossgoal ∈ [0..20]×workers ∈ [1..5]goal % workers == 0, every slot isgoal / workersgoal % workersslots get+1(in order)goal < workersedge casegoalslots get1, the rest get0toImmutableMapemptyjava.util.Map→ empty ScalaMaptoImmutableMappreserves entriesscala.collection.immutable.Map(compile-time enforced)Behavior pinned —
DistributedAggregationinit(0L, 0L)iteratesum += value,count += 1mergefinalAggdivides(15L, 5L) → 3.0dfinalAggzero guard(0L, 0L) → 0.0d(no divide-by-zero)1..5via fold-left ==3.0mergeBehavior pinned —
URLFetchUtilgetInputStreamFromURL(file:tempFile)returnsSome(stream)carrying the file's exact bytesretries = 3file:URL returnsNoneretries = 0)Noneimmediatelyretries = 2)NonegetInputStreamFromURL\$default\$2 == 5, verified via Scala's synthetic default-accessorThe URLFetchUtil specs use the JVM's built-in
file:URL handler against temporary files (success path) and non-existent paths (failure path) — no external network calls or process exec.Any related issues, documentation, discussions?
Closes #5795.
How was this PR tested?
Pure unit-test additions; verified locally with:
sbt \"WorkflowOperator/testOnly org.apache.texera.amber.operator.util.OperatorDescriptorUtilsSpec org.apache.texera.amber.operator.aggregate.DistributedAggregationSpec org.apache.texera.amber.operator.source.fetcher.URLFetchUtilSpec\"— 23 tests, all greensbt \"WorkflowOperator/Test/scalafmtCheck\"— cleanWas this PR authored or co-authored using generative AI tooling?
Generated-by: Claude Code (Opus 4.7 [1M context])