public void SaveEvents_fails_if_events_not_have_same_source_id()
        {
            // Arrange
            var userId  = Guid.NewGuid();
            var created = new FakeUserCreated
            {
                SourceId = userId,
                Version  = 1,
                RaisedAt = DateTime.UtcNow,
            };

            var usernameChanged = new FakeUsernameChanged
            {
                SourceId = Guid.NewGuid(),
                Version  = 2,
                RaisedAt = DateTime.UtcNow,
            };

            var sut = new SqlEventStore(
                () => new FakeEventStoreDbContext(_dbContextOptions),
                new JsonMessageSerializer());

            // Act
            Func <Task> action = () => sut.SaveEvents <FakeUser>(new DomainEvent[] { created, usernameChanged });

            // Assert
            action.ShouldThrow <ArgumentException>().Where(x => x.ParamName == "events");
        }
        public async Task SaveEvents_inserts_Aggregate_correctly_for_new_aggregate_id()
        {
            // Arrange
            var userId  = Guid.NewGuid();
            var created = new FakeUserCreated();
            var events  = new DomainEvent[] { created };

            events.Raise(userId);

            var sut = new SqlEventStore(
                () => new FakeEventStoreDbContext(_dbContextOptions),
                new JsonMessageSerializer());

            // Act
            await sut.SaveEvents <FakeUser>(events);

            // Assert
            using (var db = new FakeEventStoreDbContext(_dbContextOptions))
            {
                Aggregate actual = await db
                                   .Aggregates
                                   .Where(a => a.AggregateId == userId)
                                   .SingleOrDefaultAsync();

                actual.Should().NotBeNull();
                actual.AggregateType.Should().Be(typeof(FakeUser).FullName);
                actual.Version.Should().Be(created.Version);
            }
        }
        public async Task PublishPendingEvents_deletes_pending_events(
            FakeUserCreated created,
            FakeUsernameChanged usernameChanged)
        {
            // Arrange
            var sourceId = Guid.NewGuid();

            var events = new DomainEvent[] { created, usernameChanged };

            RaiseEvents(sourceId, events);

            using (var db = new DataContext())
            {
                foreach (DomainEvent e in events)
                {
                    var envelope = new Envelope(e);
                    db.PendingEvents.Add(PendingEvent.FromEnvelope(envelope, serializer));
                }
                await db.SaveChangesAsync();
            }

            // Act
            await sut.PublishPendingEvents(sourceId, CancellationToken.None);

            // Assert
            using (var db = new DataContext())
            {
                bool actual = await db
                              .PendingEvents
                              .Where(e => e.AggregateId == sourceId)
                              .AnyAsync();

                actual.Should().BeFalse();
            }
        }
Exemple #4
0
        public async Task SaveEvents_sets_message_properties_correctly(
            FakeUserCreated created,
            FakeUsernameChanged usernameChanged,
            Guid correlationId)
        {
            // Arrange
            var events = new DomainEvent[] { created, usernameChanged };

            RaiseEvents(userId, events);

            // Act
            await sut.SaveEvents <FakeUser>(events, correlationId);

            // Asseert
            using (var db = new DataContext())
            {
                IEnumerable <object> expected = db
                                                .PendingEvents
                                                .Where(e => e.AggregateId == userId)
                                                .OrderBy(e => e.Version)
                                                .AsEnumerable()
                                                .Select(e => new { e.MessageId, e.CorrelationId })
                                                .ToList();

                IEnumerable <object> actual = db
                                              .PersistentEvents
                                              .Where(e => e.AggregateId == userId)
                                              .OrderBy(e => e.Version)
                                              .AsEnumerable()
                                              .Select(e => new { e.MessageId, e.CorrelationId })
                                              .ToList();

                actual.ShouldAllBeEquivalentTo(expected);
            }
        }
Exemple #5
0
        public async Task SaveEvents_removes_existing_UniqueIndexedProperty_if_property_value_is_null(
            FakeUserCreated created,
            FakeUsernameChanged usernameChanged)
        {
            // Arrange
            RaiseEvents(userId, created);
            await sut.SaveEvents <FakeUser>(new[] { created });

            usernameChanged.Username = null;
            RaiseEvents(userId, 1, usernameChanged);

            // Act
            await sut.SaveEvents <FakeUser>(new[] { usernameChanged });

            // Assert
            using (var db = new DataContext())
            {
                UniqueIndexedProperty actual = await db
                                               .UniqueIndexedProperties
                                               .Where(
                    p =>
                    p.AggregateType == typeof(FakeUser).FullName &&
                    p.PropertyName == nameof(FakeUserCreated.Username) &&
                    p.PropertyValue == created.Username)
                                               .SingleOrDefaultAsync();

                actual.Should().BeNull();
            }
        }
