Skip to content

Adopt JasperFx.Events.ComplianceTests, retire the mirrored Marten tests, fix #392 - #393

Merged
jeremydmiller merged 2 commits into
mainfrom
feat/adopt-compliance-tests
Aug 2, 2026
Merged

Adopt JasperFx.Events.ComplianceTests, retire the mirrored Marten tests, fix #392#393
jeremydmiller merged 2 commits into
mainfrom
feat/adopt-compliance-tests

Conversation

@jeremydmiller

@jeremydmiller jeremydmiller commented Aug 2, 2026

Copy link
Copy Markdown
Member

Polecat side of marten#5117; part of the test-harness standardization program (marten#5119, epic marten#5110).

Fixes #392.

What

Polecat.Tests enrolls the shared cross-store event sourcing compliance suites instead of carrying hand-mirrored copies of Marten's tests, so the two products' event sourcing expectations can no longer drift apart.

AddedPolecatComplianceFixture, closing EventStoreComplianceFixture<TOperations, TQuerySession> over Polecat's <IDocumentSession, IQuerySession> pair (~150 lines), a ComplianceQuerySession global alias so the shared self-aggregating fixtures bind EvolveAsync to Polecat's IQuerySession, and four empty enrollment subclasses. Everything else arrives from the source-only package.

Retiredself_aggregating_evolve_method.cs, assign_tag_where_tests.cs, and the behavioral half of dcb_tag_query_and_consistency_tests.cs. What survives of the last one is dcb_documentation_samples.cs, keeping the twelve sample_polecat_dcb_* snippet blocks docs/events/dcb.md pulls from, plus the tag/event/aggregate types the other DCB fixtures share. auto_discover_aggregate_types and projection_sg_dispatch_audit_tests now reference the shared aggregates.

