Пример #1
0
        public async Task Find_restores_aggregate_using_memento_if_found(
            FakeUser user,
            string username)
        {
            // Arrange
            var memento = user.SaveToMemento();

            user.ChangeUsername(username);

            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.PendingEvents.Skip(1))
            .Verifiable();

            // Act
            FakeUser actual = await sut.Find(user.Id, CancellationToken.None);

            // Assert
            Mock.Get(eventStore).Verify();
            actual.ShouldBeEquivalentTo(
                user, opts => opts.Excluding(x => x.PendingEvents));
        }
Пример #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 SaveAndPublish_saves_events()
        {
            // Arrange
            var    user          = new FakeUser(id: Guid.NewGuid(), username: Guid.NewGuid().ToString());
            string operationId   = Guid.NewGuid().ToString();
            var    correlationId = Guid.NewGuid();
            string contributor   = Guid.NewGuid().ToString();

            user.ChangeUsername(username: Guid.NewGuid().ToString());
            var pendingEvents = new List <IDomainEvent>(user.PendingEvents);

            ISqlEventStore eventStore = Mock.Of <ISqlEventStore>();

            var sut = new SqlEventSourcedRepository <FakeUser>(
                eventStore,
                Mock.Of <ISqlEventPublisher>(),
                FakeUser.Factory);

            // Act
            await sut.SaveAndPublish(user, operationId, correlationId, contributor);

            // Assert
            Mock.Get(eventStore).Verify(
                x =>
                x.SaveEvents <FakeUser>(pendingEvents, operationId, correlationId, contributor, default),
                Times.Once());
        }
        public void SaveAndPublish_does_not_save_memento_if_fails_to_save_events()
        {
            // Arrange
            FakeUser user          = _fixture.Create <FakeUser>();
            string   operationId   = _fixture.Create <string>();
            var      correlationId = Guid.NewGuid();
            string   contributor   = _fixture.Create <string>();

            user.ChangeUsername("foo");
            Mock.Get(_eventStore)
            .Setup(
                x =>
                x.SaveEvents <FakeUser>(
                    It.IsAny <IEnumerable <IDomainEvent> >(),
                    It.IsAny <string>(),
                    It.IsAny <Guid?>(),
                    It.IsAny <string>(),
                    CancellationToken.None))
            .Throws <InvalidOperationException>();

            // Act
            Func <Task> action = () => _sut.SaveAndPublish(user, operationId, correlationId, contributor);

            // Assert
            action.ShouldThrow <InvalidOperationException>();
            Mock.Get(_mementoStore).Verify(
                x =>
                x.Save <FakeUser>(
                    user.Id,
                    It.IsAny <IMemento>(),
                    It.IsAny <CancellationToken>()),
                Times.Never());
        }
        public async Task SaveAndPublish_publishes_events()
        {
            // Arrange
            var    user          = new FakeUser(id: Guid.NewGuid(), username: Guid.NewGuid().ToString());
            string operationId   = Guid.NewGuid().ToString();
            var    correlationId = Guid.NewGuid();
            string contributor   = Guid.NewGuid().ToString();

            user.ChangeUsername(username: Guid.NewGuid().ToString());

            ISqlEventPublisher eventPublisher = Mock.Of <ISqlEventPublisher>();

            var sut = new SqlEventSourcedRepository <FakeUser>(
                Mock.Of <ISqlEventStore>(),
                eventPublisher,
                FakeUser.Factory);

            // Act
            await sut.SaveAndPublish(user, operationId, correlationId, contributor);

            // Assert
            Mock.Get(eventPublisher).Verify(
                x =>
                x.FlushPendingEvents(user.Id, default),
                Times.Once());
        }
        public async Task SaveAndPublish_saves_events()
        {
            // Arrange
            FakeUser          user              = _fixture.Create <FakeUser>();
            string            operationId       = _fixture.Create <string>();
            var               correlationId     = Guid.NewGuid();
            string            contributor       = _fixture.Create <string>();
            CancellationToken cancellationToken = new CancellationTokenSource().Token;

            user.ChangeUsername("foo");
            var pendingEvents = new List <IDomainEvent>(user.PendingEvents);

            // Act
            await _sut.SaveAndPublish(user, operationId, correlationId, contributor, cancellationToken);

            // Assert
            Mock.Get(_eventStore).Verify(
                x =>
                x.SaveEvents <FakeUser>(
                    pendingEvents,
                    operationId,
                    correlationId,
                    contributor,
                    cancellationToken),
                Times.Once());
        }
        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 void SaveAndPublish_does_not_publish_events_if_fails_to_save_events()
        {
            FakeUser user          = _fixture.Create <FakeUser>();
            string   operationId   = _fixture.Create <string>();
            var      correlationId = Guid.NewGuid();
            string   contributor   = _fixture.Create <string>();

            user.ChangeUsername(_fixture.Create("username"));
            Mock.Get(_eventStore)
            .Setup(
                x =>
                x.SaveEvents <FakeUser>(
                    It.IsAny <IEnumerable <IDomainEvent> >(),
                    operationId,
                    correlationId,
                    contributor,
                    CancellationToken.None))
            .Throws <InvalidOperationException>();

            Func <Task> action = () => _sut.SaveAndPublish(user, operationId, correlationId, contributor);

            action.ShouldThrow <InvalidOperationException>();
            Mock.Get(_eventPublisher).Verify(
                x =>
                x.FlushPendingEvents(user.Id, It.IsAny <CancellationToken>()),
                Times.Never());
        }
