From 614b3c2725fc957863c02222992effa128801618 Mon Sep 17 00:00:00 2001 From: "Jeremy D. Miller" Date: Sun, 2 Aug 2026 09:17:09 -0500 Subject: [PATCH 1/2] Adopt JasperFx.Events.ComplianceTests and retire the mirrored Marten 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 over Polecat's 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 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). --- Directory.Packages.props | 6 + .../Compliance/ComplianceQuerySessionAlias.cs | 5 + .../Compliance/PolecatComplianceFixture.cs | 153 ++++ .../polecat_event_store_compliance.cs | 23 + .../Events/assign_tag_where_tests.cs | 211 ----- .../Events/auto_discover_aggregate_types.cs | 3 +- .../Events/dcb_documentation_samples.cs | 302 +++++++ .../dcb_tag_query_and_consistency_tests.cs | 771 ------------------ .../Events/self_aggregating_evolve_method.cs | 316 ------- src/Polecat.Tests/Polecat.Tests.csproj | 3 + .../projection_sg_dispatch_audit_tests.cs | 3 +- src/Polecat/Events/EventOperations.cs | 14 +- 12 files changed, 509 insertions(+), 1301 deletions(-) create mode 100644 src/Polecat.Tests/Compliance/ComplianceQuerySessionAlias.cs create mode 100644 src/Polecat.Tests/Compliance/PolecatComplianceFixture.cs create mode 100644 src/Polecat.Tests/Compliance/polecat_event_store_compliance.cs delete mode 100644 src/Polecat.Tests/Events/assign_tag_where_tests.cs create mode 100644 src/Polecat.Tests/Events/dcb_documentation_samples.cs delete mode 100644 src/Polecat.Tests/Events/dcb_tag_query_and_consistency_tests.cs delete mode 100644 src/Polecat.Tests/Events/self_aggregating_evolve_method.cs diff --git a/Directory.Packages.props b/Directory.Packages.props index a9d1fdc2..aa653d6a 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -64,6 +64,12 @@ deliver its trailing batch twice on shutdown) and #597. Same lockstep rule as above. --> + + + diff --git a/src/Polecat.Tests/Projections/projection_sg_dispatch_audit_tests.cs b/src/Polecat.Tests/Projections/projection_sg_dispatch_audit_tests.cs index ad32ca83..7b675562 100644 --- a/src/Polecat.Tests/Projections/projection_sg_dispatch_audit_tests.cs +++ b/src/Polecat.Tests/Projections/projection_sg_dispatch_audit_tests.cs @@ -1,4 +1,5 @@ using JasperFx; +using JasperFx.Events.ComplianceTests; using JasperFx.Events.Projections; using Polecat.Projections; using Polecat.Tests.Events; @@ -59,7 +60,7 @@ public class projection_sg_dispatch_audit_tests Row("StringQuestAggregate (string)", opts => opts.Projections.Add>(ProjectionLifecycle.Inline)), Row("StudentCourseEnrollment (Guid)", - opts => opts.Projections.Add>(ProjectionLifecycle.Inline)), + opts => opts.Projections.Add>(ProjectionLifecycle.Inline)), Row("QuestAggregate (Guid)", opts => opts.Projections.Add>(ProjectionLifecycle.Inline)), Row("OrderAggregate (Guid, has natural key)", diff --git a/src/Polecat/Events/EventOperations.cs b/src/Polecat/Events/EventOperations.cs index 58d77aeb..63a14485 100644 --- a/src/Polecat/Events/EventOperations.cs +++ b/src/Polecat/Events/EventOperations.cs @@ -941,7 +941,7 @@ public async Task> QueryByTagsAsync(EventTagQuery query, { var seqId = reader.GetInt64(0); var eventId = reader.GetGuid(1); - // stream_id at index 2 + var rawStreamId = reader.GetValue(2); var eventVersion = reader.GetInt64(3); var json = reader.GetString(4); var typeName = reader.GetString(5); @@ -966,6 +966,18 @@ public async Task> QueryByTagsAsync(EventTagQuery query, @event.DotNetTypeName = dotNetTypeName!; @event.IsArchived = isArchived; + // The stream_id column was selected but never mapped onto the envelope, so every event + // a DCB tag query returned carried StreamId == Guid.Empty. Hydrated the same way the + // daemon's event loader does. + if (_events.StreamIdentity == StreamIdentity.AsGuid && rawStreamId is Guid streamGuid) + { + @event.StreamId = streamGuid; + } + else + { + @event.StreamKey = rawStreamId.ToString(); + } + var metaIndex = 10; if (eventOptions.EnableCorrelationId) { From 99468d8e69bf6bd1700f4f7ef22ef355082a905b Mon Sep 17 00:00:00 2001 From: "Jeremy D. Miller" Date: Sun, 2 Aug 2026 09:56:15 -0500 Subject: [PATCH 2/2] Pin to the published JasperFx 2.37.1 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. --- Directory.Packages.props | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index aa653d6a..e90a1df3 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -62,14 +62,14 @@ re-reads progression and succeeds when the replay had already reached the mark, plus a configurable DaemonSettings.SideEffectGateTimeout), jasperfx#595 (BatchingChannel could deliver its trailing batch twice on shutdown) and #597. Same lockstep rule as above. --> - - + + - + - + @@ -165,7 +165,7 @@ - +