The product bug this surfaced (#392)

QueryByTagsAsync selected e.stream_id and then never mapped it onto the event envelope — the reader had a bare // stream_id at index 2 comment where the assignment should have been. Every event returned from a DCB tag query carried StreamId == Guid.Empty. Fixed by hydrating stream identity exactly the way PolecatEventLoader does, honoring StreamIdentity for the AsString case.

This is precisely why the shared suites keep the strongest assertion of the two products: our mirror of assign_tag_where_by_stream_id had been weakened to check Data types where Marten asserts e.StreamId == stream1, which hid the bug. The compliance suite kept Marten's assertion and it failed here on the first run.

Seam change this drove

Polecat batches its DCB assertions into a single command and collects their failures into a list, then throws AggregateException even when there is exactly one — so a single lost concurrency race surfaces wrapped, where Marten throws DcbConcurrencyException directly. The failure semantics are identical, so the shared suites assert through a ShouldFailWithAsync<T> helper that accepts either shape rather than forcing either product to change. Whether Polecat should unwrap the single case for parity is now tracked separately in #394.

Verified

Polecat.Tests net9.0: 1629 passed, 0 failed, 3 skipped — including 42/42 compliance. Also builds clean on net10.0.

Note

Pinned to the published JasperFx.Events.ComplianceTests 2.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

…test files

Polecat side of marten#5117 (program marten#5119, epic marten#5110). Polecat.Tests
now enrolls the shared cross-store event sourcing compliance suites instead of
carrying hand-mirrored copies of Marten's tests, so the two products' event
sourcing expectations can no longer drift apart.

Added: PolecatComplianceFixture, closing
EventStoreComplianceFixture<TOperations, TQuerySession> over Polecat's
<IDocumentSession, IQuerySession> pair (~150 lines), a ComplianceQuerySession
global alias so the shared self-aggregating fixtures bind EvolveAsync to
Polecat's IQuerySession, and four empty enrollment subclasses. Everything else
comes from the source-only package.

Retired: self_aggregating_evolve_method.cs, assign_tag_where_tests.cs, and the
behavioral half of dcb_tag_query_and_consistency_tests.cs. What survives of the
last one is dcb_documentation_samples.cs, which keeps the twelve
sample_polecat_dcb_* snippet blocks docs/events/dcb.md pulls from plus the
tag/event/aggregate types the other DCB fixtures share. auto_discover_aggregate_types
and projection_sg_dispatch_audit_tests now reference the shared aggregates.

## Product bug the adoption surfaced

QueryByTagsAsync selected e.stream_id and then never mapped it onto the event
envelope -- the reader had a bare `// stream_id at index 2` comment where the
assignment should have been. Every event returned from a DCB tag query carried
StreamId == Guid.Empty. Fixed by hydrating stream identity exactly the way
PolecatEventLoader does, honoring StreamIdentity for the AsString case.

This is precisely why the shared suites keep the strongest assertion: our mirror
of assign_tag_where_by_stream_id had been weakened to check Data types where
Marten asserts e.StreamId == stream1, which hid the bug. The compliance suite
kept Marten's assertion and it failed here on the first run.

## Seam change this drove

Polecat runs its unit of work in parallel and surfaces DcbConcurrencyException
inside an AggregateException, where Marten throws it directly. The failure
semantics are identical, so the shared suites assert through a
ShouldFailWithAsync<T> helper that accepts either shape rather than forcing
either product to change. Recorded as a ninth cross-store drift item.

Polecat.Tests net9.0: 1629 passed, 0 failed, 3 skipped (42/42 compliance).
JasperFx.Events.ComplianceTests shipped in the 2.37.1 line (jasperfx#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.

Polecat.Tests net9.0 against the published packages: 1629 passed, 0 failed,
3 skipped.
@jeremydmiller
jeremydmiller merged commit a05338e into main Aug 2, 2026
7 checks passed
@jeremydmiller
jeremydmiller deleted the feat/adopt-compliance-tests branch August 2, 2026 15:29
jeremydmiller added a commit that referenced this pull request Aug 2, 2026
…400)

Companion to marten#5118 (epic marten#5110). Moves four Polecat test files onto
the shared JasperFx.Events.ComplianceTests suites (2.37.2) and deletes them here,
the same trade #393 made for the first four.

Retired, and what replaced each:

- Events/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 on both sides of the port. Polecat's registration test asserted
only PublishedTypes() and Marten's asserted only its own known-document-types
list; 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, which
is the actual point of marten#4166. Polecat's rebuild-cap test was a pure unit
test against a dummy connection string; the shared one builds a real store.

PolecatComplianceFixture 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
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.

Also documents in CLAUDE.md that Polecat test runs must never overlap. The suite
isolates by DatabaseSchemaName inside one shared master database, so a second
concurrent run -- including one left alive after its parent shell was killed --
produces a large, scattered failure set across unrelated areas that reads like a
real regression. That cost real time in this session.

Compliance namespace: 55/55, no capability gates.


Claude-Session: https://claude.ai/code/session_01VpDCvJcBDZerieJB4JEHde

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
@jeremydmiller jeremydmiller mentioned this pull request Aug 3, 2026
jeremydmiller added a commit that referenced this pull request Aug 3, 2026
Minor rather than patch: 5.9.1's line added public surface (IEventBinarySerializer,
EventStoreOptions.AddEventType/AddEventTypes) and moved the whole JasperFx/Weasel
matrix forward.

Since 5.9.1:

- feat: pluggable binary event serialization via IEventBinarySerializer (#388/#402)
- feat: EventStoreOptions.AddEventType / AddEventTypes (#395/#396)
- fix: escape interpolated identifiers and literals in constructed SQL (#390/#403)
- fix: throw a lone DcbConcurrencyException unwrapped from SaveChangesAsync (#394/#397)
- deps: JasperFx 2.37.2 -> 2.38.0, Weasel 9.23.0 -> 9.23.2 (#405, #407)
- Polecat's ProjectionScenario is now a thin subclass of the lifted
  JasperFx.Events.TestSupport harness rather than a seven-file copy of Marten's
  (#404/#408, jasperfx#616) -- a behavior change for anyone already using it, see
  the release notes
- test infrastructure: compliance waves 1-3 (#393, #400, #407), parallel-safe test
  suite (#389), IntegrationContext.StoreOptions document cleaning (#398/#401)
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.

QueryByTagsAsync never populates IEvent.StreamId (always Guid.Empty)

1 participant