Cross-store event sourcing compliance library (#5111, #5112, #5113, #5116) - #5122
Merged
Conversation
#5113) Phase 3 of the test harness standardization program (#5119, epic #5110). Lifts behavioral event sourcing tests out of Marten's own test project and into suites written once against a store-neutral seam, so Marten, Polecat and the planned Sqlite minimal store assert the same behavior instead of hand-mirroring each other's files. P3.1a -- the seam (src/Marten.Testing/Compliance/, namespace JasperFx.Events.ComplianceTests so extraction to the source-only package is a file move): - EventStoreComplianceFixture<TOperations, TQuerySession> mirroring JasperFx's own IEventStore<,> closure. Marten closes it <IDocumentOperations, IQuerySession>; Polecat will close it <IDocumentSession, IQuerySession>. Everything portable flows through the shared JasperFx surfaces; the fixture only absorbs what no shared interface declares -- store construction, session acquisition, SaveChangesAsync, document load-back, batched DCB queries, daemon plumbing and teardown. - ComplianceStoreConfig + IComplianceStoreRegistrar: store-neutral config replayed through recorded generic closures, so nothing here needs reflection or InternalsVisibleTo. - IComplianceBatch for the batched-DCB accessor divergence (batch.Events.EventsExist vs batch.EventsExist) until #5115 lands. - EventStoreComplianceSuite<TFixture, TOperations, TQuerySession> owning the fixture lifecycle, so enrolling a suite is a one-line empty subclass. - MartenComplianceFixture, Marten's ~120-line implementation. P3.1b/P3.1c -- four suites (42 tests) and Marten's enrollment: SelfAggregatingEvolveCompliance, DcbTagQueryAndConsistencyCompliance, AssignTagWhereCompliance, AsyncDaemonCompliance. Strongest assertion wins where the Marten and Polecat copies had drifted: - Inline snapshot cases assert the *persisted* document through LoadDocumentAsync. The Polecat mirror read back via AggregateStreamAsync, so it would have passed with a broken inline projection. - assign_tag_where_by_stream_id keeps Marten's e.StreamId == stream1 assertion rather than Polecat's weakened Data-type check. - Adopts two tests Marten never had, both from the Polecat side and both green here on day one: can_query_events_across_distinct_tag_types_with_or (the OR-across-distinct-tag-types INNER JOIN regression guard) and fetch_for_writing_appends_to_existing_tag_derived_stream_without_collision. Retirements: self_aggregating_evolve_method.cs and assign_tag_where_tests.cs are gone; dcb_tag_query_and_consistency_tests.cs is reduced to dcb_documentation_samples.cs, which keeps the eleven sample_marten_dcb_* snippet blocks docs/events/dcb.md pulls from plus the tag/event/aggregate types the HStore-specific fixtures share. Doc snippets stay repo-owned deliberately: after extraction the suite sources no longer live in this repo. EventSourcingTests net9.0: 1613 passed, 0 failed, 7 skipped.
…5116) Second half of the extraction: the seam and the four suites now live in the jasperfx repo and arrive here as a source-only NuGet package, so Marten's copy under src/Marten.Testing/Compliance is deleted. What stays Marten-side is exactly what should: MartenComplianceFixture (the concrete seam implementation), the ComplianceQuerySession global alias binding the shared aggregates' EvolveAsync parameter to Marten's IQuerySession, and the empty enrollment subclasses in EventSourcingTests/Compliance. Both Marten.Testing and EventSourcingTests reference the package, because the harness assembly compiles MartenComplianceFixture and the test assembly needs the suites compiled in it for the aggregate source generator to bind Marten's session types. Pinned to a locally packed 2.37.0-compliance.2 for now; this moves to the published version when the package ships. EventSourcingTests net9.0 compliance suites: 42 passed, 0 failed.
JasperFx.Events.ComplianceTests shipped in the 2.37.1 line (#607), so the local prerelease pin used during development is replaced by the real version, and the rest of the JasperFx packages move with it to keep the dependency graph on one line. EventSourcingTests net9.0 against the published packages: 1613 passed, 0 failed, 7 skipped.
This was referenced Aug 2, 2026
jeremydmiller
added a commit
that referenced
this pull request
Aug 2, 2026
…5118) (#5123) Part of #5110 / program #5119. Moves four Marten test files onto the shared JasperFx.Events.ComplianceTests suites (2.37.2) and deletes them here, the same trade #5122 made for the first four. Retired, and what replaced each: - Aggregation/auto_discover_aggregate_types.cs -> AutoDiscoveredAggregateCompliance - Projections/event_projection_should_register_document_types.cs -> EventProjectionRegistrationCompliance - Projections/event_projection_enrichment_tests.cs -> EventProjectionEnrichmentCompliance - rebuild_concurrency_cap_resolution.cs -> RebuildConcurrencyCapCompliance Coverage went up, not down. Marten's registration test only asserted that AuditRecord reached AllKnownDocumentTypes(); Polecat's only asserted PublishedTypes(). The shared suite asserts both routes and adds an end-to-end append proving the store really provisioned storage for a document type nobody registered — the actual point of #4166. MartenComplianceFixture picks up the seam members the new suites need: StoreDocument, EventStore, AllAggregateTypes, an AddProjection registrar member, and connection-string/DaemonSettings handling for the rebuild-cap knobs. The harness alias file gains ComplianceOperations and ComplianceEventProjection beside the existing ComplianceQuerySession, because the EventProjection suites declare projection types at file scope and cannot reach the suite's generics. EventSourcingTests net9.0: 1614/0/7 (was 1613/0/7 — twelve local tests out, thirteen compliance tests in). Compliance namespace alone: 55/55, no capability gates. Blocked on the JasperFx 2.37.2 publish (jasperfx PR). Verified locally against a packed prerelease of the same sources. Claude-Session: https://claude.ai/code/session_01VpDCvJcBDZerieJB4JEHde Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This was referenced Aug 4, 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.
Phase 3 of the test-harness standardization program (#5119, epic #5110). Closes #5111, #5112, #5113, #5116.
Phases 1–2 (#5098–#5107, merged 2026-08-01) standardized Marten's own harnesses. This lifts behavioral event sourcing tests out of Marten's test project entirely and into suites written once against a store-neutral seam, so Marten, Polecat and the planned Sqlite minimal store assert the same behavior instead of hand-mirroring each other's files.
The seam (#5111)
EventStoreComplianceFixture<TOperations, TQuerySession>, mirroring JasperFx's ownIEventStore<,>closure — Marten closes it<IDocumentOperations, IQuerySession>, Polecat<IDocumentSession, IQuerySession>, and convergence stays a non-goal. Everything portable flows through the shared JasperFx surfaces; the fixture only absorbs what no shared interface declares: store construction, session acquisition,SaveChangesAsync, document load-back, batched DCB queries, daemon plumbing, teardown.Marten's side is
MartenComplianceFixture(~120 lines) plus aComplianceQuerySessionglobal alias, and enrolling a suite is a one-line empty subclass.The suites (#5112, #5113)
Four suites, 42 tests:
SelfAggregatingEvolveCompliance,DcbTagQueryAndConsistencyCompliance,AssignTagWhereCompliance,AsyncDaemonCompliance.Strongest assertion wins where the Marten and Polecat copies had drifted:
LoadDocumentAsync. The Polecat mirror read back viaAggregateStreamAsync, so it would have passed with a broken inline projection.assign_tag_where_by_stream_idkeeps Marten'se.StreamId == stream1assertion rather than Polecat's weakenedData-type check.can_query_events_across_distinct_tag_types_with_or(the OR-across-distinct-tag-types INNER JOIN regression guard) andfetch_for_writing_appends_to_existing_tag_derived_stream_without_collision.That last assertion is not academic — it caught a real Polecat bug on the first run (polecat#392:
QueryByTagsAsyncnever mappedstream_idonto the event envelope, so every DCB tag query returnedStreamId == Guid.Empty).Extraction (#5116)
The seam and suites live in the jasperfx repo and arrive here as the source-only NuGet
JasperFx.Events.ComplianceTests(jasperfx#607). Source-only is forced, not preferred:JasperFx.Events.SourceGeneratoremits aggregate dispatchers per consuming assembly and binds each product's own session type, so the shared aggregates must compile inside each consumer.Both
Marten.TestingandEventSourcingTestsreference the package — the harness assembly compilesMartenComplianceFixture, and the test assembly needs the suites compiled into it for the source generator to bind Marten's session types.Retirements
self_aggregating_evolve_method.csandassign_tag_where_tests.csare gone.dcb_tag_query_and_consistency_tests.csis reduced todcb_documentation_samples.cs, which keeps the elevensample_marten_dcb_*snippet blocksdocs/events/dcb.mdpulls from plus the tag/event/aggregate types the HStore-specific fixtures share. Doc snippets stay repo-owned deliberately — after extraction the suite sources no longer live in this repo, so mdsnippets could not reach them.The two upstream additives are not being done
Both were investigated against real usage and written up on their issues:
IEventRegistrygeneric DIMs) cannot land as specified — both proposed DIMs collide with existingEventGraphmembers that have incompatible return types (CS0738), and making room means a source-breaking change toAddEventType<T>()'s public return type. The seam needs neither; it asserts throughEventMappingFor(Type)with zeroInternalsVisibleTo.IComplianceBatch, becauseExecute(ct)belongs to the batch rather than to the event queries — the open question that issue asked P3.1b to settle. Deferred; the adapter is 10 lines per store.Verified
EventSourcingTestsnet9.0: 1613 passed, 0 failed, 7 skipped, identical before and after the switch to the package. Also builds clean on net10.0.Note
Pinned to the published
JasperFx.Events.ComplianceTests2.37.1 (jasperfx#607, merged and shipped). The rest of the JasperFx packages move to 2.37.1 with it so the dependency graph stays on one line. Re-verified against the published packages, not a local feed.🤖 Generated with Claude Code