public void SaveEvents_fails_if_events_contains_null()
        {
            // Arrange
            var userId  = Guid.NewGuid();
            var created = new FakeUserCreated();

            created.Raise(userId);
            var events = new DomainEvent[] { created, null };

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

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

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

            events.Raise(userId);
            var context = new DbContextSpy(_dbContextOptions);
            var sut     = new SqlEventStore(
                () => context,
                new JsonMessageSerializer());

            await sut.SaveEvents <FakeUser>(events);

            context.CommitCount.Should().Be(1);
        }
        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);
            }
        }
Exemple #4
0
        public async Task FlushAllPendingEvents_sends_app_pending_events()
        {
            // Arrange
            IEnumerable <FakeUserCreated> createdEvents = _fixture
                                                          .Build <FakeUserCreated>()
                                                          .With(e => e.Version, 1)
                                                          .With(e => e.RaisedAt, DateTime.UtcNow)
                                                          .CreateMany();
            var eventStore = new SqlEventStore(() => new DataContext(), _serializer);

            foreach (FakeUserCreated createdEvent in createdEvents)
            {
                await eventStore.SaveEvents <FakeUser>(new[] { createdEvent });
            }

            var sentEvents = new List <Envelope>();

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

            // Act
            await _sut.FlushAllPendingEvents(CancellationToken.None);

            // Assert
            foreach (FakeUserCreated createdEvent in createdEvents)
            {
                sentEvents
                .Select(e => e.Message)
                .OfType <FakeUserCreated>()
                .Where(e => e.SourceId == createdEvent.SourceId)
                .Should()
                .ContainSingle()
                .Which
                .ShouldBeEquivalentTo(createdEvent);
            }
        }
Exemple #5
0
        public async Task FlushAllPendingEvents_sends_all_pending_events()
        {
            // Arrange
            IEnumerable <FakeUserCreated> createdEvents = new[]
            {
                new FakeUserCreated(),
                new FakeUserCreated(),
                new FakeUserCreated(),
            };

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

            foreach (FakeUserCreated createdEvent in createdEvents)
            {
                createdEvent.Raise(Guid.NewGuid());
                await eventStore.SaveEvents <FakeUser>(
                    new[] { createdEvent },
                    correlationId : default,
        public void SaveEvents_fails_if_db_context_does_not_support_transaction()
        {
            // Arrange
            var created = new FakeUserCreated {
                Version = 1
            };

            string           databaseName = nameof(SaveEvents_fails_if_db_context_does_not_support_transaction);
            DbContextOptions options      = new DbContextOptionsBuilder()
                                            .UseInMemoryDatabase(databaseName)
                                            .Options;
            var sut = new SqlEventStore(
                () => new FakeEventStoreDbContext(options),
                new JsonMessageSerializer());

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

            // Assert
            action.ShouldThrow <InvalidOperationException>();
        }
        public void SaveEvents_succeeds_if_db_context_supports_transaction()
        {
            // Arrange
            var created = new FakeUserCreated {
                Version = 1
            };

            string           databaseName = nameof(SaveEvents_succeeds_if_db_context_supports_transaction);
            DbContextOptions options      = new DbContextOptionsBuilder()
                                            .UseInMemoryDatabase(databaseName)
                                            .ConfigureWarnings(builder => builder.Ignore(InMemoryEventId.TransactionIgnoredWarning))
                                            .Options;
            var sut = new SqlEventStore(
                () => new FakeEventStoreDbContext(_dbContextOptions),
                new JsonMessageSerializer());

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

            // Assert
            action.ShouldNotThrow();
        }
        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);
            }
        }
