public async Task PublishPendingEvents_does_not_fails_even_if_all_events_persisted() { // Arrange var userId = Guid.NewGuid(); var userCreated = fixture.Create <FakeUserCreated>(); var usernameChanged = fixture.Create <FakeUsernameChanged>(); var domainEvents = new DomainEvent[] { userCreated, usernameChanged }; RaiseEvents(userId, domainEvents); var envelopes = new List <Envelope>(domainEvents.Select(e => new Envelope(e))); var batchOperation = new TableBatchOperation(); envelopes .Select(e => PendingEventTableEntity.FromEnvelope <FakeUser>(e, serializer)) .ForEach(batchOperation.Insert); await s_eventTable.ExecuteBatchAsync(batchOperation); batchOperation.Clear(); envelopes .Select(e => EventTableEntity.FromEnvelope <FakeUser>(e, serializer)) .ForEach(batchOperation.Insert); await s_eventTable.ExecuteBatchAsync(batchOperation); // Act Func <Task> action = () => sut.PublishPendingEvents <FakeUser>(userId, CancellationToken.None); // Assert action.ShouldNotThrow(); }
private async Task <IEnumerable <IDomainEvent> > Load <T>( Guid sourceId, int afterVersion, CancellationToken cancellationToken) where T : class, IEventSourced { string filter = CombineFilters( GenerateFilterCondition( nameof(ITableEntity.PartitionKey), Equal, EventTableEntity.GetPartitionKey(typeof(T), sourceId)), And, GenerateFilterCondition( nameof(ITableEntity.RowKey), GreaterThan, EventTableEntity.GetRowKey(afterVersion))); var query = new TableQuery <EventTableEntity>().Where(filter); IEnumerable <EventTableEntity> events = await _eventTable .ExecuteQuery(query, cancellationToken) .ConfigureAwait(false); return(new List <IDomainEvent>(events .Select(e => e.EventJson) .Select(_serializer.Deserialize) .Cast <IDomainEvent>())); }
public void GetPartitionKey_returns_the_same_as_EventTableEntity() { var sourceType = typeof(FakeUser); var sourceId = Guid.NewGuid(); string actual = CorrelationTableEntity.GetPartitionKey(sourceType, sourceId); actual.Should().Be(EventTableEntity.GetPartitionKey(sourceType, sourceId)); }
public void FromEnvelope_sets_EventType_correctly() { var domainEvent = fixture.Create <FakeUserCreated>(); var envelope = new Envelope(domainEvent); EventTableEntity entity = FromEnvelope <FakeUser>(envelope, serializer); entity.EventType.Should().Be(typeof(FakeUserCreated).FullName); }
public void FromEnvelope_sets_Version_correctly() { var domainEvent = fixture.Create <FakeUserCreated>(); var envelope = new Envelope(domainEvent); EventTableEntity entity = FromEnvelope <FakeUser>(envelope, serializer); entity.Version.Should().Be(domainEvent.Version); }
public void FromEnvelope_sets_PartitionKey_correctly() { var domainEvent = fixture.Create <FakeUserCreated>(); var envelope = new Envelope(domainEvent); EventTableEntity entity = FromEnvelope <FakeUser>(envelope, serializer); entity.PartitionKey.Should().Be( GetPartitionKey(typeof(FakeUser), domainEvent.SourceId)); }
public void FromEnvelope_sets_CorrelationId_correctly() { var domainEvent = fixture.Create <FakeUserCreated>(); var correlationId = Guid.NewGuid(); var envelope = new Envelope(correlationId, domainEvent); EventTableEntity entity = FromEnvelope <FakeUser>(envelope, serializer); entity.CorrelationId.Should().Be(correlationId); }
public async Task PublishAllEvents_sends_pending_events() { // Arrange var domainEvents = new List <DomainEvent>(); List <Guid> users = fixture.CreateMany <Guid>().ToList(); foreach (Guid userId in users) { var userCreated = fixture.Create <FakeUserCreated>(); var usernameChanged = fixture.Create <FakeUsernameChanged>(); var events = new DomainEvent[] { userCreated, usernameChanged }; RaiseEvents(userId, events); var envelopes = new List <Envelope>(events.Select(e => new Envelope(e))); var batchOperation = new TableBatchOperation(); envelopes .Select(e => PendingEventTableEntity.FromEnvelope <FakeUser>(e, serializer)) .ForEach(batchOperation.Insert); await s_eventTable.ExecuteBatchAsync(batchOperation); batchOperation.Clear(); envelopes .Select(e => EventTableEntity.FromEnvelope <FakeUser>(e, serializer)) .ForEach(batchOperation.Insert); await s_eventTable.ExecuteBatchAsync(batchOperation); domainEvents.AddRange(events); } var messages = new List <IDomainEvent>(); Mock.Get(messageBus) .Setup( x => x.SendBatch( It.IsAny <IEnumerable <Envelope> >(), It.IsAny <CancellationToken>())) .Callback <IEnumerable <Envelope>, CancellationToken>( (batch, cancellationToken) => messages.AddRange(batch .Select(b => b.Message) .OfType <IDomainEvent>() .Where(m => users.Contains(m.SourceId)))) .Returns(Task.FromResult(true)); // Act await sut.PublishAllEvents(CancellationToken.None); // Assert messages.Should().OnlyContain(e => e is IDomainEvent); messages.ShouldAllBeEquivalentTo(domainEvents); }
public void FromEnvelope_sets_PersistentPartition_correctly() { var domainEvent = fixture.Create <FakeUserCreated>(); var envelope = new Envelope(domainEvent); PendingEventTableEntity actual = FromEnvelope <FakeUser>(envelope, serializer); actual.PersistentPartition.Should().Be( EventTableEntity.GetPartitionKey( typeof(FakeUser), domainEvent.SourceId)); }
public void FromEnvelope_sets_EventJson_correctly() { var domainEvent = fixture.Create <FakeUserCreated>(); var envelope = new Envelope(domainEvent); EventTableEntity entity = FromEnvelope <FakeUser>(envelope, serializer); object actual = serializer.Deserialize(entity.EventJson); actual.Should().BeOfType <FakeUserCreated>(); actual.ShouldBeEquivalentTo(domainEvent); }
public async Task PublishPendingEvents_does_not_delete_pending_events_if_fails_to_send() { // Arrange var userId = Guid.NewGuid(); var userCreated = fixture.Create <FakeUserCreated>(); var usernameChanged = fixture.Create <FakeUsernameChanged>(); var domainEvents = new DomainEvent[] { userCreated, usernameChanged }; RaiseEvents(userId, domainEvents); var envelopes = new List <Envelope>(domainEvents.Select(e => new Envelope(e))); var batchOperation = new TableBatchOperation(); var pendingEvents = new List <PendingEventTableEntity>( envelopes.Select(e => PendingEventTableEntity.FromEnvelope <FakeUser>(e, serializer))); pendingEvents.ForEach(batchOperation.Insert); await s_eventTable.ExecuteBatchAsync(batchOperation); batchOperation.Clear(); envelopes .Take(1) .Select(e => EventTableEntity.FromEnvelope <FakeUser>(e, serializer)) .ForEach(batchOperation.Insert); await s_eventTable.ExecuteBatchAsync(batchOperation); Mock.Get(messageBus) .Setup( x => x.SendBatch( It.IsAny <IEnumerable <Envelope> >(), It.IsAny <CancellationToken>())) .Throws(new InvalidOperationException()); // Act try { await sut.PublishPendingEvents <FakeUser>(userId, CancellationToken.None); } catch (InvalidOperationException) { } // Assert string partitionKey = PendingEventTableEntity.GetPartitionKey(typeof(FakeUser), userId); var query = new TableQuery <PendingEventTableEntity>().Where($"PartitionKey eq '{partitionKey}'"); IEnumerable <object> actual = s_eventTable.ExecuteQuery(query).Select(e => e.RowKey); actual.ShouldAllBeEquivalentTo(pendingEvents.Select(e => e.RowKey)); }
public async Task PublishPendingEventss_sends_only_persisted_pending_events() { // Arrange var userId = Guid.NewGuid(); var userCreated = fixture.Create <FakeUserCreated>(); var usernameChanged = fixture.Create <FakeUsernameChanged>(); var domainEvents = new DomainEvent[] { userCreated, usernameChanged }; RaiseEvents(userId, domainEvents); var envelopes = new List <Envelope>(domainEvents.Select(e => new Envelope(e))); var batchOperation = new TableBatchOperation(); envelopes .Select(e => PendingEventTableEntity.FromEnvelope <FakeUser>(e, serializer)) .ForEach(batchOperation.Insert); await s_eventTable.ExecuteBatchAsync(batchOperation); batchOperation.Clear(); envelopes .Take(1) .Select(e => EventTableEntity.FromEnvelope <FakeUser>(e, serializer)) .ForEach(batchOperation.Insert); await s_eventTable.ExecuteBatchAsync(batchOperation); 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 <FakeUser>(userId, CancellationToken.None); // Assert Mock.Get(messageBus).Verify( x => x.SendBatch( It.IsAny <IEnumerable <Envelope> >(), CancellationToken.None), Times.Once()); batch.ShouldAllBeEquivalentTo(envelopes.Take(1), opts => opts.RespectingRuntimeTypes()); }
private async Task InsertEventsAndCorrelation <T>( List <Envelope> envelopes, Guid?correlationId, CancellationToken cancellationToken) where T : class, IEventSourced { var batch = new TableBatchOperation(); var firstEvent = (IDomainEvent)envelopes.First().Message; Guid sourceId = firstEvent.SourceId; foreach (Envelope envelope in envelopes) { batch.Insert(EventTableEntity.FromEnvelope <T>(envelope, _serializer)); } if (correlationId.HasValue) { batch.Insert(CorrelationTableEntity.Create(typeof(T), sourceId, correlationId.Value)); } try { await _eventTable.ExecuteBatchAsync(batch, cancellationToken).ConfigureAwait(false); } catch (StorageException exception) when(correlationId.HasValue) { string filter = CorrelationTableEntity.GetFilter(typeof(T), sourceId, correlationId.Value); var query = new TableQuery <CorrelationTableEntity>().Where(filter); if (await _eventTable.Any(query, cancellationToken)) { throw new DuplicateCorrelationException( typeof(T), sourceId, correlationId.Value, exception); } throw; } }
public async Task SaveEvents_inserts_event_entities_correctly() { // Arrange var created = fixture.Create <FakeUserCreated>(); var usernameChanged = fixture.Create <FakeUsernameChanged>(); var events = new DomainEvent[] { created, usernameChanged }; var correlationId = Guid.NewGuid(); RaiseEvents(userId, events); // Act await sut.SaveEvents <FakeUser>(events, correlationId); // Assert string partitionKey = EventTableEntity.GetPartitionKey(typeof(FakeUser), userId); var query = new TableQuery <EventTableEntity>().Where($"PartitionKey eq '{partitionKey}'"); IEnumerable <EventTableEntity> persistentEvents = s_eventTable.ExecuteQuery(query); foreach (var t in persistentEvents.Zip(events, (persistent, source) => new { Persistent = persistent, Source = source })) { var actual = new { t.Persistent.RowKey, t.Persistent.Version, t.Persistent.EventType, t.Persistent.CorrelationId, Event = (DomainEvent)serializer.Deserialize(t.Persistent.EventJson), t.Persistent.RaisedAt }; actual.ShouldBeEquivalentTo(new { RowKey = EventTableEntity.GetRowKey(t.Source.Version), t.Source.Version, EventType = t.Source.GetType().FullName, CorrelationId = correlationId, Event = t.Source, t.Source.RaisedAt }); } }
public async Task SaveEvents_inserts_pending_event_entities_correctly() { // Arrange var created = fixture.Create <FakeUserCreated>(); var usernameChanged = fixture.Create <FakeUsernameChanged>(); var events = new DomainEvent[] { created, usernameChanged }; var correlationId = Guid.NewGuid(); RaiseEvents(userId, events); // Act await sut.SaveEvents <FakeUser>(events, correlationId); // Assert string partitionKey = PendingEventTableEntity.GetPartitionKey(typeof(FakeUser), userId); var query = new TableQuery <PendingEventTableEntity>().Where($"PartitionKey eq '{partitionKey}'"); IEnumerable <PendingEventTableEntity> pendingEvents = s_eventTable.ExecuteQuery(query); foreach (var t in pendingEvents.Zip(events, (pending, source) => new { Pending = pending, Source = source })) { var actual = new { t.Pending.RowKey, t.Pending.PersistentPartition, t.Pending.Version, t.Pending.CorrelationId, Message = serializer.Deserialize(t.Pending.EventJson) }; actual.ShouldBeEquivalentTo(new { RowKey = PendingEventTableEntity.GetRowKey(t.Source.Version), PersistentPartition = EventTableEntity.GetPartitionKey(typeof(FakeUser), userId), t.Source.Version, CorrelationId = correlationId, Message = t.Source }, opts => opts.RespectingRuntimeTypes()); } }
private async Task <List <EventTableEntity> > GetPersistentEvents( string persistentPartition, int version, CancellationToken cancellationToken) { var query = new TableQuery <EventTableEntity>(); string filter = CombineFilters( GenerateFilterCondition( nameof(ITableEntity.PartitionKey), Equal, persistentPartition), And, GenerateFilterCondition( nameof(ITableEntity.RowKey), GreaterThanOrEqual, EventTableEntity.GetRowKey(version))); return(new List <EventTableEntity>(await _eventTable .ExecuteQuery(query.Where(filter), cancellationToken) .ConfigureAwait(false))); }
public static PendingEventTableEntity FromEnvelope <T>( Envelope envelope, IMessageSerializer serializer) where T : class, IEventSourced { if (envelope == null) { throw new ArgumentNullException(nameof(envelope)); } if (serializer == null) { throw new ArgumentNullException(nameof(serializer)); } var domainEvent = envelope.Message as IDomainEvent; if (domainEvent == null) { throw new ArgumentException( $"{nameof(envelope)}.{nameof(envelope.Message)} must be an {nameof(IDomainEvent)}.", nameof(envelope)); } string persistentPartition = EventTableEntity.GetPartitionKey( typeof(T), domainEvent.SourceId); return(new PendingEventTableEntity { PartitionKey = GetPartitionKey(typeof(T), domainEvent.SourceId), RowKey = GetRowKey(domainEvent.Version), PersistentPartition = persistentPartition, Version = domainEvent.Version, MessageId = envelope.MessageId, CorrelationId = envelope.CorrelationId, EventJson = serializer.Serialize(domainEvent) }); }
public async Task PublishPendingEvents_deletes_all_pending_events() { // Arrange var userId = Guid.NewGuid(); var userCreated = fixture.Create <FakeUserCreated>(); var usernameChanged = fixture.Create <FakeUsernameChanged>(); var domainEvents = new DomainEvent[] { userCreated, usernameChanged }; RaiseEvents(userId, domainEvents); var envelopes = new List <Envelope>(domainEvents.Select(e => new Envelope(e))); var batchOperation = new TableBatchOperation(); envelopes .Select(e => PendingEventTableEntity.FromEnvelope <FakeUser>(e, serializer)) .ForEach(batchOperation.Insert); await s_eventTable.ExecuteBatchAsync(batchOperation); batchOperation.Clear(); envelopes .Take(1) .Select(e => EventTableEntity.FromEnvelope <FakeUser>(e, serializer)) .ForEach(batchOperation.Insert); await s_eventTable.ExecuteBatchAsync(batchOperation); // Act await sut.PublishPendingEvents <FakeUser>(userId, CancellationToken.None); // Assert string partitionKey = PendingEventTableEntity.GetPartitionKey(typeof(FakeUser), userId); var query = new TableQuery <PendingEventTableEntity>().Where($"PartitionKey eq '{partitionKey}'"); List <PendingEventTableEntity> actual = s_eventTable.ExecuteQuery(query).ToList(); actual.Should().BeEmpty(); }
public static string GetPartitionKey(Type sourceType, Guid sourceId) => EventTableEntity.GetPartitionKey(sourceType, sourceId);