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_fails_if_version_of_first_event_not_follows_aggregate()
        {
            // Arrange
            var userId = Guid.NewGuid();

            using (var db = new FakeEventStoreDbContext(_dbContextOptions))
            {
                var aggregate = new Aggregate
                {
                    AggregateId   = userId,
                    AggregateType = typeof(FakeUser).FullName,
                    Version       = 1,
                };
                db.Aggregates.Add(aggregate);
                await db.SaveChangesAsync();
            }

            var usernameChanged = new FakeUsernameChanged();

            usernameChanged.Raise(userId, 2);

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

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

            // Assert
            action.ShouldThrow <ArgumentException>().Where(x => x.ParamName == "events");
        }
        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);
            }
        }
        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 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());
                }
            }
        }
        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);
            }
        }
Beispiel #8
0
        public static void ClassInitialize(TestContext context)
        {
            _dbContextOptions = new DbContextOptionsBuilder()
                                .UseSqlServer($@"Server=(localdb)\mssqllocaldb;Database={typeof(SqlEventPublisher_specs).FullName}.Core;Trusted_Connection=True;")
                                .Options;

            using (var db = new FakeEventStoreDbContext(_dbContextOptions))
            {
                db.Database.Migrate();
                db.Database.ExecuteSqlCommand("DELETE FROM Aggregates");
                db.Database.ExecuteSqlCommand("DELETE FROM PersistentEvents");
                db.Database.ExecuteSqlCommand("DELETE FROM PendingEvents");
                db.Database.ExecuteSqlCommand("DELETE FROM UniqueIndexedProperties");
            }
        }
Beispiel #9
0
        public async Task FlushPendingEvents_sends_events_correctly()
        {
            // Arrange
            var created         = new FakeUserCreated();
            var usernameChanged = new FakeUsernameChanged();
            var sourceId        = Guid.NewGuid();

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

            events.Raise(sourceId);

            var envelopes = new List <Envelope>();

            using (var db = new FakeEventStoreDbContext(_dbContextOptions))
            {
                var serializer = new JsonMessageSerializer();
                foreach (DomainEvent e in events)
                {
                    var envelope = new Envelope(
                        messageId: Guid.NewGuid(),
                        correlationId: Guid.NewGuid(),
                        contributor: Guid.NewGuid().ToString(),
                        message: e);
                    envelopes.Add(envelope);
                    db.PendingEvents.Add(PendingEvent.FromEnvelope <FakeUser>(envelope, serializer));
                }

                await db.SaveChangesAsync();
            }

            var messageBus = new MessageBus();

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

            // Act
            await sut.FlushPendingEvents <FakeUser>(sourceId, CancellationToken.None);

            // Assert
            messageBus.Sent.ShouldAllBeEquivalentTo(envelopes, opts => opts.RespectingRuntimeTypes());
        }
        public async Task SaveEvents_sets_message_properties_correctly()
        {
            // 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,
                correlationId : Guid.NewGuid(),
                contributor : Guid.NewGuid().ToString());

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

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

                actual.ShouldAllBeEquivalentTo(expected);
            }
        }
Beispiel #11
0
        public async Task FlushPendingEvents_deletes_pending_events()
        {
            // Arrange
            var sourceId = Guid.NewGuid();

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

            events.Raise(sourceId);

            using (var db = new FakeEventStoreDbContext(_dbContextOptions))
            {
                var serializer = new JsonMessageSerializer();
                foreach (DomainEvent e in events)
                {
                    var envelope = new Envelope(e);
                    db.PendingEvents.Add(PendingEvent.FromEnvelope <FakeUser>(envelope, serializer));
                }

                await db.SaveChangesAsync();
            }

            var sut = new SqlEventPublisher(
                () => new FakeEventStoreDbContext(_dbContextOptions),
                new JsonMessageSerializer(),
                Mock.Of <IMessageBus>());

            // Act
            await sut.FlushPendingEvents <FakeUser>(sourceId);

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

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

            using (var db = new FakeEventStoreDbContext(_dbContextOptions))
            {
                var aggregate = new Aggregate
                {
                    AggregateId   = userId,
                    AggregateType = typeof(FakeUser).FullName,
                    Version       = 1,
                };
                db.Aggregates.Add(aggregate);
                await db.SaveChangesAsync();
            }

            var usernameChanged = new FakeUsernameChanged();

            usernameChanged.Raise(userId, 1);

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

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

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

                actual.Version.Should().Be(usernameChanged.Version);
            }
        }
        public async Task SaveEvents_fails_if_unique_indexed_property_duplicate()
        {
            // Arrange
            var sut = new SqlEventStore(
                () => new FakeEventStoreDbContext(_dbContextOptions),
                new JsonMessageSerializer());

            string duplicateUserName = Guid.NewGuid().ToString();

            var macId      = Guid.NewGuid();
            var macCreated = new FakeUserCreated {
                Username = duplicateUserName
            };

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

            // Act
            var toshId      = Guid.NewGuid();
            var toshCreated = new FakeUserCreated {
                Username = duplicateUserName
            };

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

            // Assert
            action.ShouldThrow <Exception>();
            using (var db = new FakeEventStoreDbContext(_dbContextOptions))
            {
                IQueryable <PersistentEvent> query =
                    from e in db.PersistentEvents
                    where e.AggregateId == toshId
                    select e;
                (await query.AnyAsync()).Should().BeFalse();
            }
        }