You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Part of the program master issue #5119. Tracking issue for Phase 3 of the test-harness standardization program. Phases 1–2 (PRs #5098–#5107, all merged 2026-08-01) standardized Marten's own harnesses; Phase 3 lifts shared behavioral event sourcing tests into a compliance library consumed by Marten (PostgreSQL), Polecat (SQL Server), and eventually a planned Sqlite-based minimal store (JasperFx.Events + Weasel.Storage + Weasel.Sqlite). Marten is the tracking repository; cross-repo items are marked in their issues.
Why
Polecat carries ~25–35 hand-mirrored Marten test files, and they have already drifted: Polecat's inline-snapshot tests read back via live re-fold and never assert document persistence; its assign_tag_where_by_stream_id assertion was weakened; Polecat has an OR-across-distinct-tag-types INNER JOIN regression test that Marten lacks entirely. A shared suite makes this class of drift impossible.
Verified ground facts
Marten's ES-side test projects and Polecat.Tests are on identical xunit.v3 3.2.2, TFMs net9.0;net10.0 in both repos. Runner differs (VSTest vs MTP) — invisible to a library referencing only xunit.v3.extensibility.core.
Package skew is routine (Marten JasperFx 2.37.0/Weasel 9.23.0 vs Polecat 2.36.3/9.21.0).
The seam is ~90% shared already: every event-store call in the three seed test pairs resolves to JasperFx.Events.IEventStoreOperations / IEventOperations / IQueryEventStore via session.Events on both sides.
The stores close IEventStore<,> differently — Marten IEventStore<IDocumentOperations, IQuerySession>, Polecat IEventStore<IDocumentSession, IQuerySession> — and convergence is a non-goal.
Shared-source distribution is forced (not preferred): the JasperFx.Events source generator emits aggregate dispatchers per consuming assembly and binds each product's session type, so suite/aggregate sources must compile inside each consumer (and aggregates be declared partial for Polecat).
Decisions (settled 2026-08-01)
Full generics: EventStoreComplianceFixture<TOperations, TQuerySession> where TOperations : TQuerySession, IStorageOperations, mirroring the JasperFx closure. Marten closes <IDocumentOperations, IQuerySession>; Polecat <IDocumentSession, IQuerySession>.
Extraction target: source-only NuGet JasperFx.Events.ComplianceTests in the jasperfx repo (contentFiles C# sources, no compiled lib), after the seam proves out in Marten.Testing.
Upstream additives now: generic AddEventType<T>()/EventMappingFor<T>() DIMs on IEventRegistry, and a shared batch-DCB query interface.
Marten-side consumption: compliance subclasses run inside EventSourcingTests (linked source, no new project in P3.1).
Strongest assertion wins when absorbing drifted pairs — adoption may surface real product gaps; that is the point.
P3.1 includes one async-daemon suite to prove the daemon seam members before extraction freezes the API.
Constraints the library must honor
C# 13 ceiling (Marten pins LangVersion 13); explicit usings (Marten has ImplicitUsings off); reference xunit.v3.extensibility.core only (never xunit.v3 — it forces OutputType=Exe); zero InternalsVisibleTo (assert via IEventRegistry.EventMappingFor(Type) → IEventType, never Marten's internal generic); cancellation via an overridable fixture member; capability gates (virtual bool Supports... + xunit v3 dynamic skip) for per-store feature holes (e.g. Polecat native-JSON gate).
Sequencing: #5111 → (#5112, #5113 in either order) → #5116 → #5117 → #5118. #5114/#5115 are parallel upstream tracks; #5114 should land before #5116 so the extracted sources use the generic forms, #5115's shape is finalized against #5112's real usage.
Future (not yet issued): the Sqlite-based minimal document db + event store becomes the third fixture implementation and the first true test that the seam carries no PostgreSQL/SQL Server residue.
Superseded by #5155. "No file carries a ported-from-Marten marker" cannot converge — provenance citations (marten#NNNN) grow with every parity feature, so the count moves away from zero as parity work succeeds. The replacement is the child-suite checklist above.
Dev loop
Both consumers accept -p:ComplianceSourceDir=/path/to/jasperfx/src/JasperFx.Events.ComplianceTests, which swaps the published suites for a working copy. That is what makes a wave verifiable against both stores before the JasperFx release rather than after.
Part of the program master issue #5119. Tracking issue for Phase 3 of the test-harness standardization program. Phases 1–2 (PRs #5098–#5107, all merged 2026-08-01) standardized Marten's own harnesses; Phase 3 lifts shared behavioral event sourcing tests into a compliance library consumed by Marten (PostgreSQL), Polecat (SQL Server), and eventually a planned Sqlite-based minimal store (JasperFx.Events + Weasel.Storage + Weasel.Sqlite). Marten is the tracking repository; cross-repo items are marked in their issues.
Why
Polecat carries ~25–35 hand-mirrored Marten test files, and they have already drifted: Polecat's inline-snapshot tests read back via live re-fold and never assert document persistence; its
assign_tag_where_by_stream_idassertion was weakened; Polecat has an OR-across-distinct-tag-types INNER JOIN regression test that Marten lacks entirely. A shared suite makes this class of drift impossible.Verified ground facts
net9.0;net10.0in both repos. Runner differs (VSTest vs MTP) — invisible to a library referencing onlyxunit.v3.extensibility.core.JasperFx.Events.IEventStoreOperations/IEventOperations/IQueryEventStoreviasession.Eventson both sides.IEventStore<,>differently — MartenIEventStore<IDocumentOperations, IQuerySession>, PolecatIEventStore<IDocumentSession, IQuerySession>— and convergence is a non-goal.partialfor Polecat).Decisions (settled 2026-08-01)
EventStoreComplianceFixture<TOperations, TQuerySession> where TOperations : TQuerySession, IStorageOperations, mirroring the JasperFx closure. Marten closes<IDocumentOperations, IQuerySession>; Polecat<IDocumentSession, IQuerySession>.JasperFx.Events.ComplianceTestsin the jasperfx repo (contentFiles C# sources, no compiled lib), after the seam proves out inMarten.Testing.AddEventType<T>()/EventMappingFor<T>()DIMs onIEventRegistry, and a shared batch-DCB query interface.Constraints the library must honor
C# 13 ceiling (Marten pins LangVersion 13); explicit usings (Marten has ImplicitUsings off); reference
xunit.v3.extensibility.coreonly (neverxunit.v3— it forces OutputType=Exe); zero InternalsVisibleTo (assert viaIEventRegistry.EventMappingFor(Type)→IEventType, never Marten's internal generic); cancellation via an overridable fixture member; capability gates (virtual bool Supports...+ xunit v3 dynamic skip) for per-store feature holes (e.g. Polecat native-JSON gate).Child issues
EventStoreComplianceFixture<TOperations, TQuerySession>in Marten.Testing[jasperfx] Additive IEventRegistry generic DIMs: AddEventType<T>() and EventMappingFor<T>() (P3.2a) #5114(closed, not planned) — P3.2a: [jasperfx] IEventRegistry generic DIMs[jasperfx] Shared batch-DCB query interface + Marten/Polecat implementations (P3.2b) #5115(closed, not planned) — P3.2b: [jasperfx] shared batch-DCB query interfaceJasperFx.Events.ComplianceTestsSequencing: #5111 → (#5112, #5113 in either order) → #5116 → #5117 → #5118. #5114/#5115 are parallel upstream tracks; #5114 should land before #5116 so the extracted sources use the generic forms, #5115's shape is finalized against #5112's real usage.
Future (not yet issued): the Sqlite-based minimal document db + event store becomes the third fixture implementation and the first true test that the seam carries no PostgreSQL/SQL Server residue.
🤖 Generated with Claude Code
Status as of 2026-08-04
The library sits at 14 suites / 105 tests, enrolled in Marten and Polecat with zero capability gates on either store.
AutoDiscoveredAggregate,EventProjectionRegistration,EventProjectionEnrichment,RebuildConcurrencyCapActivityCorrelation,StringIdentitySingleStreamFetchForWriting,StreamRead,EventMetadata,LiveAggregationProjectionScenariowas lifted into JasperFx.Events separately (#5127, #5132, #5133). Consumer adoptions: marten#5122, #5123, #5129, #5135, #5176; polecat#393, #400, #407, #410.Remaining backlog
Portable today — no seam change:
WriteToAggregate, stream concurrency (wave 4)AggregateStreamToLastKnownAsync(wave 4)IEventmetadata contract (wave 4)FetchLatest/ProjectLatestacross lifecyclesNeeds a seam addition:
IEventStoreInstrumentationand the append observerNeeds an upstream jasperfx lift first:
CompactStreamAsync<T>ontoIEventStoreOperationsIEventDataMaskingintoJasperFx.Events.ProtectedProgram:
Exit criterion
Superseded by #5155. "No file carries a ported-from-Marten marker" cannot converge — provenance citations (
marten#NNNN) grow with every parity feature, so the count moves away from zero as parity work succeeds. The replacement is the child-suite checklist above.Dev loop
Both consumers accept
-p:ComplianceSourceDir=/path/to/jasperfx/src/JasperFx.Events.ComplianceTests, which swaps the published suites for a working copy. That is what makes a wave verifiable against both stores before the JasperFx release rather than after.