diff --git a/src/Mongo/src/Eventuous.Projections.MongoDB/MongoProjector.cs b/src/Mongo/src/Eventuous.Projections.MongoDB/MongoProjector.cs index e0ed3205..9c064f74 100644 --- a/src/Mongo/src/Eventuous.Projections.MongoDB/MongoProjector.cs +++ b/src/Mongo/src/Eventuous.Projections.MongoDB/MongoProjector.cs @@ -11,17 +11,23 @@ namespace Eventuous.Projections.MongoDB; using Tools; [Obsolete("Use MongoProjector instead")] -public abstract class MongoProjection(IMongoDatabase database, ITypeMapper? typeMap = null) : MongoProjector(database, typeMap) +public abstract class MongoProjection(IMongoDatabase database, ITypeMapper? typeMap = null) : MongoProjector(database, null, typeMap) where T : ProjectedDocument; +public record MongoProjectionOptions where T : Document { + public string CollectionName { get; set; } = MongoCollectionName.For(); +} + /// /// Base class for MongoDB projectors. Specify your event handlers in the constructor using On methods family. /// /// [UsedImplicitly] -public abstract class MongoProjector(IMongoDatabase database, ITypeMapper? typeMap = null) : BaseEventHandler where T : ProjectedDocument { +public abstract class MongoProjector(IMongoDatabase database, MongoProjectionOptions? options = null, ITypeMapper? typeMap = null) + : BaseEventHandler where T : ProjectedDocument { [PublicAPI] - protected IMongoCollection Collection { get; } = Ensure.NotNull(database).GetDocumentCollection(); + protected IMongoCollection Collection { get; } = + options != null ? Ensure.NotNull(database).GetCollection(options?.CollectionName) : Ensure.NotNull(database).GetDocumentCollection(); readonly Dictionary _handlers = new(); readonly ITypeMapper _map = typeMap ?? TypeMap.Instance; diff --git a/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/ProjectWithBuilder.cs b/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/ProjectWithBuilder.cs index ef822224..8f5273dd 100644 --- a/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/ProjectWithBuilder.cs +++ b/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/ProjectWithBuilder.cs @@ -8,14 +8,18 @@ namespace Eventuous.Tests.Projections.MongoDB; [ClassDataSource] -public class ProjectWithBuilder(IntegrationFixture fixture) : ProjectionTestBase(nameof(ProjectWithBuilder), fixture) { +public class ProjectWithBuilder(IntegrationFixture fixture) { [Test] - public async Task ShouldProjectImported() { - var evt = DomainFixture.CreateImportBooking(); - var id = new BookingId(CreateId()); - var stream = StreamNameFactory.For(id); + [MethodDataSource(typeof(CollectionSource), nameof(CollectionSource.TestOptions))] + public async Task ShouldProjectImported(MongoProjectionOptions? options) { + var projectionFixture = new ProjectionTestBase(nameof(ProjectWithBuilder), fixture); + var evt = DomainFixture.CreateImportBooking(); + var id = new BookingId(projectionFixture.CreateId()); + var stream = StreamNameFactory.For(id); + + await projectionFixture.InitializeAsync(); - var first = await Act(stream, evt); + var first = await Act(projectionFixture, stream, evt); var expected = new BookingDocument(id.ToString()) { RoomId = evt.RoomId, @@ -29,9 +33,11 @@ public async Task ShouldProjectImported() { first.Doc.Should().BeEquivalentTo(expected); - var payment = new BookingPaymentRegistered(Fixture.Auto.Create(), evt.Price); + var payment = new BookingPaymentRegistered(projectionFixture.Fixture.Auto.Create(), evt.Price); - var second = await Act(stream, payment); + var second = await Act(projectionFixture, stream, payment); + + await projectionFixture.DisposeAsync(); expected = expected with { PaidAmount = payment.AmountPaid, @@ -42,18 +48,16 @@ public async Task ShouldProjectImported() { second.Doc.Should().BeEquivalentTo(expected); } - async Task<(AppendEventsResult Append, BookingDocument? Doc)> Act(StreamName stream, T evt) - where T : class { - var append = await Fixture.AppendEvent(stream, evt); - await WaitForPosition(append.GlobalPosition); - var actual = await Fixture.Mongo.LoadDocument(stream.GetId()); + static async Task<(AppendEventsResult Append, BookingDocument? Doc)> Act(ProjectionTestBase f, StreamName stream, T evt) where T : class { + var append = await f.Fixture.AppendEvent(stream, evt); + await f.WaitForPosition(append.GlobalPosition); + var actual = await f.Fixture.Mongo.LoadDocument(stream.GetId()); return (append, actual); } public class SutProjection : MongoProjector { - public SutProjection(IMongoDatabase database) - : base(database) { + public SutProjection(IMongoDatabase database) : base(database) { On( b => b .InsertOne @@ -88,3 +92,10 @@ public SutProjection(IMongoDatabase database) } } } + +public static class CollectionSource { + public static IEnumerable?> TestOptions() { + yield return null; + yield return new() { CollectionName = "test" }; + } +} diff --git a/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/ProjectWithBulkBuilder.cs b/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/ProjectWithBulkBuilder.cs index 2b60a752..a976cbc6 100644 --- a/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/ProjectWithBulkBuilder.cs +++ b/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/ProjectWithBulkBuilder.cs @@ -11,6 +11,7 @@ namespace Eventuous.Tests.Projections.MongoDB; public class ProjectWithBulkBuilder(IntegrationFixture fixture) : ProjectionTestBase(nameof(ProjectWithBulkBuilder), fixture) { [Test] public async Task ShouldProjectImported() { + await InitializeAsync(); var evt = DomainFixture.CreateImportBooking(); var id = new BookingId(CreateId()); var stream = StreamNameFactory.For(id); @@ -32,6 +33,7 @@ public async Task ShouldProjectImported() { var payment = new BookingPaymentRegistered(Fixture.Auto.Create(), evt.Price); var second = await Act(stream, payment); + await DisposeAsync(); expected = expected with { PaidAmount = payment.AmountPaid, diff --git a/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/ProjectingWithTypedHandlers.cs b/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/ProjectingWithTypedHandlers.cs index fdedff88..3ce2347f 100644 --- a/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/ProjectingWithTypedHandlers.cs +++ b/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/ProjectingWithTypedHandlers.cs @@ -12,6 +12,7 @@ public sealed class ProjectingWithTypedHandlers(IntegrationFixture fixture) : ProjectionTestBase(nameof(ProjectingWithTypedHandlers), fixture) { [Test] public async Task ShouldProjectImported(CancellationToken cancellationToken) { + await InitializeAsync(); var evt = DomainFixture.CreateImportBooking(); var id = new BookingId(CreateId()); var stream = StreamNameFactory.For(id); @@ -32,6 +33,8 @@ public async Task ShouldProjectImported(CancellationToken cancellationToken) { var actual = await Fixture.Mongo.LoadDocument(id.ToString(), cancellationToken: cancellationToken); actual.Should().Be(expected); + + await DisposeAsync(); } public class SutProjection : MongoProjector { diff --git a/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/ProjectionTestBase.cs b/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/ProjectionTestBase.cs index 78162464..d1cf47f0 100644 --- a/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/ProjectionTestBase.cs +++ b/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/ProjectionTestBase.cs @@ -21,7 +21,6 @@ protected ProjectionTestBase(string id) { protected abstract void ConfigureServices(IServiceCollection services, string id); - [Before(Test)] public async Task InitializeAsync() { _builder.ConfigureServices(collection => ConfigureServices(collection, _id)); Host = _builder.Build(); @@ -29,13 +28,12 @@ public async Task InitializeAsync() { await Host.StartAsync(); } - [After(Test)] public async Task DisposeAsync() => await Host.StopAsync(); } -public abstract class ProjectionTestBase(string id, IntegrationFixture fixture) : ProjectionTestBase(id) +public class ProjectionTestBase(string id, IntegrationFixture fixture) : ProjectionTestBase(id) where TProjection : class, IEventHandler { - protected readonly IntegrationFixture Fixture = fixture; + public readonly IntegrationFixture Fixture = fixture; protected override void ConfigureServices(IServiceCollection services, string id) => services @@ -47,9 +45,9 @@ protected override void ConfigureServices(IServiceCollection services, string id builder => builder.AddEventHandler() ); - protected string CreateId() => new(Guid.NewGuid().ToString("N")); + public string CreateId() => new(Guid.NewGuid().ToString("N")); - protected async Task WaitForPosition(ulong position) { + public async Task WaitForPosition(ulong position) { var checkpointStore = Host.Services.GetRequiredService(); var count = 100;