From dc169eec1d14bed52d4436535666a6f97ec212d4 Mon Sep 17 00:00:00 2001 From: "Jeremy D. Miller" Date: Mon, 28 Oct 2024 15:42:07 -0500 Subject: [PATCH] Folding a handful of bug tests into the right spots --- .../Subscriptions/subscriptions_end_to_end.cs | 1 + .../rebuilding_a_single_stream_projection.cs | 1 + .../Aggregation/write_to_aggregate.cs | 1 + ...o_not_create_tables_for_live_aggregates.cs | 27 ----- ...gainst_events_and_event_has_id_property.cs | 45 -------- .../Bugs/Bug_2848_distinct_count_query.cs | 21 ---- .../Bug_3103_linq_query_against_event_id.cs | 25 ---- ...not_create_tables_for_live_aggregations.cs | 39 ------- .../Bug_3389_flat_table_compilation_issue.cs | 61 ---------- ...g_3465_multiple_fetch_for_writing_calls.cs | 108 ------------------ ..._should_not_be_erroneously_in_the_patch.cs | 23 ---- ...FetchStreamState_before_AggregateStream.cs | 35 ------ ...itional_events_should_return_projection.cs | 69 ----------- .../Bugs/Bug_fetch_for_writing_cache.cs | 67 ----------- .../Bugs/nulls_in_event_name_cache.cs | 27 +++-- .../EventSourcingTests.csproj | 12 -- .../fetching_async_aggregates_for_writing.cs | 49 +++++++- .../fetching_inline_aggregates_for_writing.cs | 60 +++++++++- .../fetching_live_aggregates_for_writing.cs | 101 +++++++++++++++- .../Projections/EventSliceTests.cs | 1 + ...on_with_stream_id_identifier_end_to_end.cs | 50 ++++++++ ...sing_explicit_code_for_live_aggregation.cs | 1 + .../cannot_start_a_stream_with_zero_events.cs | 4 +- ..._event_capture_and_fetching_the_stream.cs} | 4 +- src/EventSourcingTests/fake_aggregate.js | 19 --- .../fetching_stream_state.cs | 28 +++++ src/EventSourcingTests/location.js | 14 --- src/EventSourcingTests/party.js | 21 ---- .../querying_event_data_with_linq.cs | 51 +++++++++ .../schema_object_management.cs | 108 ++++++++++++++++++ .../using_the_schema_objects_Tests.cs | 53 --------- .../Harness/IntegrationContext.cs | 18 +++ 32 files changed, 482 insertions(+), 662 deletions(-) delete mode 100644 src/EventSourcingTests/Bugs/Bug_2610_do_not_create_tables_for_live_aggregates.cs delete mode 100644 src/EventSourcingTests/Bugs/Bug_2777_querying_with_linq_against_events_and_event_has_id_property.cs delete mode 100644 src/EventSourcingTests/Bugs/Bug_2848_distinct_count_query.cs delete mode 100644 src/EventSourcingTests/Bugs/Bug_3103_linq_query_against_event_id.cs delete mode 100644 src/EventSourcingTests/Bugs/Bug_3140_do_not_create_tables_for_live_aggregations.cs delete mode 100644 src/EventSourcingTests/Bugs/Bug_3389_flat_table_compilation_issue.cs delete mode 100644 src/EventSourcingTests/Bugs/Bug_3465_multiple_fetch_for_writing_calls.cs delete mode 100644 src/EventSourcingTests/Bugs/Bug_499_event_store_objects_should_not_be_erroneously_in_the_patch.cs delete mode 100644 src/EventSourcingTests/Bugs/Bug_705_FetchStreamState_before_AggregateStream.cs delete mode 100644 src/EventSourcingTests/Bugs/Bug_9999_fetch_for_writing_async_projection_with_no_additional_events_should_return_projection.cs delete mode 100644 src/EventSourcingTests/Bugs/Bug_fetch_for_writing_cache.cs rename src/EventSourcingTests/{Aggregation => FetchForWriting}/fetching_async_aggregates_for_writing.cs (92%) rename src/EventSourcingTests/{Aggregation => FetchForWriting}/fetching_inline_aggregates_for_writing.cs (92%) rename src/EventSourcingTests/{Aggregation => FetchForWriting}/fetching_live_aggregates_for_writing.cs (82%) rename src/EventSourcingTests/{end_to_end_event_capture_and_fetching_the_stream_Tests.cs => end_to_end_event_capture_and_fetching_the_stream.cs} (99%) delete mode 100644 src/EventSourcingTests/fake_aggregate.js delete mode 100644 src/EventSourcingTests/location.js delete mode 100644 src/EventSourcingTests/party.js create mode 100644 src/EventSourcingTests/schema_object_management.cs delete mode 100644 src/EventSourcingTests/using_the_schema_objects_Tests.cs diff --git a/src/DaemonTests/Subscriptions/subscriptions_end_to_end.cs b/src/DaemonTests/Subscriptions/subscriptions_end_to_end.cs index a20eee9397..7cc3808d99 100644 --- a/src/DaemonTests/Subscriptions/subscriptions_end_to_end.cs +++ b/src/DaemonTests/Subscriptions/subscriptions_end_to_end.cs @@ -4,6 +4,7 @@ using System.Threading; using System.Threading.Tasks; using EventSourcingTests.Aggregation; +using EventSourcingTests.FetchForWriting; using JasperFx.Core; using Lamar.IoC.Instances; using Marten; diff --git a/src/EventSourcingTests/Aggregation/rebuilding_a_single_stream_projection.cs b/src/EventSourcingTests/Aggregation/rebuilding_a_single_stream_projection.cs index b120a36e28..6701f3bf92 100644 --- a/src/EventSourcingTests/Aggregation/rebuilding_a_single_stream_projection.cs +++ b/src/EventSourcingTests/Aggregation/rebuilding_a_single_stream_projection.cs @@ -1,5 +1,6 @@ using System; using System.Threading.Tasks; +using EventSourcingTests.FetchForWriting; using Marten.Events; using Marten.Testing.Harness; using Shouldly; diff --git a/src/EventSourcingTests/Aggregation/write_to_aggregate.cs b/src/EventSourcingTests/Aggregation/write_to_aggregate.cs index edeff5b034..725357c1ab 100644 --- a/src/EventSourcingTests/Aggregation/write_to_aggregate.cs +++ b/src/EventSourcingTests/Aggregation/write_to_aggregate.cs @@ -1,5 +1,6 @@ using System; using System.Threading.Tasks; +using EventSourcingTests.FetchForWriting; using Marten.Events; using Marten.Testing.Harness; using Xunit; diff --git a/src/EventSourcingTests/Bugs/Bug_2610_do_not_create_tables_for_live_aggregates.cs b/src/EventSourcingTests/Bugs/Bug_2610_do_not_create_tables_for_live_aggregates.cs deleted file mode 100644 index 0779685e5e..0000000000 --- a/src/EventSourcingTests/Bugs/Bug_2610_do_not_create_tables_for_live_aggregates.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; -using System.Linq; -using System.Threading.Tasks; -using EventSourcingTests.Projections; -using Marten.Testing.Harness; -using Shouldly; -using Weasel.Postgresql.Tables; -using Xunit; - -namespace EventSourcingTests.Bugs; - -public class Bug_2610_do_not_create_tables_for_live_aggregates : BugIntegrationContext -{ - [Fact] - public async Task do_not_create_tables() - { - StoreOptions(opts => - { - opts.Projections.LiveStreamAggregation(); - }); - - await theStore.Storage.ApplyAllConfiguredChangesToDatabaseAsync(); - - var tables = theStore.Storage.AllObjects().OfType(); - tables.ShouldNotContain(x => x.Identifier.Name.Contains(nameof(QuestParty), StringComparison.OrdinalIgnoreCase)); - } -} diff --git a/src/EventSourcingTests/Bugs/Bug_2777_querying_with_linq_against_events_and_event_has_id_property.cs b/src/EventSourcingTests/Bugs/Bug_2777_querying_with_linq_against_events_and_event_has_id_property.cs deleted file mode 100644 index ab7eb4e190..0000000000 --- a/src/EventSourcingTests/Bugs/Bug_2777_querying_with_linq_against_events_and_event_has_id_property.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System.Linq; -using System.Threading.Tasks; -using Marten; -using Marten.Events; -using Marten.Testing.Harness; -using Xunit; -using Xunit.Abstractions; - -namespace EventSourcingTests.Bugs; - -public class Bug_2777_querying_with_linq_against_events_and_event_has_id_property : BugIntegrationContext -{ - private readonly ITestOutputHelper _output; - - public Bug_2777_querying_with_linq_against_events_and_event_has_id_property(ITestOutputHelper output) - { - _output = output; - } - - [Fact] - public async Task do_not_blow_up() - { - StoreOptions(opts => - { - opts.Events.StreamIdentity = StreamIdentity.AsString; - opts.Logger(new TestOutputMartenLogger(_output)); - }, true); - - await using (var session = theStore.LightweightSession()) - { - session.Events.Append("a", new DummyEvent("a")); - - await session.SaveChangesAsync(); - } - - await using (var session = theStore.QuerySession()) - { - var ids = await session.Events.QueryRawEventDataOnly() - .Select(d => d.Id) // This causes the operation to fail - .ToListAsync(); - } - } -} - -public record DummyEvent(string Id); diff --git a/src/EventSourcingTests/Bugs/Bug_2848_distinct_count_query.cs b/src/EventSourcingTests/Bugs/Bug_2848_distinct_count_query.cs deleted file mode 100644 index 237c854752..0000000000 --- a/src/EventSourcingTests/Bugs/Bug_2848_distinct_count_query.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System.Linq; -using System.Threading.Tasks; -using Marten; -using Marten.Events.Archiving; -using Marten.Testing.Harness; -using Xunit; - -namespace EventSourcingTests.Bugs; - -public class Bug_2848_distinct_count_query : BugIntegrationContext -{ - [Fact] - public async Task can_make_the_query() - { - await theSession.Events.QueryAllRawEvents() - .Where(x => x.MaybeArchived()) - .Select(x => x.StreamKey) - .Distinct() - .CountAsync(); - } -} diff --git a/src/EventSourcingTests/Bugs/Bug_3103_linq_query_against_event_id.cs b/src/EventSourcingTests/Bugs/Bug_3103_linq_query_against_event_id.cs deleted file mode 100644 index c549033646..0000000000 --- a/src/EventSourcingTests/Bugs/Bug_3103_linq_query_against_event_id.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; -using System.Linq; -using System.Threading.Tasks; -using Marten; -using Marten.Testing.Harness; -using Shouldly; -using Xunit; - -namespace EventSourcingTests.Bugs; - -public class Bug_3103_linq_query_against_event_id : BugIntegrationContext -{ - [Fact] - public async Task generate_proper_sql() - { - var eventsIds = new Guid[] { Guid.NewGuid(), Guid.NewGuid(), Guid.NewGuid() }; - var command = theSession.Events.QueryAllRawEvents() - .Where(e => e.Id.In(eventsIds)) - .Where(e => e.AnyTenant()) - .ToCommand(); - - command.CommandText.ShouldContain("d.id = ANY(:p0)"); - command.CommandText.ShouldNotContain("CAST(d.data ->> 'Id' as uuid)"); - } -} diff --git a/src/EventSourcingTests/Bugs/Bug_3140_do_not_create_tables_for_live_aggregations.cs b/src/EventSourcingTests/Bugs/Bug_3140_do_not_create_tables_for_live_aggregations.cs deleted file mode 100644 index a530e3c165..0000000000 --- a/src/EventSourcingTests/Bugs/Bug_3140_do_not_create_tables_for_live_aggregations.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System; -using System.Diagnostics; -using System.Linq; -using System.Threading.Tasks; -using EventSourcingTests.Aggregation; -using Marten.Events.Aggregation; -using Marten.Events.Projections; -using Marten.Schema; -using Marten.Storage; -using Marten.Testing.Harness; -using Shouldly; -using Xunit; - -namespace EventSourcingTests.Bugs; - -public class Bug_3140_do_not_create_tables_for_live_aggregations : BugIntegrationContext -{ - [Fact] - public async Task should_not_create_tables_for_live_aggregations_when_registered_directly() - { - StoreOptions(opts => - { - opts.Projections.Add(new MyAggregateProjection(), ProjectionLifecycle.Live); - }); - - await theStore.Storage.ApplyAllConfiguredChangesToDatabaseAsync(); - - var existingTables = await theStore.Storage.Database.SchemaTables(); - existingTables.Any(x => x.QualifiedName == "bugs.mt_doc_aggregate").ShouldBeFalse(); - - var tables = theStore.Storage.AllObjects().OfType(); - tables.ShouldNotContain(x => x.DocumentType == typeof(MyAggregate)); - } -} - -public class MyAggregateProjection: SingleStreamProjection -{ - public void Apply(MyAggregate aggregate, AEvent e) => aggregate.ACount++; -} diff --git a/src/EventSourcingTests/Bugs/Bug_3389_flat_table_compilation_issue.cs b/src/EventSourcingTests/Bugs/Bug_3389_flat_table_compilation_issue.cs deleted file mode 100644 index ac5d9a3eea..0000000000 --- a/src/EventSourcingTests/Bugs/Bug_3389_flat_table_compilation_issue.cs +++ /dev/null @@ -1,61 +0,0 @@ -using System; -using Marten.Events.Projections.Flattened; -using Marten.Testing.Harness; -using System.Threading.Tasks; -using Marten.Events.Projections; -using Xunit; - -namespace EventSourcingTests.Bugs; - -public class Bug_3389_flat_table_compilation_issue : BugIntegrationContext -{ - [Fact] - public async System.Threading.Tasks.Task try_compilation() - { - StoreOptions(opts => - { - opts.Projections.Add(ProjectionLifecycle.Inline); - }); - - var id = theSession.Events.StartStream(new SiteCreated("one")).Id; - await theSession.SaveChangesAsync(); - } -} - -public record SiteCreated(string Name); - -public record SiteEnrolledToLite(); - -public record SiteLocationRecorded(decimal Latitude, decimal Longitude); - -public class SiteProjection : FlatTableProjection -{ - public SiteProjection() - : base("site_projection", SchemaNameSource.DocumentSchema) - { - _ = Table.AddColumn("id").AsPrimaryKey(); - - TeardownDataOnRebuild = true; - - Project(map => - { - _ = map.Map(x => x.Name); - - _ = map.SetValue("is_lite", 0); - _ = map.SetValue("created_at", DateTimeOffset.UtcNow.ToString()); - }); - - Project(map => - { - map.SetValue("is_lite", 0); - }); - - Project(map => - { - _ = map.Map(x => x.Latitude); - _ = map.Map(x => x.Longitude); - }); - } -} - - diff --git a/src/EventSourcingTests/Bugs/Bug_3465_multiple_fetch_for_writing_calls.cs b/src/EventSourcingTests/Bugs/Bug_3465_multiple_fetch_for_writing_calls.cs deleted file mode 100644 index 9a24565e31..0000000000 --- a/src/EventSourcingTests/Bugs/Bug_3465_multiple_fetch_for_writing_calls.cs +++ /dev/null @@ -1,108 +0,0 @@ -using System; -using System.Threading.Tasks; -using JasperFx.Core; -using Marten.Metadata; -using Marten.Testing.Harness; -using Shouldly; -using Xunit; - -namespace EventSourcingTests.Bugs; - -public class Bug_3465_multiple_fetch_for_writing_calls : BugIntegrationContext -{ - [Fact] - public async Task work_correctly() - { - StoreOptions(opts => - { - opts.Projections.LiveStreamAggregation(); - opts.Projections.LiveStreamAggregation(); - }); - - var streamId = CombGuidIdGeneration.NewGuid(); - theSession.Events.StartStream(streamId, new EventA(), new EventA(), new EventA()); - await theSession.SaveChangesAsync(); -// stream version is now 3 - - var otherSession = theStore.LightweightSession(); - var firstProjection = await otherSession.Events.FetchForWriting(streamId); - - firstProjection.StartingVersion.ShouldBe(3); - firstProjection.Aggregate.ShouldNotBeNull(); - firstProjection.Aggregate.Version.ShouldBe(3); - -// in another session, append more events - - theSession.Events.Append(streamId, new EventA(), new EventA()); - await theSession.SaveChangesAsync(); -// stream version is now 5 - - var secondProjection = await otherSession.Events.FetchForWriting(streamId); - secondProjection.Aggregate.ShouldNotBeNull(); - secondProjection.Aggregate.Version.ShouldBe(5); - -// attempt to append - otherSession.Events.Append(streamId, new EventA()); - -// should fail with concurrency error -// because current version of the stream (5) is ahead of the first optimistic lock (3) - await otherSession.SaveChangesAsync(); - } - - [Fact] - public async Task work_correctly_with_identity_map() - { - StoreOptions(opts => - { - opts.Projections.LiveStreamAggregation(); - opts.Projections.LiveStreamAggregation(); - }); - - using var session = theStore.IdentitySession(); - - var streamId = CombGuidIdGeneration.NewGuid(); - session.Events.StartStream(streamId, new EventA(), new EventA(), new EventA()); - await session.SaveChangesAsync(); -// stream version is now 3 - - var otherSession = theStore.LightweightSession(); - var firstProjection = await otherSession.Events.FetchForWriting(streamId); - - firstProjection.StartingVersion.ShouldBe(3); - firstProjection.Aggregate.ShouldNotBeNull(); - firstProjection.Aggregate.Version.ShouldBe(3); - -// in another session, append more events - - session.Events.Append(streamId, new EventA(), new EventA()); - await session.SaveChangesAsync(); -// stream version is now 5 - - var secondProjection = await otherSession.Events.FetchForWriting(streamId); - secondProjection.Aggregate.ShouldNotBeNull(); - secondProjection.Aggregate.Version.ShouldBe(5); - -// attempt to append - otherSession.Events.Append(streamId, new EventA()); - -// should fail with concurrency error -// because current version of the stream (5) is ahead of the first optimistic lock (3) - await otherSession.SaveChangesAsync(); - } -} - -public class SomeProjection : IRevisioned -{ - public Guid Id { get; set; } - public int A { get; set; } - public void Apply(EventA e) => A++; - public int Version { get; set; } -} - -public class SomeOtherProjection : IRevisioned -{ - public Guid Id { get; set; } - public int A { get; set; } - public void Apply(EventA e) => A++; - public int Version { get; set; } -} diff --git a/src/EventSourcingTests/Bugs/Bug_499_event_store_objects_should_not_be_erroneously_in_the_patch.cs b/src/EventSourcingTests/Bugs/Bug_499_event_store_objects_should_not_be_erroneously_in_the_patch.cs deleted file mode 100644 index b09fe88019..0000000000 --- a/src/EventSourcingTests/Bugs/Bug_499_event_store_objects_should_not_be_erroneously_in_the_patch.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.Threading.Tasks; -using Marten.Testing; -using Marten.Testing.Documents; -using Marten.Testing.Harness; -using Shouldly; -using Xunit; - -namespace EventSourcingTests.Bugs; - -public class Bug_499_event_store_objects_should_not_be_erroneously_in_the_patch: BugIntegrationContext -{ - [Fact] - public async Task not_using_the_event_store_should_not_be_in_patch() - { - StoreOptions(_ => _.Schema.For()); - - var patch = await theStore.Storage.Database.CreateMigrationAsync(); - - patch.UpdateSql().ShouldNotContain("mt_events"); - patch.UpdateSql().ShouldNotContain("mt_streams"); - } - -} diff --git a/src/EventSourcingTests/Bugs/Bug_705_FetchStreamState_before_AggregateStream.cs b/src/EventSourcingTests/Bugs/Bug_705_FetchStreamState_before_AggregateStream.cs deleted file mode 100644 index cf5ee1631a..0000000000 --- a/src/EventSourcingTests/Bugs/Bug_705_FetchStreamState_before_AggregateStream.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System; -using System.Threading.Tasks; -using Marten.Testing.Harness; -using Shouldly; -using Xunit; - -namespace EventSourcingTests.Bugs; - -public class Bug_705_FetchStreamState_before_AggregateStream: BugIntegrationContext -{ - [Fact] - public async Task call_fetch_stream_state_on_new_stream() - { - Guid id; - - await using (var session = theStore.LightweightSession()) - { - var joined = new MembersJoined { Members = new[] { "Rand", "Matt", "Perrin", "Thom" } }; - var departed = new MembersDeparted { Members = new[] { "Thom" } }; - - id = session.Events.StartStream(joined, departed).Id; - await session.SaveChangesAsync(); - } - - using (var store2 = SeparateStore()) - { - await using (var session = store2.LightweightSession()) - { - var state = await session.Events.FetchStreamStateAsync(id); - state.Version.ShouldBe(2); - } - } - } - -} diff --git a/src/EventSourcingTests/Bugs/Bug_9999_fetch_for_writing_async_projection_with_no_additional_events_should_return_projection.cs b/src/EventSourcingTests/Bugs/Bug_9999_fetch_for_writing_async_projection_with_no_additional_events_should_return_projection.cs deleted file mode 100644 index aca011cac5..0000000000 --- a/src/EventSourcingTests/Bugs/Bug_9999_fetch_for_writing_async_projection_with_no_additional_events_should_return_projection.cs +++ /dev/null @@ -1,69 +0,0 @@ -using System; -using System.Diagnostics; -using System.Threading; -using System.Threading.Tasks; -using Marten.Events; -using Marten.Events.CodeGeneration; -using Marten.Events.Projections; -using Marten.Schema; -using Marten.Testing.Harness; -using Xunit; - -namespace EventSourcingTests.Bugs; - -public class Bug_9999_fetch_for_writing_async_projection_with_no_additional_events_should_return_projection : BugIntegrationContext -{ - // ! IMPORTANT - this does not fail on the bug - for some reason the dynamic codegen doesn't produce the error. - // TODO: Rework this to properly highlight the error - [Fact] - public async Task override_the_optimistic_concurrency_on_projected_document() - { - StoreOptions(opts => - { - opts.Events.StreamIdentity = StreamIdentity.AsString; - opts.Projections.Snapshot(SnapshotLifecycle.Async); - }); - - var streamKey = Guid.NewGuid().ToString(); - - theSession.Events.StartStream(streamKey, new NamedEvent("foo"), new EventB()); - await theSession.SaveChangesAsync(); - - using var daemon = await theStore.BuildProjectionDaemonAsync(); - await daemon.RebuildProjectionAsync(CancellationToken.None); - - var result = await theSession.Events.FetchForWriting(streamKey); - - Assert.Equal(2, result.CurrentVersion); - Assert.Equal(2, result.StartingVersion); - Assert.NotNull(result.Aggregate); - Assert.Equal(streamKey, result.Aggregate.StreamKey); - // TODO: There is a weird bug here where ~25% of the time this is set to null. Seems to happen intermittently across all frameworks. No idea why. - Assert.Equal("foo", result.Aggregate.Name); - } - - - -} - -public record NamedEvent(string Name); - -// put it here to avoid a bug with rebuilding projections whose types are nested classes -// TODO: Write up this bug -public record TestProjection -{ - public TestProjection() - { - Debug.WriteLine("Called with default Ctor"); - } - - [Identity] - public string StreamKey { get; set; } = null!; - - public string Name { get; set; } = null!; - - public static TestProjection Create(NamedEvent @event) => new() - { - Name = @event.Name - }; -} diff --git a/src/EventSourcingTests/Bugs/Bug_fetch_for_writing_cache.cs b/src/EventSourcingTests/Bugs/Bug_fetch_for_writing_cache.cs deleted file mode 100644 index f305f3cf4c..0000000000 --- a/src/EventSourcingTests/Bugs/Bug_fetch_for_writing_cache.cs +++ /dev/null @@ -1,67 +0,0 @@ -using Marten.Events; -using Marten.Events.Aggregation; -using Marten.Events.Projections; -using Marten.Schema; -using Marten.Testing.Harness; -using System; -using System.Threading.Tasks; -using Shouldly; -using Xunit; - -namespace EventSourcingTests.Bugs; - -public class Bug_fetch_for_writing_cache: BugIntegrationContext -{ - [Fact] - public async Task Test() - { - StoreOptions(opts => - { - opts.Events.StreamIdentity = StreamIdentity.AsString; - opts.Projections.Add(ProjectionLifecycle.Inline); - opts.Schema - .For() - .Identity(x => x.StreamKey); - }); - - var streamKey = Guid.NewGuid().ToString(); - - theSession.Events.StartStream(streamKey, new NamedEvent2("foo")); - await theSession.SaveChangesAsync(); - - var test = await theSession.Events.FetchForWriting(streamKey); - test.Aggregate.Name.ShouldBe("foo"); - - test.AppendOne(new NamedEvent2("bar")); - //await theSession.Events.AppendOptimistic(streamKey, new NamedEvent2("bar")); If I commented the two lines above and uncommented this one it works fine - await theSession.SaveChangesAsync(); - - test = await theSession.Events.FetchForWriting(streamKey); - test.Aggregate.Name.ShouldBe("bar"); - } - -} - -public record NamedEvent2(string Name); - -public class TestProjection2: SingleStreamProjection -{ - public TestAggregate Create(NamedEvent2 @event) - => new TestAggregate(@event.Name); - - public TestAggregate Apply(NamedEvent2 @event, TestAggregate aggregate) - => aggregate with { Name = @event.Name }; -} - -public record TestAggregate -{ - public TestAggregate(string name) - { - Name = name; - } - - [Identity] - public string StreamKey { get; set; } = null!; - - public string Name { get; set; } = null!; -} diff --git a/src/EventSourcingTests/Bugs/nulls_in_event_name_cache.cs b/src/EventSourcingTests/Bugs/nulls_in_event_name_cache.cs index 5e8c5bd95a..d4807a7924 100644 --- a/src/EventSourcingTests/Bugs/nulls_in_event_name_cache.cs +++ b/src/EventSourcingTests/Bugs/nulls_in_event_name_cache.cs @@ -23,22 +23,21 @@ public async Task rebuild_with_unregistered_events_does_not_cause_null_ref() await session.SaveChangesAsync(); } - using (var store = SeparateStore(_ => - { - _.DatabaseSchemaName = theStore.Options.DatabaseSchemaName; - _.Events.StreamIdentity = StreamIdentity.AsGuid; - _.Projections.Add(ProjectionLifecycle.Inline); - _.Projections.Add(new CustomProjection(), ProjectionLifecycle.Async); - _.Connection(ConnectionSource.ConnectionString); - })) + await using var store = SeparateStore(opts => { - var daemon = await store.BuildProjectionDaemonAsync(); + opts.DatabaseSchemaName = theStore.Options.DatabaseSchemaName; + opts.Events.StreamIdentity = StreamIdentity.AsGuid; + opts.Projections.Add(ProjectionLifecycle.Inline); + opts.Projections.Add(new CustomProjection(), ProjectionLifecycle.Async); + opts.Connection(ConnectionSource.ConnectionString); + }); - // Populate EventGraph name cache with null event mappings by requesting a projection with no event restrictions - await daemon.RebuildProjectionAsync("EventSourcingTests.Bugs.CustomProjection", CancellationToken.None); - // Request a rebuild from a projection that uses the event filter - await daemon.RebuildProjectionAsync(CancellationToken.None); - } + var daemon = await store.BuildProjectionDaemonAsync(); + + // Populate EventGraph name cache with null event mappings by requesting a projection with no event restrictions + await daemon.RebuildProjectionAsync("EventSourcingTests.Bugs.CustomProjection", CancellationToken.None); + // Request a rebuild from a projection that uses the event filter + await daemon.RebuildProjectionAsync(CancellationToken.None); } } diff --git a/src/EventSourcingTests/EventSourcingTests.csproj b/src/EventSourcingTests/EventSourcingTests.csproj index 3ff393576f..59d99d9042 100644 --- a/src/EventSourcingTests/EventSourcingTests.csproj +++ b/src/EventSourcingTests/EventSourcingTests.csproj @@ -139,18 +139,6 @@ - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - xUnit1013 diff --git a/src/EventSourcingTests/Aggregation/fetching_async_aggregates_for_writing.cs b/src/EventSourcingTests/FetchForWriting/fetching_async_aggregates_for_writing.cs similarity index 92% rename from src/EventSourcingTests/Aggregation/fetching_async_aggregates_for_writing.cs rename to src/EventSourcingTests/FetchForWriting/fetching_async_aggregates_for_writing.cs index 51d6dcab5a..c5dfe56c1e 100644 --- a/src/EventSourcingTests/Aggregation/fetching_async_aggregates_for_writing.cs +++ b/src/EventSourcingTests/FetchForWriting/fetching_async_aggregates_for_writing.cs @@ -1,16 +1,19 @@ using System; +using System.Diagnostics; using System.Threading; using System.Threading.Tasks; +using EventSourcingTests.Aggregation; using Marten.Events; using Marten.Events.Projections; using Marten.Exceptions; +using Marten.Schema; using Marten.Storage; using Marten.Testing.Harness; using Shouldly; using Xunit; using Xunit.Abstractions; -namespace EventSourcingTests.Aggregation; +namespace EventSourcingTests.FetchForWriting; public class fetching_async_aggregates_for_writing : OneOffConfigurationsContext { @@ -537,4 +540,48 @@ public async Task fetch_aggregate_that_is_in_progress() stream.Aggregate.BCount.ShouldBe(6); } + + [Fact] + public async Task override_the_optimistic_concurrency_on_projected_document() + { + StoreOptions(opts => + { + opts.Events.StreamIdentity = StreamIdentity.AsString; + opts.Projections.Snapshot(SnapshotLifecycle.Async); + }); + + var streamKey = Guid.NewGuid().ToString(); + + theSession.Events.StartStream(streamKey, new NamedEvent("foo"), new EventB()); + await theSession.SaveChangesAsync(); + + using var daemon = await theStore.BuildProjectionDaemonAsync(); + await daemon.RebuildProjectionAsync(CancellationToken.None); + + var result = await theSession.Events.FetchForWriting(streamKey); + + Assert.Equal(2, result.CurrentVersion); + Assert.Equal(2, result.StartingVersion); + Assert.NotNull(result.Aggregate); + Assert.Equal(streamKey, result.Aggregate.StreamKey); + // TODO: There is a weird bug here where ~25% of the time this is set to null. Seems to happen intermittently across all frameworks. No idea why. + Assert.Equal("foo", result.Aggregate.Name); + } + } + +public record NamedEvent(string Name); + +public record TestProjection +{ + [Identity] + public string StreamKey { get; set; } = null!; + + public string Name { get; set; } = null!; + + public static TestProjection Create(NamedEvent @event) => new() + { + Name = @event.Name + }; +} + diff --git a/src/EventSourcingTests/Aggregation/fetching_inline_aggregates_for_writing.cs b/src/EventSourcingTests/FetchForWriting/fetching_inline_aggregates_for_writing.cs similarity index 92% rename from src/EventSourcingTests/Aggregation/fetching_inline_aggregates_for_writing.cs rename to src/EventSourcingTests/FetchForWriting/fetching_inline_aggregates_for_writing.cs index 4afc42bdd4..78c04d3759 100644 --- a/src/EventSourcingTests/Aggregation/fetching_inline_aggregates_for_writing.cs +++ b/src/EventSourcingTests/FetchForWriting/fetching_inline_aggregates_for_writing.cs @@ -1,17 +1,20 @@ using System; using System.Threading.Tasks; +using EventSourcingTests.Aggregation; using Marten; using Marten.Events; +using Marten.Events.Aggregation; using Marten.Events.Projections; using Marten.Exceptions; +using Marten.Schema; using Marten.Storage; using Marten.Testing.Harness; using Microsoft.Extensions.Hosting; -using Xunit; using Shouldly; +using Xunit; using Xunit.Abstractions; -namespace EventSourcingTests.Aggregation; +namespace EventSourcingTests.FetchForWriting; public class fetching_inline_aggregates_for_writing : OneOffConfigurationsContext { @@ -587,4 +590,57 @@ public async Task silently_turns_on_identity_map_for_inline_aggregates() loadedFresh.ACount.ShouldBe(2); } + [Fact] + public async Task fetch_for_writing_cache() + { + StoreOptions(opts => + { + opts.Events.StreamIdentity = StreamIdentity.AsString; + opts.Projections.Add(ProjectionLifecycle.Inline); + opts.Schema + .For() + .Identity(x => x.StreamKey); + }); + + var streamKey = Guid.NewGuid().ToString(); + + theSession.Events.StartStream(streamKey, new NamedEvent2("foo")); + await theSession.SaveChangesAsync(); + + var test = await theSession.Events.FetchForWriting(streamKey); + test.Aggregate.Name.ShouldBe("foo"); + + test.AppendOne(new NamedEvent2("bar")); + //await theSession.Events.AppendOptimistic(streamKey, new NamedEvent2("bar")); If I commented the two lines above and uncommented this one it works fine + await theSession.SaveChangesAsync(); + + test = await theSession.Events.FetchForWriting(streamKey); + test.Aggregate.Name.ShouldBe("bar"); + } + +} + +public record NamedEvent2(string Name); + +public class TestProjection2: SingleStreamProjection +{ + public TestAggregate Create(NamedEvent2 @event) + => new TestAggregate(@event.Name); + + public TestAggregate Apply(NamedEvent2 @event, TestAggregate aggregate) + => aggregate with { Name = @event.Name }; } + +public record TestAggregate +{ + public TestAggregate(string name) + { + Name = name; + } + + [Identity] + public string StreamKey { get; set; } = null!; + + public string Name { get; set; } = null!; +} + diff --git a/src/EventSourcingTests/Aggregation/fetching_live_aggregates_for_writing.cs b/src/EventSourcingTests/FetchForWriting/fetching_live_aggregates_for_writing.cs similarity index 82% rename from src/EventSourcingTests/Aggregation/fetching_live_aggregates_for_writing.cs rename to src/EventSourcingTests/FetchForWriting/fetching_live_aggregates_for_writing.cs index 466673494f..13573826b7 100644 --- a/src/EventSourcingTests/Aggregation/fetching_live_aggregates_for_writing.cs +++ b/src/EventSourcingTests/FetchForWriting/fetching_live_aggregates_for_writing.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using EventSourcingTests.Aggregation; using Marten; using Marten.Events; using Marten.Events.Aggregation; @@ -9,12 +10,12 @@ using Marten.Events.Projections; using Marten.Exceptions; using Marten.Metadata; +using Marten.Schema.Identity; using Marten.Testing.Harness; -using NSubstitute; using Shouldly; using Xunit; -namespace EventSourcingTests.Aggregation; +namespace EventSourcingTests.FetchForWriting; public class fetching_live_aggregates_for_writing: IntegrationContext { @@ -372,6 +373,86 @@ await Should.ThrowAsync(async () => }); } + [Fact] + public async Task work_correctly_with_multiple_calls() + { + StoreOptions(opts => + { + opts.Projections.LiveStreamAggregation(); + opts.Projections.LiveStreamAggregation(); + }); + + var streamId = CombGuidIdGeneration.NewGuid(); + theSession.Events.StartStream(streamId, new EventA(), new EventA(), new EventA()); + await theSession.SaveChangesAsync(); +// stream version is now 3 + + var otherSession = theStore.LightweightSession(); + var firstProjection = await otherSession.Events.FetchForWriting(streamId); + + firstProjection.StartingVersion.ShouldBe(3); + firstProjection.Aggregate.ShouldNotBeNull(); + firstProjection.Aggregate.Version.ShouldBe(3); + +// in another session, append more events + + theSession.Events.Append(streamId, new EventA(), new EventA()); + await theSession.SaveChangesAsync(); +// stream version is now 5 + + var secondProjection = await otherSession.Events.FetchForWriting(streamId); + secondProjection.Aggregate.ShouldNotBeNull(); + secondProjection.Aggregate.Version.ShouldBe(5); + +// attempt to append + otherSession.Events.Append(streamId, new EventA()); + +// should fail with concurrency error +// because current version of the stream (5) is ahead of the first optimistic lock (3) + await otherSession.SaveChangesAsync(); + } + + [Fact] + public async Task work_correctly_for_multiple_calls_with_identity_map() + { + StoreOptions(opts => + { + opts.Projections.LiveStreamAggregation(); + opts.Projections.LiveStreamAggregation(); + }); + + using var session = theStore.IdentitySession(); + + var streamId = CombGuidIdGeneration.NewGuid(); + session.Events.StartStream(streamId, new EventA(), new EventA(), new EventA()); + await session.SaveChangesAsync(); +// stream version is now 3 + + var otherSession = theStore.LightweightSession(); + var firstProjection = await otherSession.Events.FetchForWriting(streamId); + + firstProjection.StartingVersion.ShouldBe(3); + firstProjection.Aggregate.ShouldNotBeNull(); + firstProjection.Aggregate.Version.ShouldBe(3); + +// in another session, append more events + + session.Events.Append(streamId, new EventA(), new EventA()); + await session.SaveChangesAsync(); +// stream version is now 5 + + var secondProjection = await otherSession.Events.FetchForWriting(streamId); + secondProjection.Aggregate.ShouldNotBeNull(); + secondProjection.Aggregate.Version.ShouldBe(5); + +// attempt to append + otherSession.Events.Append(streamId, new EventA()); + +// should fail with concurrency error +// because current version of the stream (5) is ahead of the first optimistic lock (3) + await otherSession.SaveChangesAsync(); + } + } public class SimpleAggregate : IRevisioned @@ -530,3 +611,19 @@ public ValueTask>> SliceAsyncEvents public void Apply(CEvent e, Totals totals) => totals.Count++; public void Apply(DEvent e, Totals totals) => totals.Count++; } + +public class SomeProjection : IRevisioned +{ + public Guid Id { get; set; } + public int A { get; set; } + public void Apply(EventA e) => A++; + public int Version { get; set; } +} + +public class SomeOtherProjection : IRevisioned +{ + public Guid Id { get; set; } + public int A { get; set; } + public void Apply(EventA e) => A++; + public int Version { get; set; } +} diff --git a/src/EventSourcingTests/Projections/EventSliceTests.cs b/src/EventSourcingTests/Projections/EventSliceTests.cs index dcfd9f806c..f4f4871416 100644 --- a/src/EventSourcingTests/Projections/EventSliceTests.cs +++ b/src/EventSourcingTests/Projections/EventSliceTests.cs @@ -1,6 +1,7 @@ using System; using System.Linq; using EventSourcingTests.Aggregation; +using EventSourcingTests.FetchForWriting; using JasperFx.Core.Reflection; using Marten.Events.Projections; using Marten.Storage; diff --git a/src/EventSourcingTests/Projections/Flattened/flat_table_projection_with_stream_id_identifier_end_to_end.cs b/src/EventSourcingTests/Projections/Flattened/flat_table_projection_with_stream_id_identifier_end_to_end.cs index cbc474582f..9eecaf8b1b 100644 --- a/src/EventSourcingTests/Projections/Flattened/flat_table_projection_with_stream_id_identifier_end_to_end.cs +++ b/src/EventSourcingTests/Projections/Flattened/flat_table_projection_with_stream_id_identifier_end_to_end.cs @@ -6,6 +6,7 @@ using JasperFx.Core; using JasperFx.Core.Reflection; using Marten.Events.Projections; +using Marten.Events.Projections.Flattened; using Marten.Testing.Harness; using Npgsql; using Shouldly; @@ -207,4 +208,53 @@ public async Task delete_a_row() count.As().ShouldBe(0); } + + [Fact] + public async System.Threading.Tasks.Task try_compilation() + { + StoreOptions(opts => + { + opts.Projections.Add(ProjectionLifecycle.Inline); + }); + + var id = theSession.Events.StartStream(new SiteCreated("one")).Id; + await theSession.SaveChangesAsync(); + } } + +public record SiteCreated(string Name); + +public record SiteEnrolledToLite(); + +public record SiteLocationRecorded(decimal Latitude, decimal Longitude); + +public class SiteProjection : FlatTableProjection +{ + public SiteProjection() + : base("site_projection", SchemaNameSource.DocumentSchema) + { + _ = Table.AddColumn("id").AsPrimaryKey(); + + TeardownDataOnRebuild = true; + + Project(map => + { + _ = map.Map(x => x.Name); + + _ = map.SetValue("is_lite", 0); + _ = map.SetValue("created_at", DateTimeOffset.UtcNow.ToString()); + }); + + Project(map => + { + map.SetValue("is_lite", 0); + }); + + Project(map => + { + _ = map.Map(x => x.Latitude); + _ = map.Map(x => x.Longitude); + }); + } +} + diff --git a/src/EventSourcingTests/Projections/using_explicit_code_for_live_aggregation.cs b/src/EventSourcingTests/Projections/using_explicit_code_for_live_aggregation.cs index 3e3d346bd1..02f72b871a 100644 --- a/src/EventSourcingTests/Projections/using_explicit_code_for_live_aggregation.cs +++ b/src/EventSourcingTests/Projections/using_explicit_code_for_live_aggregation.cs @@ -4,6 +4,7 @@ using System.Threading; using System.Threading.Tasks; using EventSourcingTests.Aggregation; +using EventSourcingTests.FetchForWriting; using Marten; using Marten.Events; using Marten.Events.Aggregation; diff --git a/src/EventSourcingTests/cannot_start_a_stream_with_zero_events.cs b/src/EventSourcingTests/cannot_start_a_stream_with_zero_events.cs index 66db29441e..35c007a5de 100644 --- a/src/EventSourcingTests/cannot_start_a_stream_with_zero_events.cs +++ b/src/EventSourcingTests/cannot_start_a_stream_with_zero_events.cs @@ -57,7 +57,7 @@ public void should_be_unable_to_start_a_stream_without_any_events_as_string() } [Fact] - public async Task Bug_1388_cannot_start_the_same_stream_twice_with_the_same_session() + public async Task cannot_start_the_same_stream_twice_with_the_same_session() { StoreOptions(x => x.Events.StreamIdentity = StreamIdentity.AsString); @@ -74,4 +74,4 @@ public async Task Bug_1388_cannot_start_the_same_stream_twice_with_the_same_sess }); } -} \ No newline at end of file +} diff --git a/src/EventSourcingTests/end_to_end_event_capture_and_fetching_the_stream_Tests.cs b/src/EventSourcingTests/end_to_end_event_capture_and_fetching_the_stream.cs similarity index 99% rename from src/EventSourcingTests/end_to_end_event_capture_and_fetching_the_stream_Tests.cs rename to src/EventSourcingTests/end_to_end_event_capture_and_fetching_the_stream.cs index 815a7130c5..bf66d3fc72 100644 --- a/src/EventSourcingTests/end_to_end_event_capture_and_fetching_the_stream_Tests.cs +++ b/src/EventSourcingTests/end_to_end_event_capture_and_fetching_the_stream.cs @@ -16,14 +16,14 @@ namespace EventSourcingTests; -public class end_to_end_event_capture_and_fetching_the_stream_Tests: OneOffConfigurationsContext +public class end_to_end_event_capture_and_fetching_the_stream: OneOffConfigurationsContext { private readonly ITestOutputHelper _output; private static readonly string[] SameTenants = { "tenant", "tenant" }; private static readonly string[] DifferentTenants = { "tenant", "differentTenant" }; private static readonly string[] DefaultTenant = { Tenancy.DefaultTenantId }; - public end_to_end_event_capture_and_fetching_the_stream_Tests(ITestOutputHelper output) + public end_to_end_event_capture_and_fetching_the_stream(ITestOutputHelper output) { _output = output; } diff --git a/src/EventSourcingTests/fake_aggregate.js b/src/EventSourcingTests/fake_aggregate.js deleted file mode 100644 index 186b74da62..0000000000 --- a/src/EventSourcingTests/fake_aggregate.js +++ /dev/null @@ -1,19 +0,0 @@ -var mt_transforms = require('mt_transforms'); -mt_transforms.snapshot({ - name: 'fake_aggregate', - $init: function () { - return { ANames: [], BNames: [], CNames: [], DNames: [] } - }, - event_a: function (a, e) { - a.ANames.push(e.Name); - }, - event_b: function (a, e) { - a.BNames.push(e.Name); - }, - event_c: function (a, e) { - c.ANames.push(e.Name); - }, - event_d: function (a, e) { - d.ANames.push(e.Name); - } -}) diff --git a/src/EventSourcingTests/fetching_stream_state.cs b/src/EventSourcingTests/fetching_stream_state.cs index d40167f4c7..2cbc62979c 100644 --- a/src/EventSourcingTests/fetching_stream_state.cs +++ b/src/EventSourcingTests/fetching_stream_state.cs @@ -238,5 +238,33 @@ public async Task can_fetch_the_stream_events_through_batch_query() events.Count.ShouldBe(2); } + + [Fact] + public async Task call_fetch_stream_state_on_new_stream() + { + UseStreamIdentity(StreamIdentity.AsGuid); + + Guid id; + + await using (var session = theStore.LightweightSession()) + { + var joined = new MembersJoined { Members = new[] { "Rand", "Matt", "Perrin", "Thom" } }; + var departed = new MembersDeparted { Members = new[] { "Thom" } }; + + id = session.Events.StartStream(joined, departed).Id; + await session.SaveChangesAsync(); + } + + using (var store2 = SeparateStore()) + { + await using (var session = store2.LightweightSession()) + { + var state = await session.Events.FetchStreamStateAsync(id); + state.Version.ShouldBe(2); + } + } + } + + } diff --git a/src/EventSourcingTests/location.js b/src/EventSourcingTests/location.js deleted file mode 100644 index 16716f9615..0000000000 --- a/src/EventSourcingTests/location.js +++ /dev/null @@ -1,14 +0,0 @@ -var mt_transforms = require('mt_transforms'); -mt_transforms.transform({ - timing: 'inline', - name: 'location', - - // TODO -- like to capture the EventStream here. - members_joined: function (evt, metadata) { - return { Day: evt.Day, Location: evt.Location, Quest: metadata.stream } - }, - - members_departed: function (evt, metadata) { - return { Day: evt.Day, Location: evt.Location, Quest: metadata.stream } - } -}); diff --git a/src/EventSourcingTests/party.js b/src/EventSourcingTests/party.js deleted file mode 100644 index 6556b7e20a..0000000000 --- a/src/EventSourcingTests/party.js +++ /dev/null @@ -1,21 +0,0 @@ -var mt_transforms = require('mt_transforms'); -mt_transforms.snapshot({ - timing: 'inline', - name: 'party', - - $init: function (stream) { - return { Quest: stream.id, Members: [], Day: 0, Location: 'Nowhere' } - }, - - members_joined: function (aggregate, event) { - aggregate.Members = aggregate.Members.concat(event.Members); - aggregate.Location = event.Location; - aggregate.Day = event.Day; - }, - - quest_started: function (aggregate, event) { - aggregate.Members = aggregate.Members.concat(event.Members); - aggregate.Location = event.Location; - aggregate.Day = 0; - } -}); diff --git a/src/EventSourcingTests/querying_event_data_with_linq.cs b/src/EventSourcingTests/querying_event_data_with_linq.cs index 1a4de9d899..37548496b4 100644 --- a/src/EventSourcingTests/querying_event_data_with_linq.cs +++ b/src/EventSourcingTests/querying_event_data_with_linq.cs @@ -2,8 +2,10 @@ using System.Linq; using System.Threading.Tasks; using EventSourcingTests.Aggregation; +using EventSourcingTests.FetchForWriting; using Marten; using Marten.Events; +using Marten.Events.Archiving; using Marten.Testing.Harness; using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities; using Shouldly; @@ -257,6 +259,53 @@ public async Task can_search_by_event_types() } } + [Fact] // was GH-2777 + public async Task querying_with_linq_against_events_and_event_has_id_property() + { + StoreOptions(opts => + { + opts.Events.StreamIdentity = StreamIdentity.AsString; + opts.Logger(new TestOutputMartenLogger(_output)); + }, true); + + await using (var session = theStore.LightweightSession()) + { + session.Events.Append("a", new DummyEvent("a")); + + await session.SaveChangesAsync(); + } + + await using (var session = theStore.QuerySession()) + { + var ids = await session.Events.QueryRawEventDataOnly() + .Select(d => d.Id) // This causes the operation to fail + .ToListAsync(); + } + } + + [Fact] // was GH-2848 + public async Task can_make_the_query_with_distinct_and_count() + { + await theSession.Events.QueryAllRawEvents() + .Where(x => x.MaybeArchived()) + .Select(x => x.StreamKey) + .Distinct() + .CountAsync(); + } + + [Fact] + public void generate_proper_sql_with_querying_against_event_id() + { + var eventsIds = new[] { Guid.NewGuid(), Guid.NewGuid(), Guid.NewGuid() }; + var command = theSession.Events.QueryAllRawEvents() + .Where(e => e.Id.In(eventsIds)) + .Where(e => e.AnyTenant()) + .ToCommand(); + + command.CommandText.ShouldContain("d.id = ANY(:p0)"); + command.CommandText.ShouldNotContain("CAST(d.data ->> 'Id' as uuid)"); + } + /* * MORE!!! * Async everything @@ -267,3 +316,5 @@ public querying_event_data_with_linq(ITestOutputHelper output) theStore.Advanced.Clean.DeleteAllEventData(); } } + +public record DummyEvent(string Id); diff --git a/src/EventSourcingTests/schema_object_management.cs b/src/EventSourcingTests/schema_object_management.cs new file mode 100644 index 0000000000..f944de4782 --- /dev/null +++ b/src/EventSourcingTests/schema_object_management.cs @@ -0,0 +1,108 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using EventSourcingTests.Aggregation; +using EventSourcingTests.Projections; +using Marten; +using Marten.Events.Aggregation; +using Marten.Events.Projections; +using Marten.Storage; +using Marten.Testing; +using Marten.Testing.Documents; +using Marten.Testing.Harness; +using Shouldly; +using Weasel.Core; +using Weasel.Postgresql.Tables; +using Xunit; + +namespace EventSourcingTests; + +public class schema_object_management : OneOffConfigurationsContext +{ + [Fact] + public async Task can_build_schema_with_auto_create_none() + { + var id = Guid.NewGuid(); + + using (var store1 = DocumentStore.For(opts => + { + opts.Connection(ConnectionSource.ConnectionString); + opts.DatabaseSchemaName = "samples"; + })) + { + using (var session = store1.LightweightSession()) + { + session.Events.StartStream(id, new QuestStarted { Name = "Destroy the Orb" }, + new MonsterSlayed { Name = "Troll" }, new MonsterSlayed { Name = "Dragon" }); + await session.SaveChangesAsync(); + } + } + + #region sample_registering-event-types + var store2 = DocumentStore.For(_ => + { + _.DatabaseSchemaName = "samples"; + _.Connection(ConnectionSource.ConnectionString); + _.AutoCreateSchemaObjects = AutoCreate.None; + + _.Events.AddEventType(typeof(QuestStarted)); + _.Events.AddEventType(typeof(MonsterSlayed)); + }); + #endregion + + using (var session = store2.LightweightSession()) + { + session.Events.FetchStream(id).Count.ShouldBe(3); + } + + store2.Dispose(); + } + + [Fact] + public async Task not_using_the_event_store_should_not_be_in_patch() + { + StoreOptions(_ => _.Schema.For()); + + var patch = await theStore.Storage.Database.CreateMigrationAsync(); + + patch.UpdateSql().ShouldNotContain("mt_events"); + patch.UpdateSql().ShouldNotContain("mt_streams"); + } + + [Fact] + public async Task do_not_create_tables() + { + StoreOptions(opts => + { + opts.Projections.LiveStreamAggregation(); + }); + + await theStore.Storage.ApplyAllConfiguredChangesToDatabaseAsync(); + + var tables = theStore.Storage.AllObjects().OfType
(); + tables.ShouldNotContain(x => x.Identifier.Name.Contains(nameof(QuestParty), StringComparison.OrdinalIgnoreCase)); + } + + [Fact] + public async Task should_not_create_tables_for_live_aggregations_when_registered_directly() + { + StoreOptions(opts => + { + opts.Projections.Add(new MyAggregateProjection(), ProjectionLifecycle.Live); + }); + + await theStore.Storage.ApplyAllConfiguredChangesToDatabaseAsync(); + + var existingTables = await theStore.Storage.Database.SchemaTables(); + existingTables.Any(x => x.QualifiedName == "bugs.mt_doc_aggregate").ShouldBeFalse(); + + var tables = theStore.Storage.AllObjects().OfType(); + tables.ShouldNotContain(x => x.DocumentType == typeof(MyAggregate)); + } + +} + +public class MyAggregateProjection: SingleStreamProjection +{ + public void Apply(MyAggregate aggregate, AEvent e) => aggregate.ACount++; +} diff --git a/src/EventSourcingTests/using_the_schema_objects_Tests.cs b/src/EventSourcingTests/using_the_schema_objects_Tests.cs deleted file mode 100644 index bde1ed1b6f..0000000000 --- a/src/EventSourcingTests/using_the_schema_objects_Tests.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System; -using System.Threading.Tasks; -using EventSourcingTests.Projections; -using Marten; -using Marten.Testing.Harness; -using Shouldly; -using Weasel.Core; -using Xunit; - -namespace EventSourcingTests; - -public class using_the_schema_objects_Tests : OneOffConfigurationsContext -{ - [Fact] - public async Task can_build_schema_with_auto_create_none() - { - var id = Guid.NewGuid(); - - using (var store1 = DocumentStore.For(opts => - { - opts.Connection(ConnectionSource.ConnectionString); - opts.DatabaseSchemaName = "samples"; - })) - { - using (var session = store1.LightweightSession()) - { - session.Events.StartStream(id, new QuestStarted { Name = "Destroy the Orb" }, - new MonsterSlayed { Name = "Troll" }, new MonsterSlayed { Name = "Dragon" }); - await session.SaveChangesAsync(); - } - } - - #region sample_registering-event-types - var store2 = DocumentStore.For(_ => - { - _.DatabaseSchemaName = "samples"; - _.Connection(ConnectionSource.ConnectionString); - _.AutoCreateSchemaObjects = AutoCreate.None; - - _.Events.AddEventType(typeof(QuestStarted)); - _.Events.AddEventType(typeof(MonsterSlayed)); - }); - #endregion - - using (var session = store2.LightweightSession()) - { - session.Events.FetchStream(id).Count.ShouldBe(3); - } - - store2.Dispose(); - } - -} diff --git a/src/Marten.Testing/Harness/IntegrationContext.cs b/src/Marten.Testing/Harness/IntegrationContext.cs index 7b92c00386..32d4d68357 100644 --- a/src/Marten.Testing/Harness/IntegrationContext.cs +++ b/src/Marten.Testing/Harness/IntegrationContext.cs @@ -2,6 +2,7 @@ using System.Collections; using System.Collections.Generic; using System.Threading.Tasks; +using JasperFx.CodeGeneration; using Marten.Events; using Marten.Internal.CodeGeneration; using Weasel.Core; @@ -54,6 +55,23 @@ public IntegrationContext(DefaultStoreFixture fixture) _fixture = fixture; } + /// + /// Build a unique document store with the same configuration as the basic, + /// Guid-identified store from IntegrationContext + /// + /// + protected IDocumentStore SeparateStore() + { + return DocumentStore.For(opts => + { + opts.Connection(ConnectionSource.ConnectionString); + opts.AutoCreateSchemaObjects = AutoCreate.All; + + opts.GeneratedCodeMode = TypeLoadMode.Auto; + opts.ApplicationAssembly = GetType().Assembly; + }); + } + /// /// Switch the DocumentStore between stream identity styles, but reuse /// the underlying document store