Exemple #6
0
        public async Task SaveEvents_inserts_UniqueIndexedProperty_for_new_property(
            FakeUserCreated created)
        {
            var events = new DomainEvent[] { created };

            RaiseEvents(userId, events);

            await sut.SaveEvents <FakeUser>(events);

            using (var db = new DataContext())
            {
                UniqueIndexedProperty actual = await db
                                               .UniqueIndexedProperties
                                               .Where(
                    p =>
                    p.AggregateType == typeof(FakeUser).FullName &&
                    p.PropertyName == nameof(FakeUserCreated.Username) &&
                    p.PropertyValue == created.Username)
                                               .SingleOrDefaultAsync();

                actual.Should().NotBeNull();
                actual.AggregateId.Should().Be(userId);
                actual.Version.Should().Be(created.Version);
            }
        }
Exemple #7
0
        public async Task SaveEvents_fails_if_unique_indexed_property_duplicate(
            Guid macId,
            Guid toshId,
            string duplicateUserName)
        {
            // Arrange
            var macCreated = new FakeUserCreated {
                Username = duplicateUserName
            };

            RaiseEvents(macId, macCreated);
            await sut.SaveEvents <FakeUser>(new[] { macCreated });

            // Act
            var toshCreated = new FakeUserCreated {
                Username = duplicateUserName
            };

            RaiseEvents(toshId, toshCreated);
            Func <Task> action = () => sut.SaveEvents <FakeUser>(new[] { toshCreated });

            // Assert
            action.ShouldThrow <Exception>();
            using (var db = new DataContext())
            {
                IQueryable <PersistentEvent> query =
                    from e in db.PersistentEvents
                    where e.AggregateId == toshId
                    select e;
                (await query.AnyAsync()).Should().BeFalse();
            }
        }
