Skip to content

test(nexus): migrate hand-rolled decide/react tests onto the fixtures (#238)#299

Merged
joeldsouzax merged 1 commit into
mainfrom
test/238-migrate-onto-fixtures
Jul 6, 2026
Merged

test(nexus): migrate hand-rolled decide/react tests onto the fixtures (#238)#299
joeldsouzax merged 1 commit into
mainfrom
test/238-migrate-onto-fixtures

Conversation

@joeldsouzax

Copy link
Copy Markdown
Contributor

What

Migrates the hand-rolled decide-only / react-only kernel tests onto
nexus::testing::AggregateFixture / SagaFixture — the intended
given/when/then surface — instead of open-coding
AggregateRoot::new → handle/react → assert. Closes #238.

Because given drives the real replay path, each migrated decide test now also
proves 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 check green.

Migrated

File To fixture Kept hand-rolled (why)
aggregate_root_tests 5 Handle decide tests (CounterEvent gains PartialEq) replay / commit_persisted / restore / version tests
saga_tests 3 React tests → SagaFixture (+ a then_expect_commands intent-projection assertion the original lacked) correlate tested directly
integration_test rejection + rehydrate→state via given-history (UserEvent tree gains PartialEq) one commit_persisted+version lifecycle test
newtype_aggregate_tests rejection + rehydrate→state lifecycle + new()-contract tests

Plus kernel.rs: allow unused_must_use for the terminal then_expect_* chains.

The recurring transform: create → commit_persisted → assert a later command is rejected collapses to given([events]).when(cmd).then_expect_error_matching(...).

Production change (one)

src/testing.rs gains a "When NOT to use the fixture" doc section recording
the boundary (replay/version-contract tests, version/commit_persisted
progression, and store-owned lifecycle all stay hand-rolled).

Deviations from the issue (surfaced, not silent)

  1. Two listed targets were not migrated — reading the code (not the stale line
    numbers) showed they can't be. The fixture is a closed assertion DSL that
    never returns the AggregateRoot, but property_tests'
    replay_events/apply_events_to feed proptest properties that inspect
    .version()/raw state and exercise the replay contract itself, and
    repository_qa_tests' equivalents no longer exist. Consistent with the issue's
    own "out of scope" carve-out.
  2. test: consolidate duplicated toy test-domains into one shared, fixture-friendly module #239 (shared test-domain) was not pulled in — treated as a separate DRY
    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-targets clean (flake gate is lib-only).
  • All migrated tests confirmed running by name.

🤖 Generated with Claude Code

…#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>
@joeldsouzax joeldsouzax enabled auto-merge (squash) July 6, 2026 12:12
@joeldsouzax joeldsouzax merged commit 6c21975 into main Jul 6, 2026
4 checks passed
@joeldsouzax joeldsouzax deleted the test/238-migrate-onto-fixtures branch July 6, 2026 12:21
@github-actions github-actions Bot mentioned this pull request Jul 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

test: migrate hand-rolled decide/react tests onto AggregateFixture/SagaFixture

1 participant