Lift the ProjectionScenario test harness into JasperFx.Events - #616
Merged
Conversation
…ents Cross-store lift of Marten's EventProjectionScenario test support (marten#5127), so Marten and Polecat can share one implementation (closes the intent of polecat#404 without hand-porting overloads). New JasperFx.Events.TestSupport namespace: - ProjectionScenario<TOperations, TQuerySession>: scripts a sequence of event appends (through the shared IEventOperations surface) and document assertions, then executes them in order. Consecutive appends batch into one commit; pending work is saved before each assertion and once more at the end (the marten#5126 trailing-append rule). A failed action stops the scenario; failed assertions accumulate and report together in ProjectionScenarioException. Scenarios are single-use -- re-execution throws. Stands up an IProjectionDaemon for the run when the store has any async projections, with a configurable non-stale timeout (default 30s). The generic closure mirrors IEventStore<TOperations, TQuerySession> and the compliance fixture. - Seven abstract seam members, deliberately the same shape the EventStoreComplianceFixture already uses: DeleteExistingDataAsync, HasAnyAsyncProjections, BuildDaemonAsync, OpenSession, SaveChangesAsync, EventsFor, and LoadDocumentAsync<T>(session, object id, ct). - DocumentShouldExist/DocumentShouldNotExist take object ids and dispatch through LoadDocumentAsync, so stores need no per-id-type overloads. - StartStream overloads that generate their own stream id return that Guid; the rest of the append surface returns void (the operations only queue closures -- there is no meaningful result until execution). - ProjectionScenarioException (AggregateException with the step-by-step report) and ProjectionScenarioAssertionException (typed assertion failures, distinguishable from infrastructure errors). Concrete stores subclass with ~40 lines of one-liners; Marten's adoption branch is marten feat/5127-scenario-lift. Tests: 15 new EventTests unit tests drive the harness through a fake store closure (NSubstitute) covering step ordering and deferred execution, flush points, arrange-only commit, typed assertion failures, action fail-fast + skip reporting, re-execution guard, daemon lifecycle and timeout, tenant flow-through, and session disposal. EventTests 672/672 on net9.0. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This was referenced Aug 3, 2026
This was referenced Aug 3, 2026
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.
Cross-store lift of Marten's
EventProjectionScenariotest support, following the ergonomics/quality pass in JasperFx/marten#5127 (merged as marten#5132). Fixing the shape first was deliberate — the shared surface starts from the corrected API rather than inheriting the throwawayStreamActionreturns and the copy-pasted assertion overloads.This also closes the intent of JasperFx/polecat#404 without anyone hand-porting 15 overloads: Polecat subclasses the shared type instead of maintaining a parallel implementation.
What's here
New
JasperFx.Events.TestSupportnamespace in the main library (it ships in the product assembly because users reach it through their store'sAdvancedsurface):ProjectionScenario<TOperations, TQuerySession>— scripts a sequence of event appends (through the sharedIEventOperationssurface) and document assertions, then executes them in order. Consecutive appends batch into one commit; pending work is saved before each assertion and once more at the end, which is the marten#5126 trailing-append rule. A failed action stops the scenario and reports how many steps were skipped; failed assertions accumulate and report together. Scenarios are single-use — re-execution throws rather than silently doing nothing. When the store has any async projections the scenario stands up anIProjectionDaemonfor the duration and waits for non-stale data after each batch, with a configurableTimeout(default 30s) that honors the caller'sCancellationToken.DeleteExistingDataAsync,HasAnyAsyncProjections,BuildDaemonAsync,OpenSession,SaveChangesAsync,EventsFor,LoadDocumentAsync<T>(session, object id, ct). Deliberately the same shapeEventStoreComplianceFixture<TOperations, TQuerySession>already asks of each store, including theobject idload dispatch — so a store that already has a compliance fixture has already written every one of these.DocumentShouldExist/DocumentShouldNotExisttakeobjectids and dispatch throughLoadDocumentAsync. That collapses what were four typed overloads per assertion into one, and typed call sites keep compiling since everything converts toobject.StartStreamoverloads that generate their own stream id return thatGuid; the rest of the append surface returnsvoid, since these operations only queue closures and there is no meaningful result until execution.ProjectionScenarioException(aggregate, carries the step-by-step report) andProjectionScenarioAssertionException(typed assertion failures, distinguishable from infrastructure errors inside the aggregate).The generic closure mirrors
IEventStore<TOperations, TQuerySession>and the compliance fixture — the products close the pair differently and convergence stays a non-goal.Tests
15 new unit tests in
EventTests/TestSupportdrive the harness against a fake store closure via NSubstitute, so the sequencing logic is covered with no database: deferred execution and step ordering, flush points (before each assertion, once at the end, including the arrange-only case), typed assertion failures and accumulation, action fail-fast with skip reporting and no final flush, the re-execution guard, daemon start/wait/stop plus the timeout value, tenant id flowing to both session and daemon, and session disposal.EventTestsis 672/672 on net9.0.Consumer status
Marten's adoption branch (
feat/5127-scenario-lift) is built and green against a local pack of this branch —Marten.slnxcompiles clean and the full scenario suite is 15/15 on net9.0. Marten'sProjectionScenariobecomes a ~70-line subclass; seven files and roughly 400 lines of duplicated harness delete. That PR opens once this is merged and published.🤖 Generated with Claude Code