Exemple #8
0
        public async Task SaveEvents_updates_existing_UniqueIndexedProperty_correctly(
            FakeUserCreated created,
            FakeUsernameChanged usernameChanged)
        {
            // Arrange
            RaiseEvents(userId, created);
            await sut.SaveEvents <FakeUser>(new[] { created });

            RaiseEvents(userId, 1, usernameChanged);

            // Act
            await sut.SaveEvents <FakeUser>(new[] { usernameChanged });

            // Assert
            using (var db = new DataContext())
            {
                UniqueIndexedProperty actual = await db
                                               .UniqueIndexedProperties
                                               .Where(
                    p =>
                    p.AggregateId == userId &&
                    p.PropertyName == nameof(FakeUserCreated.Username))
                                               .SingleOrDefaultAsync();

                actual.Should().NotBeNull();
                actual.PropertyValue.Should().Be(usernameChanged.Username);
                actual.Version.Should().Be(usernameChanged.Version);
            }
        }
        public async Task PublishPendingEvents_commits_once(
            FakeUserCreated created,
            FakeUsernameChanged usernameChanged)
        {
            // Arrange
            var sourceId = Guid.NewGuid();

            var events = new DomainEvent[] { created, usernameChanged };

            RaiseEvents(sourceId, events);

            Mock.Get(mockDbContext.PendingEvents)
            .SetupData(events
                       .Select(e => new Envelope(e))
                       .Select(e => PendingEvent.FromEnvelope(e, serializer))
                       .ToList());

            var sut = new SqlEventPublisher(
                () => mockDbContext, serializer, messageBus);

            // Act
            await sut.PublishPendingEvents(sourceId, CancellationToken.None);

            // Assert
            Mock.Get(mockDbContext).Verify(
                x => x.SaveChangesAsync(CancellationToken.None),
                Times.Once());
        }
        public async Task SaveEvents_throws_DuplicateCorrelationException_if_correlation_duplicate()
        {
            // Arrange
            var userId = Guid.NewGuid();

            var created         = new FakeUserCreated();
            var usernameChanged = new FakeUsernameChanged();

            new DomainEvent[] { created, usernameChanged }.Raise(userId);

            var sut = new SqlEventStore(
                () => new FakeEventStoreDbContext(_dbContextOptions),
                new JsonMessageSerializer());

            var correlationId = Guid.NewGuid();

            await sut.SaveEvents <FakeUser>(new[] { created }, correlationId : correlationId);

            // Act
            Func <Task> action = () =>
                                 sut.SaveEvents <FakeUser>(new[] { usernameChanged }, correlationId: correlationId);

            // Assert
            action.ShouldThrow <DuplicateCorrelationException>().Where(
                x =>
                x.SourceType == typeof(FakeUser) &&
                x.SourceId == userId &&
                x.CorrelationId == correlationId &&
                x.InnerException is DbUpdateException);
        }
        public async Task SaveEvents_inserts_Correlation_entity_correctly()
        {
            // Arrange
            var userId = Guid.NewGuid();

            var created       = new FakeUserCreated();
            var correlationId = Guid.NewGuid();

            created.Raise(userId);
            DateTime now = DateTime.UtcNow;

            var sut = new SqlEventStore(
                () => new FakeEventStoreDbContext(_dbContextOptions),
                new JsonMessageSerializer());

            // Act
            await sut.SaveEvents <FakeUser>(new[] { created }, correlationId : correlationId);

            // Assert
            using (var db = new FakeEventStoreDbContext(_dbContextOptions))
            {
                Correlation correlation = await db
                                          .Correlations
                                          .Where(
                    c =>
                    c.AggregateType == typeof(FakeUser).FullName &&
                    c.AggregateId == userId &&
                    c.CorrelationId == correlationId)
                                          .SingleOrDefaultAsync();

                correlation.Should().NotBeNull();
                correlation.HandledAt.Should().BeCloseTo(now, precision: 100);
            }
        }
        public async Task SaveEvents_does_not_insert_UniqueIndexedProperty_if_property_value_is_null()
        {
            // Arrange
            var userId = Guid.NewGuid();

            var created = new FakeUserCreated {
                Username = null
            };

            created.Raise(userId);

            var sut = new SqlEventStore(
                () => new FakeEventStoreDbContext(_dbContextOptions),
                new JsonMessageSerializer());

            // Act
            await sut.SaveEvents <FakeUser>(new DomainEvent[] { created });

            // Assert
            using (var db = new FakeEventStoreDbContext(_dbContextOptions))
            {
                UniqueIndexedProperty actual = await db
                                               .UniqueIndexedProperties
                                               .Where(
                    p =>
                    p.AggregateType == typeof(FakeUser).FullName &&
                    p.PropertyName == nameof(FakeUserCreated.Username) &&
                    p.PropertyValue == created.Username)
                                               .SingleOrDefaultAsync();

                actual.Should().BeNull();
            }
        }
        public async Task SaveEvents_inserts_UniqueIndexedProperty_with_value_of_latest_indexed_event()
        {
            // Arrange
            var userId = Guid.NewGuid();

            var created         = new FakeUserCreated();
            var usernameChanged = new FakeUsernameChanged();
            var events          = new DomainEvent[] { created, usernameChanged };

            events.Raise(userId);

            var sut = new SqlEventStore(
                () => new FakeEventStoreDbContext(_dbContextOptions),
                new JsonMessageSerializer());

            // Act
            await sut.SaveEvents <FakeUser>(events);

            // Assert
            using (var db = new FakeEventStoreDbContext(_dbContextOptions))
            {
                UniqueIndexedProperty actual = await db
                                               .UniqueIndexedProperties
                                               .Where(
                    p =>
                    p.AggregateId == userId &&
                    p.PropertyName == nameof(FakeUserCreated.Username))
                                               .SingleOrDefaultAsync();

                actual.PropertyValue.Should().Be(usernameChanged.Username);
                actual.Version.Should().Be(usernameChanged.Version);
            }
        }
Exemple #14
0
        public async Task SaveEvents_does_not_insert_UniqueIndexedProperty_if_property_value_is_null()
        {
            var created = new FakeUserCreated {
                Username = null
            };
            var events = new DomainEvent[] { created };

            RaiseEvents(userId, events);

            await sut.SaveEvents <FakeUser>(events);

            using (var db = new DataContext())
            {
                UniqueIndexedProperty actual = await db
                                               .UniqueIndexedProperties
                                               .Where(
                    p =>
                    p.AggregateType == typeof(FakeUser).FullName &&
                    p.PropertyName == nameof(FakeUserCreated.Username) &&
                    p.PropertyValue == created.Username)
                                               .SingleOrDefaultAsync();

                actual.Should().BeNull();
            }
        }
