Skip to content

test(workflow-operator): add unit test coverage for OperatorDescriptorUtils + DistributedAggregation + URLFetchUtil - #5798

Merged
aglinxinyuan merged 2 commits into
apache:mainfrom
aglinxinyuan:test-operator-utility-classes
Jun 20, 2026
Merged

test(workflow-operator): add unit test coverage for OperatorDescriptorUtils + DistributedAggregation + URLFetchUtil#5798
aglinxinyuan merged 2 commits into
apache:mainfrom
aglinxinyuan:test-operator-utility-classes

Conversation

@aglinxinyuan

Copy link
Copy Markdown
Contributor

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 #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])

…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.
Copilot AI review requested due to automatic review settings June 19, 2026 07:00
@codecov-commenter

codecov-commenter commented Jun 19, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 53.27%. Comparing base (731d671) to head (c1b3d29).
⚠️ Report is 3 commits behind head on main.

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     
Flag Coverage Δ *Carryforward flag
access-control-service 70.44% <ø> (ø)
agent-service 34.36% <ø> (ø) Carriedforward from b6d2502
amber 53.72% <ø> (-0.09%) ⬇️
computing-unit-managing-service 1.65% <ø> (ø)
config-service 56.71% <ø> (ø)
file-service 57.06% <ø> (ø)
frontend 48.00% <ø> (ø) Carriedforward from b6d2502
pyamber 90.13% <ø> (ø) Carriedforward from b6d2502
python 90.80% <ø> (ø) Carriedforward from b6d2502
workflow-compiling-service 58.69% <ø> (ø)

*This pull request uses carry forward flags. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 OperatorDescriptorUtilsSpec to validate partitioning behavior and Java→Scala map conversion.
  • Add DistributedAggregationSpec to pin the case-class contract and a worked end-to-end average aggregation.
  • Add URLFetchUtilSpec to 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.

@github-actions

github-actions Bot commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

⚠️ Benchmark changes need a look

🟢 2 better · 🔴 3 worse · ⚪ 10 noise (<±5%) · 0 without baseline

Compared against main d513324 benchmarked on this same runner, so the delta is largely free of cross-runner hardware noise. The "7d avg" column still reflects the gh-pages dashboard. Treat <±5% as noise unless repeated.

Dashboard · Run

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

@aglinxinyuan
aglinxinyuan requested a review from xuang7 June 19, 2026 22:57
…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'.

@xuang7 xuang7 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@aglinxinyuan
aglinxinyuan added this pull request to the merge queue Jun 20, 2026
Merged via the queue into apache:main with commit 8febad2 Jun 20, 2026
20 checks passed
@aglinxinyuan
aglinxinyuan deleted the test-operator-utility-classes branch June 20, 2026 07:10
aglinxinyuan added a commit to aglinxinyuan/texera that referenced this pull request Jun 22, 2026
…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])
yangzhang75 pushed a commit to yangzhang75/texera that referenced this pull request Jun 24, 2026
…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])
yangzhang75 pushed a commit to yangzhang75/texera that referenced this pull request Jun 24, 2026
…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])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add unit test coverage for operator utility classes (OperatorDescriptorUtils, DistributedAggregation, URLFetchUtil)

4 participants