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();
        }
Пример #2
0
        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));
        }
Пример #12
0
        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();
        }
Пример #13
0
        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);
        }
Пример #14
0
        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);
        }
Пример #15
0
        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());
        }
Пример #18
0
        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());
        }