Exemple #15
0
        public void FromEnvelope_sets_MessageId_correctly()
        {
            FakeUserCreated domainEvent = _fixture.Create <FakeUserCreated>();
            var             envelope    = new Envelope(domainEvent);
            var             actual      = PendingEvent.FromEnvelope(envelope, _serializer);

            actual.MessageId.Should().Be(envelope.MessageId);
        }
Exemple #16
0
        public void FromEnvelope_sets_Contributor_correctly()
        {
            FakeUserCreated domainEvent = _fixture.Create <FakeUserCreated>();
            string          contributor = _fixture.Create <string>();
            var             envelope    = new Envelope(Guid.NewGuid(), domainEvent, contributor: contributor);
            var             actual      = PendingEvent.FromEnvelope(envelope, _serializer);

            actual.Contributor.Should().Be(contributor);
        }
Exemple #17
0
        public void FromEnvelope_sets_CorrelationId_correctly()
        {
            FakeUserCreated domainEvent   = _fixture.Create <FakeUserCreated>();
            var             correlationId = Guid.NewGuid();
            var             envelope      = new Envelope(Guid.NewGuid(), domainEvent, correlationId: correlationId);
            var             actual        = PendingEvent.FromEnvelope(envelope, _serializer);

            actual.CorrelationId.Should().Be(correlationId);
        }
Exemple #18
0
        public void FromEnvelope_sets_OperationId_correctly()
        {
            FakeUserCreated domainEvent = _fixture.Create <FakeUserCreated>();
            string          operationId = $"{Guid.NewGuid()}";
            var             envelope    = new Envelope(Guid.NewGuid(), domainEvent, operationId);
            var             actual      = PendingEvent.FromEnvelope(envelope, _serializer);

            actual.OperationId.Should().Be(operationId);
        }
        public async Task SaveEvents_saves_pending_events_correctly()
        {
            // Arrange
            var userId = Guid.NewGuid();

            var created         = new FakeUserCreated();
            var usernameChanged = new FakeUsernameChanged();
            var events          = new DomainEvent[] { created, usernameChanged };

            events.Raise(userId);

            var serializer = new JsonMessageSerializer();

            var sut = new SqlEventStore(
                () => new FakeEventStoreDbContext(_dbContextOptions),
                serializer);

            string operationId   = Guid.NewGuid().ToString();
            var    correlationId = Guid.NewGuid();
            string contributor   = Guid.NewGuid().ToString();

            // Act
            await sut.SaveEvents <FakeUser>(events, operationId, correlationId, contributor);

            // Asseert
            using (var db = new FakeEventStoreDbContext(_dbContextOptions))
            {
                var pendingEvents = db
                                    .PendingEvents
                                    .Where(e => e.AggregateId == userId)
                                    .OrderBy(e => e.Version)
                                    .ToList();

                foreach (var t in pendingEvents.Zip(events, (pending, source) =>
                                                    new { Pending = pending, Source = source }))
                {
                    var actual = new
                    {
                        t.Pending.Version,
                        t.Pending.CorrelationId,
                        t.Pending.Contributor,
                        Message = serializer.Deserialize(t.Pending.EventJson),
                    };
                    actual.ShouldBeEquivalentTo(new
                    {
                        t.Source.Version,
                        OperationId   = operationId,
                        CorrelationId = correlationId,
                        Contributor   = contributor,
                        Message       = t.Source,
                    },
                                                opts => opts.RespectingRuntimeTypes());
                }
            }
        }
Exemple #20
0
        public async Task FindIdByUniqueIndexedProperty_returns_aggregate_id_if_property_found(
            FakeUserCreated created)
        {
            RaiseEvents(userId, created);
            await sut.SaveEvents <FakeUser>(new[] { created });

            Guid?actual = await
                          sut.FindIdByUniqueIndexedProperty <FakeUser>("Username", created.Username);

            actual.Should().Be(userId);
        }
Exemple #21
0
        public void SaveEvents_fails_if_events_contains_null(
            FakeUserCreated created)
        {
            var events = new DomainEvent[] { created, null };

            RaiseEvents(userId, created);

            Func <Task> action = () => sut.SaveEvents <FakeUser>(events);

            action.ShouldThrow <ArgumentException>()
            .Where(x => x.ParamName == "events");
        }
