public async Task SaveEvents_does_not_insert_pending_event_entities_if_fails_to_insert_correlation_entities() { // Arrange var fixture = new Fixture(); var user = new FakeUser(Guid.NewGuid(), fixture.Create <string>()); user.ChangeUsername(fixture.Create <string>()); IList <IDomainEvent> domainEvents = user.FlushPendingEvents().ToList(); var correlationId = Guid.NewGuid(); var batch = new TableBatchOperation(); batch.Insert(new TableEntity { PartitionKey = AggregateEntity.GetPartitionKey(typeof(FakeUser), user.Id), RowKey = Correlation.GetRowKey(correlationId), }); await s_eventTable.ExecuteBatchAsync(batch); // Act Func <Task> action = () => _sut.SaveEvents <FakeUser>(domainEvents, correlationId: correlationId); // Assert action.ShouldThrow <DuplicateCorrelationException>(); string filter = PendingEvent.GetFilter(typeof(FakeUser), user.Id); var query = new TableQuery <PendingEvent> { FilterString = filter }; IEnumerable <PendingEvent> actual = await s_eventTable.ExecuteQuerySegmentedAsync(query, default); actual.Should().BeEmpty(); }
public async Task FlushPendingEvents_sends_all_pending_events_correctly() { // Arrange var messageBus = new MessageLogger(); var sut = new AzureEventPublisher(s_eventTable, s_serializer, messageBus); var fixture = new Fixture(); var user = new FakeUser(Guid.NewGuid(), fixture.Create <string>()); user.ChangeUsername(fixture.Create <string>()); string operationId = fixture.Create <string>(); var correlationId = Guid.NewGuid(); string contributor = fixture.Create <string>(); var envelopes = new List <Envelope <IDomainEvent> >( from domainEvent in user.FlushPendingEvents() let messageId = Guid.NewGuid() select new Envelope <IDomainEvent>(messageId, domainEvent, operationId, correlationId, contributor)); var batch = new TableBatchOperation(); foreach (Envelope <IDomainEvent> envelope in envelopes) { batch.Insert(PendingEvent.Create(typeof(FakeUser), envelope, s_serializer)); } await s_eventTable.ExecuteBatchAsync(batch); // Act await sut.FlushPendingEvents <FakeUser>(user.Id); // Assert messageBus.Log.ShouldAllBeEquivalentTo(envelopes); }
public async Task Find_restores_aggregate_from_events() { // Arrange FakeUser user = _fixture.Create <FakeUser>(); user.ChangeUsername(_fixture.Create("username")); Mock.Get(_eventStore) .Setup( x => x.LoadEvents <FakeUser>(user.Id, 0, CancellationToken.None)) .ReturnsAsync(user.FlushPendingEvents()) .Verifiable(); var sut = new SqlEventSourcedRepository <FakeUser>( _eventStore, _eventPublisher, FakeUser.Factory); // Act FakeUser actual = await sut.Find(user.Id, CancellationToken.None); // Assert Mock.Get(_eventStore).Verify(); actual.ShouldBeEquivalentTo(user); }
public async Task Find_restores_aggregate_using_memento_if_found() { // Arrange var user = new FakeUser(id: Guid.NewGuid(), username: Guid.NewGuid().ToString()); IMemento memento = user.SaveToMemento(); user.ChangeUsername(username: Guid.NewGuid().ToString()); IMementoStore mementoStore = Mock.Of <IMementoStore>(); Mock.Get(mementoStore) .Setup(x => x.Find <FakeUser>(user.Id, default)) .ReturnsAsync(memento); ISqlEventStore eventStore = Mock.Of <ISqlEventStore>(); Mock.Get(eventStore) .Setup(x => x.LoadEvents <FakeUser>(user.Id, 1, default)) .ReturnsAsync(user.FlushPendingEvents().Skip(1)) .Verifiable(); var sut = new SqlEventSourcedRepository <FakeUser>( eventStore, Mock.Of <ISqlEventPublisher>(), mementoStore, FakeUser.Factory, FakeUser.Factory); // Act FakeUser actual = await sut.Find(user.Id, default); // Assert Mock.Get(eventStore).Verify(); actual.ShouldBeEquivalentTo(user); }
public async Task Find_loads_events() { // Arrange var user = new FakeUser(id: Guid.NewGuid(), username: Guid.NewGuid().ToString()); user.ChangeUsername(username: Guid.NewGuid().ToString()); ISqlEventStore eventStore = Mock.Of <ISqlEventStore>(); Mock.Get(eventStore) .Setup(x => x.LoadEvents <FakeUser>(user.Id, 0, default)) .ReturnsAsync(user.FlushPendingEvents()) .Verifiable(); var sut = new SqlEventSourcedRepository <FakeUser>( eventStore, Mock.Of <ISqlEventPublisher>(), FakeUser.Factory); // Act await sut.Find(user.Id, default); // Assert Mock.Get(eventStore).Verify(); }
public async Task Find_restores_aggregate_using_memento_if_found() { // Arrange FakeUser user = _fixture.Create <FakeUser>(); IMemento memento = user.SaveToMemento(); user.ChangeUsername("foo"); Mock.Get(_mementoStore) .Setup(x => x.Find <FakeUser>(user.Id, CancellationToken.None)) .ReturnsAsync(memento); Mock.Get(_eventStore) .Setup( x => x.LoadEvents <FakeUser>(user.Id, 1, CancellationToken.None)) .ReturnsAsync(user.FlushPendingEvents().Skip(1)) .Verifiable(); // Act FakeUser actual = await _sut.Find(user.Id, CancellationToken.None); // Assert Mock.Get(_eventStore).Verify(); actual.ShouldBeEquivalentTo(user); }
public void FlushPendingEvents_clears_pending_events() { var fixture = new Fixture(); var sut = new FakeUser(Guid.NewGuid(), fixture.Create <string>()); sut.FlushPendingEvents(); sut.PendingEvents.Should().BeEmpty(); }
public void FlushPendingEvents_returns_all_pending_events() { var fixture = new Fixture(); var sut = new FakeUser(Guid.NewGuid(), fixture.Create <string>()); var expected = sut.PendingEvents.ToList(); IEnumerable <IDomainEvent> actual = sut.FlushPendingEvents(); actual.Should().Equal(expected); }
public async Task Find_restores_aggregate() { FakeUser user = _fixture.Create <FakeUser>(); user.ChangeUsername("foo"); Mock.Get(_eventStore) .Setup(x => x.LoadEvents <FakeUser>(user.Id, 0, CancellationToken.None)) .ReturnsAsync(user.FlushPendingEvents()); FakeUser actual = await _sut.Find(user.Id, CancellationToken.None); actual.ShouldBeEquivalentTo(user); }
public async Task Find_loads_events() { FakeUser user = _fixture.Create <FakeUser>(); user.ChangeUsername(_fixture.Create("username")); Mock.Get(_eventStore) .Setup( x => x.LoadEvents <FakeUser>(user.Id, 0, CancellationToken.None)) .ReturnsAsync(user.FlushPendingEvents()) .Verifiable(); await _sut.Find(user.Id, CancellationToken.None); Mock.Get(_eventStore).Verify(); }
public async Task LoadEvents_correctly_restores_domain_events_after_specified_version() { // Arrange var fixture = new Fixture(); var user = new FakeUser(Guid.NewGuid(), fixture.Create <string>()); user.ChangeUsername(fixture.Create <string>()); IList <IDomainEvent> domainEvents = user.FlushPendingEvents().ToList(); await _sut.SaveEvents <FakeUser>(domainEvents); // Act IEnumerable <IDomainEvent> actual = await _sut.LoadEvents <FakeUser>(user.Id, 1); // Assert actual.Should().BeInAscendingOrder(e => e.Version); actual.ShouldAllBeEquivalentTo(domainEvents.Skip(1)); }
public async Task FlushPendingEvents_absorbs_exception_caused_by_that_some_pending_event_already_deleted_since_loaded() { // Arrange var messageBus = new CompletableMessageBus(); var sut = new AzureEventPublisher(s_eventTable, s_serializer, messageBus); var fixture = new Fixture(); var user = new FakeUser(Guid.NewGuid(), fixture.Create <string>()); user.ChangeUsername(fixture.Create <string>()); var pendingEvents = new List <PendingEvent>( from message in user.FlushPendingEvents() let messageId = Guid.NewGuid() let envelope = new Envelope <IDomainEvent>(messageId, message) select PendingEvent.Create(typeof(FakeUser), envelope, s_serializer)); var batch = new TableBatchOperation(); foreach (PendingEvent pendingEvent in pendingEvents) { batch.Insert(pendingEvent); } await s_eventTable.ExecuteBatchAsync(batch); // Act Func <Task> action = async() => { Task flushTask = sut.FlushPendingEvents <FakeUser>(user.Id, CancellationToken.None); await s_eventTable.ExecuteAsync(TableOperation.Delete(pendingEvents.OrderBy(e => e.GetHashCode()).First())); messageBus.Complete(); await flushTask; }; // Assert action.ShouldNotThrow(); string filter = PendingEvent.GetFilter(typeof(FakeUser), user.Id); var query = new TableQuery { FilterString = filter }; TableQuerySegment actual = await s_eventTable.ExecuteQuerySegmentedAsync(query, default); actual.Should().BeEmpty(); }
public async Task FlushPendingEvents_does_not_delete_pending_events_if_fails_to_send() { // Arrange var exception = new InvalidOperationException(); IMessageBus messageBus = Mock.Of <IMessageBus>( x => x.Send(It.IsAny <IEnumerable <Envelope> >(), default) == Task.FromException(exception)); var sut = new AzureEventPublisher(s_eventTable, s_serializer, messageBus); var fixture = new Fixture(); var user = new FakeUser(Guid.NewGuid(), fixture.Create <string>()); user.ChangeUsername(fixture.Create <string>()); string operationId = fixture.Create <string>(); var correlationId = Guid.NewGuid(); string contributor = fixture.Create <string>(); var pendingEvents = new List <PendingEvent>( from message in user.FlushPendingEvents() let messageId = Guid.NewGuid() let envelope = new Envelope <IDomainEvent>(messageId, message, operationId, correlationId, contributor) select PendingEvent.Create(typeof(FakeUser), envelope, s_serializer)); var batch = new TableBatchOperation(); foreach (PendingEvent pendingEvent in pendingEvents) { batch.Insert(pendingEvent); } await s_eventTable.ExecuteBatchAsync(batch); // Act Func <Task> action = () => sut.FlushPendingEvents <FakeUser>(user.Id); // Assert action.ShouldThrow <InvalidOperationException>(); string filter = PendingEvent.GetFilter(typeof(FakeUser), user.Id); var query = new TableQuery <PendingEvent> { FilterString = filter }; TableQuerySegment <PendingEvent> actual = await s_eventTable.ExecuteQuerySegmentedAsync(query, default); actual.ShouldAllBeEquivalentTo(pendingEvents); }
public async Task FlushAllPendingEvents_sends_all_pending_events() { // Arrange await s_eventTable.DeleteIfExistsAsync(); await s_eventTable.CreateAsync(); var messageBus = new MessageLogger(); var sut = new AzureEventPublisher(s_eventTable, s_serializer, messageBus); var expected = new List <Envelope <IDomainEvent> >(); var fixture = new Fixture(); var userIds = fixture.CreateMany <Guid>().ToList(); foreach (Guid userId in userIds) { var user = new FakeUser(userId, fixture.Create <string>()); user.ChangeUsername(fixture.Create <string>()); string operationId = fixture.Create <string>(); var correlationId = Guid.NewGuid(); string contributor = fixture.Create <string>(); var envelopes = new List <Envelope <IDomainEvent> >( from message in user.FlushPendingEvents() let messageId = Guid.NewGuid() select new Envelope <IDomainEvent>(messageId, message, operationId, correlationId, contributor)); expected.AddRange(envelopes); var batch = new TableBatchOperation(); foreach (Envelope <IDomainEvent> envelope in envelopes) { batch.Insert(PendingEvent.Create(typeof(FakeUser), envelope, s_serializer)); } await s_eventTable.ExecuteBatchAsync(batch); } // Act await sut.FlushAllPendingEvents(CancellationToken.None); // Assert messageBus.Log.ShouldAllBeEquivalentTo(expected); }
public async Task FlushPendingEvents_absorbs_exception_caused_by_that_some_pending_event_already_deleted_since_loaded() { // Arrange var completionSource = new TaskCompletionSource <bool>(); IMessageBus messageBus = new AwaitingMessageBus(completionSource.Task); var fixture = new Fixture(); FakeUser user = fixture.Create <FakeUser>(); user.ChangeUsername(fixture.Create(nameof(user.Username))); var eventStore = new SqlEventStore(CreateDbContext, _serializer); await eventStore.SaveEvents <FakeUser>(user.FlushPendingEvents()); var sut = new SqlEventPublisher(CreateDbContext, _serializer, messageBus); // Act Func <Task> action = async() => { Task flushTask = sut.FlushPendingEvents(user.Id, CancellationToken.None); using (EventStoreDbContext db = CreateDbContext()) { List <PendingEvent> pendingEvents = await db .PendingEvents .Where(e => e.AggregateId == user.Id) .OrderBy(e => e.Version) .Take(1) .ToListAsync(); db.PendingEvents.RemoveRange(pendingEvents); await db.SaveChangesAsync(); } completionSource.SetResult(true); await flushTask; }; // Assert action.ShouldNotThrow(); using (EventStoreDbContext db = CreateDbContext()) { (await db.PendingEvents.AnyAsync(e => e.AggregateId == user.Id)) .Should().BeFalse("all pending events should be deleted"); } }
public async Task SaveEvents_throws_DuplicateCorrelationException_if_correlation_duplicate() { // Arrange var fixture = new Fixture(); var user = new FakeUser(Guid.NewGuid(), fixture.Create <string>()); user.ChangeUsername(fixture.Create <string>()); IList <IDomainEvent> domainEvents = user.FlushPendingEvents().ToList(); var correlationId = Guid.NewGuid(); await _sut.SaveEvents <FakeUser>(domainEvents.Take(1), correlationId : correlationId); // Act Func <Task> action = () => _sut.SaveEvents <FakeUser>(domainEvents.Skip(1), correlationId: correlationId); // Assert action.ShouldThrow <DuplicateCorrelationException>().Where( x => x.SourceType == typeof(FakeUser) && x.SourceId == user.Id && x.CorrelationId == correlationId && x.InnerException is StorageException); }
public async Task SaveEvents_inserts_persistent_event_entities_correctly() { // Arrange var fixture = new Fixture(); var user = new FakeUser(Guid.NewGuid(), fixture.Create <string>()); user.ChangeUsername(fixture.Create <string>()); IList <IDomainEvent> domainEvents = user.FlushPendingEvents().ToList(); string operationId = fixture.Create <string>(); var correlationId = Guid.NewGuid(); string contributor = fixture.Create <string>(); // Act await _sut.SaveEvents <FakeUser>(domainEvents, operationId, correlationId, contributor); // Assert string filter = PersistentEvent.GetFilter(typeof(FakeUser), user.Id); var query = new TableQuery <PersistentEvent> { FilterString = filter }; IEnumerable <PersistentEvent> actual = await s_eventTable.ExecuteQuerySegmentedAsync(query, default); actual.ShouldAllBeEquivalentTo( from domainEvent in domainEvents let envelope = new Envelope <IDomainEvent>( Guid.NewGuid(), domainEvent, operationId, correlationId, contributor) select PersistentEvent.Create(typeof(FakeUser), envelope, _serializer), opts => opts .Excluding(e => e.MessageId) .Excluding(e => e.Timestamp) .Excluding(e => e.ETag) .WithStrictOrdering()); }
public async Task FlushPendingEvents_deletes_all_pending_events() { // Arrange var sut = new AzureEventPublisher(s_eventTable, s_serializer, Mock.Of <IMessageBus>()); var fixture = new Fixture(); var user = new FakeUser(Guid.NewGuid(), fixture.Create <string>()); user.ChangeUsername(fixture.Create <string>()); var envelopes = new List <Envelope <IDomainEvent> >( from domainEvent in user.FlushPendingEvents() let messageId = Guid.NewGuid() select new Envelope <IDomainEvent>(messageId, domainEvent)); var batch = new TableBatchOperation(); foreach (Envelope <IDomainEvent> envelope in envelopes) { batch.Insert(PendingEvent.Create(typeof(FakeUser), envelope, s_serializer)); } await s_eventTable.ExecuteBatchAsync(batch); // Act await sut.FlushPendingEvents <FakeUser>(user.Id); // Assert string filter = PendingEvent.GetFilter(typeof(FakeUser), user.Id); var query = new TableQuery { FilterString = filter }; TableQuerySegment actual = await s_eventTable.ExecuteQuerySegmentedAsync(query, default); actual.Results.Should().BeEmpty(); }
public async Task SaveEvents_inserts_correlation_entity_correctly() { // Arrange var fixture = new Fixture(); var user = new FakeUser(Guid.NewGuid(), fixture.Create <string>()); IList <IDomainEvent> domainEvents = user.FlushPendingEvents().ToList(); string operationId = fixture.Create <string>(); var correlationId = Guid.NewGuid(); string contributor = fixture.Create <string>(); // Act await _sut.SaveEvents <FakeUser>(domainEvents, operationId, correlationId, contributor); // Assert string filter = Correlation.GetFilter(typeof(FakeUser), user.Id, correlationId); var query = new TableQuery <Correlation> { FilterString = filter }; IEnumerable <Correlation> actual = await s_eventTable.ExecuteQuerySegmentedAsync(query, default); actual.Should().ContainSingle().Which.ShouldBeEquivalentTo( new { CorrelationId = correlationId }, opts => opts.ExcludingMissingMembers()); }