Exemple #9
0
        public SqlEventStore_features(ITestOutputHelper output)
        {
            this.output = output;

            fixture = new Fixture().Customize(new AutoMoqCustomization());
            fixture.Inject <Func <EventStoreDbContext> >(() => new DataContext());

            userId = Guid.NewGuid();

            serializer = new JsonMessageSerializer();
            fixture.Inject(serializer);

            sut = fixture.Create <SqlEventStore>();

            mockDbContext = Mock.Of <EventStoreDbContext>(
                x => x.SaveChangesAsync() == Task.FromResult(default(int)));

            mockDbContext.Aggregates = Mock.Of <DbSet <Aggregate> >();
            Mock.Get(mockDbContext.Aggregates).SetupData();

            mockDbContext.PersistentEvents = Mock.Of <DbSet <PersistentEvent> >();
            Mock.Get(mockDbContext.PersistentEvents).SetupData();

            mockDbContext.PendingEvents = Mock.Of <DbSet <PendingEvent> >();
            Mock.Get(mockDbContext.PendingEvents).SetupData();

            mockDbContext.UniqueIndexedProperties = Mock.Of <DbSet <UniqueIndexedProperty> >();
            Mock.Get(mockDbContext.UniqueIndexedProperties).SetupData();

            using (var db = new DataContext())
            {
                db.Database.Log = output.WriteLine;
                db.Database.ExecuteSqlCommand("DELETE FROM Aggregates");
                db.Database.ExecuteSqlCommand("DELETE FROM PersistentEvents");
                db.Database.ExecuteSqlCommand("DELETE FROM PendingEvents");
                db.Database.ExecuteSqlCommand("DELETE FROM UniqueIndexedProperties");
            }
        }
        public async Task SaveEvents_removes_existing_UniqueIndexedProperty_if_property_value_is_null()
        {
            // Arrange
            var userId = Guid.NewGuid();

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

            var created = new FakeUserCreated();

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

            var usernameChanged = new FakeUsernameChanged {
                Username = null
            };

            usernameChanged.Raise(userId, 1);

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

            // 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 LoadEvents_restores_events_after_specified_version_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());

            await sut.SaveEvents <FakeUser>(events);

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

            // Assert
            actual.ShouldAllBeEquivalentTo(events.Skip(1));
        }
        public void SaveEvents_fails_if_kind_of_event_raised_time_is_not_utc(DateTimeKind dateTimeKind)
        {
            // Arrange
            var userId = Guid.NewGuid();
            var events = new DomainEvent[]
            {
                new FakeUserCreated(),
                new FakeUsernameChanged(),
            };

            events.Raise(userId);
            events.OrderBy(e => e.GetHashCode()).First().RaisedAt = new DateTime(DateTime.Now.Ticks, dateTimeKind);

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

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

            // Assert
            action.ShouldThrow <ArgumentException>().Where(x => x.ParamName == "events");
        }
        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();
            }
        }
        public void TestInitialize()
        {
            _fixture = new Fixture().Customize(new AutoMoqCustomization());
            _fixture.Inject <Func <EventStoreDbContext> >(() => new DataContext());

            _userId = Guid.NewGuid();

            _serializer = new JsonMessageSerializer();
            _fixture.Inject(_serializer);

            _sut = _fixture.Create <SqlEventStore>();

            _mockDbContext = Mock.Of <EventStoreDbContext>(
                x => x.SaveChangesAsync() == Task.FromResult(default(int)));

            _mockDbContext.Aggregates = Mock.Of <DbSet <Aggregate> >();
            Mock.Get(_mockDbContext.Aggregates).SetupData();

            _mockDbContext.PersistentEvents = Mock.Of <DbSet <PersistentEvent> >();
            Mock.Get(_mockDbContext.PersistentEvents).SetupData();

            _mockDbContext.PendingEvents = Mock.Of <DbSet <PendingEvent> >();
            Mock.Get(_mockDbContext.PendingEvents).SetupData();

            _mockDbContext.UniqueIndexedProperties = Mock.Of <DbSet <UniqueIndexedProperty> >();
            Mock.Get(_mockDbContext.UniqueIndexedProperties).SetupData();

            using (var db = new DataContext())
            {
                db.Database.Log = m => TestContext?.WriteLine(m);
                db.Database.ExecuteSqlCommand("DELETE FROM Aggregates");
                db.Database.ExecuteSqlCommand("DELETE FROM PersistentEvents");
                db.Database.ExecuteSqlCommand("DELETE FROM PendingEvents");
                db.Database.ExecuteSqlCommand("DELETE FROM UniqueIndexedProperties");
            }
        }