Пример #9
0
        public void Save_does_not_publish_events_if_fails_to_save(
            FakeUser user,
            Guid correlationId,
            string username)
        {
            // Arrange
            user.ChangeUsername(username);
            Mock.Get(eventStore)
            .Setup(
                x =>
                x.SaveEvents <FakeUser>(
                    It.IsAny <IEnumerable <IDomainEvent> >(),
                    It.IsAny <Guid?>(),
                    CancellationToken.None))
            .Throws <InvalidOperationException>();

            // Act
            Func <Task> action = () => sut.Save(user, correlationId, CancellationToken.None);

            // Assert
            action.ShouldThrow <InvalidOperationException>();
            Mock.Get(eventPublisher).Verify(
                x =>
                x.PublishPendingEvents <FakeUser>(
                    user.Id,
                    CancellationToken.None),
                Times.Never());
        }
Пример #10
0
        public void Find_does_not_load_events_if_fails_to_publish_events(
            FakeUser user,
            string username)
        {
            // Arrange
            user.ChangeUsername(username);
            Mock.Get(eventPublisher)
            .Setup(
                x =>
                x.PublishPendingEvents <FakeUser>(
                    user.Id,
                    CancellationToken.None))
            .Throws <InvalidOperationException>();

            // Act
            Func <Task> action = () => sut.Find(user.Id, CancellationToken.None);

            // Assert
            action.ShouldThrow <InvalidOperationException>();
            Mock.Get(eventStore).Verify(
                x =>
                x.LoadEvents <FakeUser>(
                    user.Id,
                    It.IsAny <int>(),
                    It.IsAny <CancellationToken>()),
                Times.Never());
        }
        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 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 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);
        }
Пример #15
0
        public async Task Find_restores_aggregate(
            FakeUser user,
            string username)
        {
            user.ChangeUsername(username);
            Mock.Get(eventStore)
            .Setup(x => x.LoadEvents <FakeUser>(user.Id, 0, CancellationToken.None))
            .ReturnsAsync(user.PendingEvents);

            FakeUser actual = await sut.Find(user.Id, CancellationToken.None);

            actual.ShouldBeEquivalentTo(user, opts => opts.Excluding(x => x.PendingEvents));
        }
        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);
        }
Пример #17
0
        public async Task Find_publishes_pending_events(
            FakeUser user,
            string username)
        {
            user.ChangeUsername(username);

            await sut.Find(user.Id, CancellationToken.None);

            Mock.Get(eventPublisher).Verify(
                x =>
                x.PublishPendingEvents <FakeUser>(
                    user.Id,
                    CancellationToken.None),
                Times.Once());
        }
        public async Task Find_publishes_pending_events()
        {
            FakeUser user = _fixture.Create <FakeUser>();

            user.ChangeUsername("foo");

            await _sut.Find(user.Id, CancellationToken.None);

            Mock.Get(_eventPublisher).Verify(
                x =>
                x.FlushPendingEvents <FakeUser>(
                    user.Id,
                    CancellationToken.None),
                Times.Once());
        }