Exemple #22
0
        public void FromEnvelope_sets_EventJson_correctly()
        {
            FakeUserCreated domainEvent = _fixture.Create <FakeUserCreated>();
            var             envelope    = new Envelope(domainEvent);

            var actual = PendingEvent.FromEnvelope(envelope, _serializer);

            object message = _serializer.Deserialize(actual.EventJson);

            message.Should().BeOfType <FakeUserCreated>();
            message.ShouldBeEquivalentTo(domainEvent);
        }
Exemple #23
0
        public async Task FlushPendingEvents_sends_events_correctly()
        {
            // Arrange
            FakeUserCreated     created         = _fixture.Create <FakeUserCreated>();
            FakeUsernameChanged usernameChanged = _fixture.Create <FakeUsernameChanged>();
            var    sourceId      = Guid.NewGuid();
            string operationId   = _fixture.Create <string>();
            var    correlationId = Guid.NewGuid();
            string contributor   = _fixture.Create <string>();

            var domainEvents = new DomainEvent[] { created, usernameChanged };

            RaiseEvents(sourceId, domainEvents);

            var envelopes = new List <Envelope>();

            using (var db = new DataContext())
            {
                foreach (DomainEvent domainEvent in domainEvents)
                {
                    var envelope = new Envelope(Guid.NewGuid(), domainEvent, operationId, correlationId, contributor);
                    envelopes.Add(envelope);
                    db.PendingEvents.Add(PendingEvent.FromEnvelope(envelope, _serializer));
                }

                await db.SaveChangesAsync();
            }

            List <Envelope> batch = null;

            Mock.Get(_messageBus)
            .Setup(
                x =>
                x.Send(
                    It.IsAny <IEnumerable <Envelope> >(),
                    It.IsAny <CancellationToken>()))
            .Callback <IEnumerable <Envelope>, CancellationToken>((b, t) => batch = b.ToList())
            .Returns(Task.FromResult(true));

            // Act
            await _sut.FlushPendingEvents(sourceId, CancellationToken.None);

            // Assert
            Mock.Get(_messageBus).Verify(
                x =>
                x.Send(
                    It.IsAny <IEnumerable <Envelope> >(),
                    CancellationToken.None),
                Times.Once());
            batch.ShouldAllBeEquivalentTo(envelopes, opts => opts.RespectingRuntimeTypes());
        }
        public async Task SaveEvents_saves_events_correctly()
        {
            // Arrange
            var userId = Guid.NewGuid();

            var created         = new FakeUserCreated();
            var usernameChanged = new FakeUsernameChanged();
            var events          = new DomainEvent[] { created, usernameChanged };

            events.Raise(userId);

            var serializer = new JsonMessageSerializer();

            var sut = new SqlEventStore(
                () => new FakeEventStoreDbContext(_dbContextOptions),
                serializer);

            // Act
            await sut.SaveEvents <FakeUser>(events);

            // Asseert
            using (var db = new FakeEventStoreDbContext(_dbContextOptions))
            {
                IEnumerable <object> actual = db
                                              .PersistentEvents
                                              .Where(e => e.AggregateId == userId)
                                              .OrderBy(e => e.Version)
                                              .AsEnumerable()
                                              .Select(e => new
                {
                    e.AggregateType,
                    e.Version,
                    e.EventType,
                    Payload = serializer.Deserialize(e.EventJson),
                })
                                              .ToList();

                actual.Should().HaveCount(events.Length);

                IEnumerable <object> expected = events.Select(e => new
                {
                    AggregateType = typeof(FakeUser).FullName,
                    e.Version,
                    EventType = e.GetType().FullName,
                    Payload   = e,
                });

                actual.ShouldAllBeEquivalentTo(expected);
            }
        }
        public async Task SaveEvents_saves_events_correctly()
        {
            // Arrange
            FakeUserCreated     created         = _fixture.Create <FakeUserCreated>();
            FakeUsernameChanged usernameChanged = _fixture.Create <FakeUsernameChanged>();
            var events = new DomainEvent[] { created, usernameChanged };

            RaiseEvents(_userId, events);
            string operationId   = _fixture.Create <string>();
            var    correlationId = Guid.NewGuid();
            string contributor   = _fixture.Create <string>();

            // Act
            await _sut.SaveEvents <FakeUser>(events, operationId, correlationId, contributor);

            // Asseert
            using (var db = new DataContext())
            {
                IEnumerable <object> actual = db
                                              .PersistentEvents
                                              .Where(e => e.AggregateId == _userId)
                                              .OrderBy(e => e.Version)
                                              .AsEnumerable()
                                              .Select(e => new
                {
                    e.Version,
                    e.EventType,
                    e.OperationId,
                    e.CorrelationId,
                    e.Contributor,
                    Payload = _serializer.Deserialize(e.EventJson),
                })
                                              .ToList();

                actual.Should().HaveCount(events.Length);

                IEnumerable <object> expected = events.Select(e => new
                {
                    e.Version,
                    EventType     = e.GetType().FullName,
                    OperationId   = operationId,
                    CorrelationId = correlationId,
                    Contributor   = contributor,
                    Payload       = e,
                });

                actual.ShouldAllBeEquivalentTo(expected);
            }
        }
        public async Task FindIdByUniqueIndexedProperty_returns_aggregate_id_if_property_found()
        {
            var userId  = Guid.NewGuid();
            var created = new FakeUserCreated();

            created.Raise(userId);
            var sut = new SqlEventStore(
                () => new FakeEventStoreDbContext(_dbContextOptions),
                new JsonMessageSerializer());
            await sut.SaveEvents <FakeUser>(new[] { created });

            Guid?actual = await sut.FindIdByUniqueIndexedProperty <FakeUser>("Username", created.Username);

            actual.Should().Be(userId);
        }
