test(nexus): migrate hand-rolled decide/react tests onto the fixtures (#238)#299
Merged
Merged
Conversation
…#238) Route pure decide-only and react-only tests through `nexus::testing::AggregateFixture` / `SagaFixture` — the intended given/when/then surface — instead of hand-rolling `AggregateRoot::new -> handle/react -> assert`. The fixture drives the real replay path for `given`, so each migrated test now also proves the decision behaves correctly *after rehydration*, not just on a bare root. Adds fixture usage to four more test files: - aggregate_root_tests: the 5 `Handle` decide tests -> given/when/then. - saga_tests: the 3 `React` tests -> `SagaFixture` (and asserts the projected intent via `then_expect_commands`, which the hand-rolled version never checked). - integration_test / newtype_aggregate_tests: invariant-rejection and rehydrate->state assertions -> fixture, using `given(history)` to replace the `commit_persisted` setup. One `commit_persisted`+version lifecycle test is kept hand-rolled in each file — `when` folds state but deliberately does not advance the version, so version progression is not the fixture's job. `then_expect_events` requires `EventOf<A>: PartialEq + Debug`, so `CounterEvent` (aggregate_root) and the `UserEvent` tree (integration) gain a `PartialEq` derive. Local error types have no `PartialEq`, so rejections assert with `then_expect_error_matching`. Premise corrections (from reading the code, not the issue's line numbers): the two listed store-side targets are not migrated. The fixture is a closed assertion DSL that never returns the `AggregateRoot`, so tests needing its `version()` / raw state cannot use it — `property_tests`' `replay_events`/`apply_events_to` feed proptest properties that inspect the root and exercise the replay contract itself, and `repository_qa_tests`' equivalents no longer exist. Adds a "When NOT to use the fixture" section to the `src/testing.rs` module docs — the sole production change — recording the boundary: `replay`/version-contract tests, version/`commit_persisted` progression, and store-owned load/replay lifecycle all stay hand-rolled. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Migrates the hand-rolled decide-only / react-only kernel tests onto
nexus::testing::AggregateFixture/SagaFixture— the intendedgiven/when/then surface — instead of open-coding
AggregateRoot::new → handle/react → assert. Closes #238.Because
givendrives the real replay path, each migrated decide test now alsoproves the decision holds after rehydration, not just on a bare
new()root —strictly more coverage than the originals.
Fixture adoption goes from 2 dedicated files to 6; net +162 / −159 (collapsed
scaffolding). 153/153 kernel tests pass; full
nix flake checkgreen.Migrated
aggregate_root_testsHandledecide tests (CounterEventgainsPartialEq)replay/commit_persisted/restore/ version testssaga_testsReacttests →SagaFixture(+ athen_expect_commandsintent-projection assertion the original lacked)correlatetested directlyintegration_testgiven-history (UserEventtree gainsPartialEq)commit_persisted+version lifecycle testnewtype_aggregate_testsnew()-contract testsPlus
kernel.rs: allowunused_must_usefor the terminalthen_expect_*chains.The recurring transform:
create → commit_persisted → assert a later command is rejectedcollapses togiven([events]).when(cmd).then_expect_error_matching(...).Production change (one)
src/testing.rsgains a "When NOT to use the fixture" doc section recordingthe boundary (replay/version-contract tests, version/
commit_persistedprogression, and store-owned lifecycle all stay hand-rolled).
Deviations from the issue (surfaced, not silent)
numbers) showed they can't be. The fixture is a closed assertion DSL that
never returns the
AggregateRoot, butproperty_tests'replay_events/apply_events_tofeed proptest properties that inspect.version()/raw state and exercise the replay contract itself, andrepository_qa_tests' equivalents no longer exist. Consistent with the issue'sown "out of scope" carve-out.
concern; each file keeps its local toy domain (a dedicated crate for shared
toy domains is likely overkill, revisit under test: consolidate duplicated toy test-domains into one shared, fixture-friendly module #239).
Test plan
nix flake check— all six checks pass (clippy, doc, fmt, toml-fmt, audit,deny, nextest, hakari).
cargo clippy -p nexus --all-targetsclean (flake gate is lib-only).🤖 Generated with Claude Code