Пример #19
0
        public async Task Save_publishes_events(
            FakeUser user,
            Guid correlationId,
            string username)
        {
            user.ChangeUsername(username);

            await sut.Save(user, correlationId, CancellationToken.None);

            Mock.Get(eventPublisher).Verify(
                x =>
                x.PublishPendingEvents <FakeUser>(
                    user.Id,
                    CancellationToken.None),
                Times.Once());
        }
        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 SaveAndPublish_publishes_events()
        {
            FakeUser user          = _fixture.Create <FakeUser>();
            string   operationId   = _fixture.Create <string>();
            var      correlationId = Guid.NewGuid();
            string   contributor   = Guid.NewGuid().ToString();

            user.ChangeUsername(_fixture.Create("username"));

            await _sut.SaveAndPublish(user, operationId, correlationId, contributor);

            Mock.Get(_eventPublisher).Verify(
                x =>
                x.FlushPendingEvents(user.Id, CancellationToken.None),
                Times.Once());
        }
Пример #22
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();
        }
        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));
        }
Пример #24
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);
        }
Пример #25
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);
        }
Пример #26
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");
            }
        }
Пример #27
0
        public async Task Save_saves_memento(
            FakeUser user,
            Guid correlationId,
            string username)
        {
            user.ChangeUsername(username);

            await sut.Save(user, correlationId, CancellationToken.None);

            Mock.Get(mementoStore).Verify(
                x =>
                x.Save <FakeUser>(
                    user.Id,
                    It.Is <FakeUserMemento>(
                        p =>
                        p.Version == user.Version &&
                        p.Username == user.Username),
                    CancellationToken.None),
                Times.Once());
        }
        public void SaveAndPublish_does_not_publish_events_if_fails_to_save_events()
        {
            // Arrange
            var    user          = new FakeUser(id: Guid.NewGuid(), username: Guid.NewGuid().ToString());
            string operationId   = Guid.NewGuid().ToString();
            var    correlationId = Guid.NewGuid();
            string contributor   = Guid.NewGuid().ToString();

            user.ChangeUsername(username: Guid.NewGuid().ToString());

            ISqlEventStore eventStore = Mock.Of <ISqlEventStore>();

            Mock.Get(eventStore)
            .Setup(
                x =>
                x.SaveEvents <FakeUser>(
                    It.IsAny <IEnumerable <IDomainEvent> >(),
                    operationId,
                    correlationId,
                    contributor,
                    default))
            .Throws <InvalidOperationException>();

            ISqlEventPublisher eventPublisher = Mock.Of <ISqlEventPublisher>();

            var sut = new SqlEventSourcedRepository <FakeUser>(
                eventStore,
                eventPublisher,
                FakeUser.Factory);

            // Act
            Func <Task> action = () => sut.SaveAndPublish(user, operationId, correlationId, contributor);

            // Assert
            action.ShouldThrow <InvalidOperationException>();
            Mock.Get(eventPublisher).Verify(
                x =>
                x.FlushPendingEvents(user.Id, It.IsAny <CancellationToken>()),
                Times.Never());
        }
        public async Task SaveAndPublish_saves_memento()
        {
            FakeUser user          = _fixture.Create <FakeUser>();
            string   operationId   = _fixture.Create <string>();
            var      correlationId = Guid.NewGuid();
            string   contributor   = _fixture.Create <string>();

            user.ChangeUsername("foo");

            await _sut.SaveAndPublish(user, operationId, correlationId, contributor);

            Mock.Get(_mementoStore).Verify(
                x =>
                x.Save <FakeUser>(
                    user.Id,
                    It.Is <FakeUserMemento>(
                        p =>
                        p.Version == user.Version &&
                        p.Username == user.Username),
                    CancellationToken.None),
                Times.Once());
        }
        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);
        }