Exemple #27
0
        public static FakeUser CreateNew(Guid userGuid, IEventStore eventStore, IEventBus eventBus, IDateTimeProvider dateTimeProvider, DateTime createdDate, string username)
        {
            // Instantiate the new FakeUser
            FakeUser fakeUser = new FakeUser(userGuid, eventStore, eventBus, dateTimeProvider);

            // Create the event
            FakeUserCreated @event = new FakeUserCreated(createdDate, userGuid, username);

            // Apply the event
            fakeUser.Apply(@event, true);

            // Save the events and publish them
            fakeUser.SaveAndPublishEvents();

            return(fakeUser);
        }
Exemple #28
0
        public async Task LoadEvents_restores_all_events_correctly(
            FakeUserCreated created,
            FakeUsernameChanged usernameChanged)
        {
            // Arrange
            var events = new DomainEvent[] { created, usernameChanged };

            RaiseEvents(userId, events);
            await sut.SaveEvents <FakeUser>(events);

            // Act
            IEnumerable <IDomainEvent> actual = await sut.LoadEvents <FakeUser>(userId);

            // Assert
            actual.ShouldAllBeEquivalentTo(events);
        }
Exemple #29
0
        public async Task SaveEvents_commits_once(
            FakeUserCreated created,
            FakeUsernameChanged usernameChanged)
        {
            var events = new DomainEvent[] { created, usernameChanged };

            RaiseEvents(userId, events);
            var sut = new SqlEventStore(
                () => mockDbContext,
                new JsonMessageSerializer());

            await sut.SaveEvents <FakeUser>(events);

            Mock.Get(mockDbContext).Verify(
                x => x.SaveChangesAsync(CancellationToken.None), Times.Once());
        }
        public async Task PublishPendingEvents_sends_events(
            FakeUserCreated created,
            FakeUsernameChanged usernameChanged)
        {
            // Arrange
            var sourceId = Guid.NewGuid();

            var events = new DomainEvent[] { created, usernameChanged };

            RaiseEvents(sourceId, events);

            var envelopes = new List <Envelope>();

            using (var db = new DataContext())
            {
                foreach (DomainEvent e in events)
                {
                    var envelope = new Envelope(e);
                    envelopes.Add(envelope);
                    db.PendingEvents.Add(PendingEvent.FromEnvelope(envelope, serializer));
                }
                await db.SaveChangesAsync();
            }

            List <Envelope> batch = null;

            Mock.Get(messageBus)
            .Setup(
                x =>
                x.SendBatch(
                    It.IsAny <IEnumerable <Envelope> >(),
                    It.IsAny <CancellationToken>()))
            .Callback <IEnumerable <Envelope>, CancellationToken>((b, t) => batch = b.ToList())
            .Returns(Task.FromResult(true));

            // Act
            await sut.PublishPendingEvents(sourceId, CancellationToken.None);

            // Assert
            Mock.Get(messageBus).Verify(
                x =>
                x.SendBatch(
                    It.IsAny <IEnumerable <Envelope> >(),
                    CancellationToken.None),
                Times.Once());
            batch.ShouldAllBeEquivalentTo(envelopes, opts => opts.RespectingRuntimeTypes());
        }