This is an automated email from the ASF dual-hosted git repository.
Yicong-Huang 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 47ef2f5fdc test(amber): add CheckpointState unknown-key negative test
(#4687)
47ef2f5fdc is described below
commit 47ef2f5fdc9e59a0f4d05e5884a4cdaf005a4aa7
Author: Yicong Huang <[email protected]>
AuthorDate: Sun May 3 16:07:58 2026 -0700
test(amber): add CheckpointState unknown-key negative test (#4687)
### What changes were proposed in this PR?
Add a single negative-case unit test to `CheckpointSpec` for
`CheckpointState.load`.
```scala
"CheckpointState" should "fail loudly on an unknown key" in {
val chkpt = new CheckpointState()
assert(!chkpt.has("unknown"))
val ex = intercept[RuntimeException] {
chkpt.load[Any]("unknown")
}
assert(ex.getMessage.contains("unknown"))
}
```
The contract — `load` throws `RuntimeException("no state saved for key =
$key")` on a missing key — has always been there but was untested.
Replay rehydration in `Controller.loadFromCheckpoint` and
`WorkflowWorker.loadFromCheckpoint` rely on this loud failure (rather
than silent `null`) so the calling actor knows to fail fast. Pinning the
contract with a test keeps that behavior from being refactored away
accidentally.
This change is the small leftover from a longer attempt at restoring
`ControllerSpec` coverage. The bigger gap — `ControllerProcessor` save →
load round-trip throws Kryo `NullPointerException` on
`StatisticsManager.inputStatistics` — is tracked separately in #4686.
### Any related issues, documentation, discussions?
Related: #4686 (ControllerProcessor round-trip NPE).
### How was this PR tested?
```
sbt 'WorkflowExecutionService / Test / testOnly
org.apache.texera.amber.engine.faulttolerance.CheckpointSpec'
```
3 tests pass (the two existing serializability tests plus the new
negative test).
### Was this PR authored or co-authored using generative AI tooling?
Generated-by: Claude Code (Opus 4.7, 1M context)
---------
Co-authored-by: Claude Opus 4.7 (1M context) <[email protected]>
---
.../texera/amber/engine/faulttolerance/CheckpointSpec.scala | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git
a/amber/src/test/scala/org/apache/texera/amber/engine/faulttolerance/CheckpointSpec.scala
b/amber/src/test/scala/org/apache/texera/amber/engine/faulttolerance/CheckpointSpec.scala
index ee5a0b9a60..73d4601a92 100644
---
a/amber/src/test/scala/org/apache/texera/amber/engine/faulttolerance/CheckpointSpec.scala
+++
b/amber/src/test/scala/org/apache/texera/amber/engine/faulttolerance/CheckpointSpec.scala
@@ -85,6 +85,19 @@ class CheckpointSpec extends AnyFlatSpecLike with
BeforeAndAfterAll {
chkpt.save(DP_STATE_KEY, dp)
}
+ "CheckpointState" should "fail loudly on an unknown key" in {
+ // Pin the documented contract precisely: load throws
+ // RuntimeException("no state saved for key = $key"). A bare
+ // `contains("unknown")` would still pass if the message ever drifts to
+ // something like "unknown checkpoint", silently weakening the assertion.
+ val chkpt = new CheckpointState()
+ assert(!chkpt.has("unknown"))
+ val ex = intercept[RuntimeException] {
+ chkpt.load[Any]("unknown")
+ }
+ assert(ex.getMessage == "no state saved for key = unknown")
+ }
+
// "CSVScanOperator" should "be serializable" in {
// val chkpt = new CheckpointState()
// val headerlessCsvOpDesc = TestOperators.headerlessSmallCsvScanOpDesc()