Skip to content

test(amber): add unit test coverage for ClientEvent sealed-trait family#5440

Merged
aglinxinyuan merged 2 commits into
apache:mainfrom
aglinxinyuan:test-client-event-spec
Jun 7, 2026
Merged

test(amber): add unit test coverage for ClientEvent sealed-trait family#5440
aglinxinyuan merged 2 commits into
apache:mainfrom
aglinxinyuan:test-client-event-spec

Conversation

@aglinxinyuan
Copy link
Copy Markdown
Contributor

What changes were proposed in this PR?

Adds ClientEventSpec covering all nine subtypes of the controller→client ClientEvent family. The trait extends WorkflowFIFOMessagePayload and flows over Pekko, so the spec round-trips each subtype via the production wire path (AmberRuntime.serde).

Subtype Pinned
ExecutionStateUpdate Exposes state; round-trips via AmberRuntime.serde; case-class equality + hashCode distinguish different states.
ExecutionStatsUpdate Exposes operatorMetrics; round-trip preserves both populated and empty metrics maps.
RuntimeStatisticsPersist Round-trips operatorMetrics.
ReportCurrentProcessingTuple Exposes operatorID and the Array[(Tuple, AVI)] tuple field; round-trip preserves operatorID and array length (Array equality is reference-based at the case-class level, so length is the right pin without over-constraining serializer choice).
WorkerAssignmentUpdate Exposes workerMapping; round-trip preserves both populated and empty mappings.
FatalError Defaults fromActor to None; explicit Some(actorId) round-trips; the carried Throwable survives by message + class (Throwable equality is reference-based, so we don't pin the instance).
UpdateExecutorCompleted Round-trips the actor id.
ReplayStatusUpdate Round-trips status = true and status = false independently.
WorkflowRecoveryStatus Round-trips isRecovering = true and isRecovering = false independently.
Cross-subtype ExecutionStatsUpdate and RuntimeStatisticsPersist share the Map[String, OperatorMetrics] field shape; case-class equality still distinguishes them across types.

Membership in ClientEvent and (transitively) WorkflowFIFOMessagePayload is enforced at compile time by : ClientEvent ascriptions plus a runtime isInstanceOf[WorkflowFIFOMessagePayload] sweep — a regression that demoted the trait off WorkflowFIFOMessagePayload would surface immediately.

The suite owns a suite-local Pekko ActorSystem injected into AmberRuntime via reflection, torn down via TestKit.shutdownActorSystem in afterAll (same pattern as CheckpointSubsystemSpec).

No production code changed; this is test-only.

Any related issues, documentation, discussions?

Closes #5437

How was this PR tested?

sbt "WorkflowExecutionService/Test/testOnly org.apache.texera.amber.engine.architecture.controller.ClientEventSpec"
# → 17 tests, all pass

sbt "WorkflowExecutionService/Test/scalafmtCheck"
# → clean

Was this PR authored or co-authored using generative AI tooling?

Generated-by: Claude Code (Claude Opus 4.7)

Pin the data contract and Pekko Serialization wire-path for all nine
ClientEvent subtypes used in the controller→client channel.

Per-subtype: each constructor exposes its fields verbatim; case-class
equality + hashCode work as expected; the subtype extends
ClientEvent and (via the trait) WorkflowFIFOMessagePayload — the
membership pin is compile-time-enforced by `: ClientEvent` ascriptions.

Pekko Serialization: round-trip via AmberRuntime.serde (the
production wire path) for every subtype:
- ExecutionStateUpdate(state) preserves the WorkflowAggregatedState
- ExecutionStatsUpdate(operatorMetrics) preserves both populated and
  empty operatorMetrics maps
- RuntimeStatisticsPersist preserves operatorMetrics
- ReportCurrentProcessingTuple preserves operatorID and the tuple
  array length (arrays compared element-wise — case-class equality on
  Array is reference-based, so length is the right pin without
  over-constraining serializer choice)
- WorkerAssignmentUpdate preserves workerMapping (populated and empty)
- FatalError preserves both the exception (message + class) and the
  optional fromActor; pin that fromActor defaults to None
- UpdateExecutorCompleted preserves the actor id
- ReplayStatusUpdate round-trips both status = true and status = false
- WorkflowRecoveryStatus round-trips both isRecovering = true and
  isRecovering = false

Cross-subtype: ExecutionStatsUpdate and RuntimeStatisticsPersist
share the Map[String, OperatorMetrics] field shape; pin that
case-class equality still distinguishes them across types.

The suite owns a suite-local Pekko ActorSystem injected into
AmberRuntime via reflection, torn down via
TestKit.shutdownActorSystem in afterAll (same pattern as
CheckpointSubsystemSpec).

Closes apache#5437
Copilot AI review requested due to automatic review settings June 7, 2026 02:08
@github-actions github-actions Bot added the engine label Jun 7, 2026
@Yicong-Huang
Copy link
Copy Markdown
Contributor

good timing! I am adding more ClientEvents, for a self contained amber.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds a ScalaTest suite (ClientEventSpec) to pin the data contract and Pekko serialization round-trip behavior for the controller→client ClientEvent sealed-trait family using the production serialization entrypoint (AmberRuntime.serde).

Changes:

  • Introduces ClientEventSpec that round-trips each ClientEvent subtype through AmberRuntime.serde.
  • Adds checks for subtype membership in WorkflowFIFOMessagePayload and cross-subtype inequality for same-shaped payloads.
  • Uses a suite-local Pekko ActorSystem injected into AmberRuntime via reflection and torn down in afterAll.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@codecov-commenter
Copy link
Copy Markdown

codecov-commenter commented Jun 7, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 51.69%. Comparing base (09aac02) to head (dceabbe).
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@             Coverage Diff              @@
##               main    #5440      +/-   ##
============================================
- Coverage     51.71%   51.69%   -0.03%     
+ Complexity     2461     2457       -4     
============================================
  Files          1067     1067              
  Lines         41254    41254              
  Branches       4437     4437              
============================================
- Hits          21336    21327       -9     
- Misses        18668    18670       +2     
- Partials       1250     1257       +7     
Flag Coverage Δ *Carryforward flag
access-control-service 42.22% <ø> (ø) Carriedforward from 82eb5c7
agent-service 33.76% <ø> (ø) Carriedforward from 82eb5c7
amber 52.29% <ø> (-0.06%) ⬇️
computing-unit-managing-service 1.65% <ø> (ø) Carriedforward from 82eb5c7
config-service 56.06% <ø> (ø) Carriedforward from 82eb5c7
file-service 38.32% <ø> (ø) Carriedforward from 82eb5c7
frontend 46.44% <ø> (ø) Carriedforward from 82eb5c7
pyamber 90.69% <ø> (ø) Carriedforward from 82eb5c7
python 90.83% <ø> (ø) Carriedforward from 82eb5c7
workflow-compiling-service 58.69% <ø> (ø) Carriedforward from 82eb5c7

*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.

Address three threads on apache#5440:

- Yicong-Huang: drop the runtime "all extend WorkflowFIFOMessagePayload"
  sweep — the trait inheritance is enforced at compile time by
  `trait ClientEvent extends WorkflowFIFOMessagePayload` plus the
  `: ClientEvent` ascriptions used in the per-subtype tests below.
  A runtime isInstanceOf check would be tautological and would need
  to be edited every time a new subtype is added.

- Copilot: the ReportCurrentProcessingTuple round-trip previously
  only exercised an empty array, so the (Tuple, AVI) element survival
  contract wasn't actually pinned (and the comment lied about
  "element-by-element verification"). Build a real single-element
  array fixture from a tiny Schema/Tuple, round-trip, and verify
  schema + field value + AVI all survive. Added a separate empty-
  array test for the degenerate case.

- Copilot: the FatalError default-None test only checked the
  constructor default, not the wire-path. Combine into one test that
  pins both the default and the round-trip preservation of None +
  exception (by message + class) — a regression mishandling missing
  fromActor on the receive side would now surface.
@aglinxinyuan aglinxinyuan added this pull request to the merge queue Jun 7, 2026
Merged via the queue into apache:main with commit 7cf37d0 Jun 7, 2026
14 checks passed
@aglinxinyuan aglinxinyuan deleted the test-client-event-spec branch June 7, 2026 04:20
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 ClientEvent sealed-trait family

4 participants