public void given_command_publisher_fails_SaveProcessManagerAndPublishCommands_invokes_exception_handler() { // Arrange var processManager = new FooProcessManager(); CancellationToken cancellationToken = CancellationToken.None; Exception exception = new InvalidOperationException(); ICommandPublisher commandPublisher = Mock.Of <ICommandPublisher>(); Mock.Get(commandPublisher) .Setup(x => x.FlushCommands(processManager.Id, cancellationToken)) .ThrowsAsync(exception); ICommandPublisherExceptionHandler commandPublisherExceptionHandler = Mock.Of <ICommandPublisherExceptionHandler>(); var sut = new SqlProcessManagerDataContext <FooProcessManager>( new FooProcessManagerDbContext(), new JsonMessageSerializer(), commandPublisher, commandPublisherExceptionHandler); // Act Func <Task> action = () => sut.SaveProcessManagerAndPublishCommands(processManager, cancellationToken: cancellationToken); // Assert action.ShouldThrow <InvalidOperationException>().Which.Should().BeSameAs(exception); Mock.Get(commandPublisherExceptionHandler).Verify( x => x.Handle(It.Is <CommandPublisherExceptionContext>( p => p.ProcessManagerType == typeof(FooProcessManager) && p.ProcessManagerId == processManager.Id && p.Exception == exception)), Times.Once()); }
public void SaveProcessManagerAndPublishCommands_absorbs_command_publisher_exception_handler_exception() { // Arrange var processManager = new FooProcessManager(); CancellationToken cancellationToken = CancellationToken.None; ICommandPublisher commandPublisher = Mock.Of <ICommandPublisher>(); Mock.Get(commandPublisher) .Setup(x => x.FlushCommands(processManager.Id, cancellationToken)) .ThrowsAsync(new InvalidOperationException()); var sut = new SqlProcessManagerDataContext <FooProcessManager>( new FooProcessManagerDbContext(), new JsonMessageSerializer(), commandPublisher, new DelegatingCommandPublisherExceptionHandler( context => { context.Handled = true; throw new InvalidOperationException(); })); // Act Func <Task> action = () => sut.SaveProcessManagerAndPublishCommands(processManager, cancellationToken: cancellationToken); // Assert action.ShouldNotThrow(); }
public void given_command_publisher_exception_handled_SaveProcessManagerAndPublishCommands_does_not_throw() { // Arrange var processManager = new FooProcessManager(); CancellationToken cancellationToken = CancellationToken.None; Exception exception = new InvalidOperationException(); ICommandPublisher commandPublisher = Mock.Of <ICommandPublisher>(); Mock.Get(commandPublisher) .Setup(x => x.FlushCommands(processManager.Id, cancellationToken)) .ThrowsAsync(exception); var sut = new SqlProcessManagerDataContext <FooProcessManager>( new FooProcessManagerDbContext(), new JsonMessageSerializer(), commandPublisher, new DelegatingCommandPublisherExceptionHandler( context => context.Handled = context.ProcessManagerType == typeof(FooProcessManager) && context.ProcessManagerId == processManager.Id && context.Exception == exception)); // Act Func <Task> action = () => sut.SaveProcessManagerAndPublishCommands(processManager, cancellationToken: cancellationToken); // Assert action.ShouldNotThrow(); }
public async Task SaveProcessManagerAndPublishCommands_inserts_new_process_manager() { // Arrange var processManager = new FooProcessManager { AggregateId = Guid.NewGuid() }; var sut = new SqlProcessManagerDataContext <FooProcessManager>( new FooProcessManagerDbContext(), new JsonMessageSerializer(), Mock.Of <ICommandPublisher>()); using (sut) { // Act await sut.SaveProcessManagerAndPublishCommands(processManager); } // Assert using (var db = new FooProcessManagerDbContext()) { FooProcessManager actual = await db.FooProcessManagers.SingleOrDefaultAsync(x => x.Id == processManager.Id); actual.Should().NotBeNull(); actual.AggregateId.Should().Be(processManager.AggregateId); } }
public void FromScheduledEnvelope_sets_ProcessManagerId_correctly() { var processManager = new FooProcessManager(); var actual = PendingScheduledCommand.FromScheduledEnvelope( processManager, new Fixture().Create <ScheduledEnvelope>(), new JsonMessageSerializer()); actual.ProcessManagerId.Should().Be(processManager.Id); }
public void FromEnvelope_sets_CorrelationId_correctly() { var fixture = new Fixture(); var envelope = new Envelope(Guid.NewGuid(), fixture.Create <FooCommand>(), correlationId: Guid.NewGuid()); var processManager = new FooProcessManager(); var serializer = new JsonMessageSerializer(); var actual = PendingCommand.FromEnvelope(processManager, envelope, serializer); actual.CorrelationId.Should().Be(envelope.CorrelationId); }
public void FromEnvelope_sets_ProcessManagerId_correctly() { var fixture = new Fixture(); var envelope = new Envelope(fixture.Create <FooCommand>()); var processManager = new FooProcessManager(); var serializer = new JsonMessageSerializer(); var actual = PendingCommand.FromEnvelope(processManager, envelope, serializer); TestContext.WriteLine($"{actual.ProcessManagerId}"); actual.ProcessManagerId.Should().Be(processManager.Id); }
public void FromEnvelope_sets_CommandJson_correctly() { var fixture = new Fixture(); FooCommand command = fixture.Create <FooCommand>(); var envelope = new Envelope(command); var processManager = new FooProcessManager(); var serializer = new JsonMessageSerializer(); var actual = PendingCommand.FromEnvelope(processManager, envelope, serializer); serializer.Deserialize(actual.CommandJson) .Should().BeOfType <FooCommand>() .Subject.ShouldBeEquivalentTo(command); }
public async Task given_scheduled_message_bus_fails_FlushCommands_deletes_no_scheduled_command() { // Arrange var serializer = new JsonMessageSerializer(); var processManager = new FooProcessManager(); var fixture = new Fixture(); var scheduledCommands = new List <PendingScheduledCommand>( from command in fixture.CreateMany <FooCommand>() let envelope = new Envelope(command) let scheduledEnvelope = new ScheduledEnvelope(envelope, fixture.Create <DateTimeOffset>()) select PendingScheduledCommand.FromScheduledEnvelope(processManager, scheduledEnvelope, serializer)); using (var db = new FooProcessManagerDbContext()) { db.PendingScheduledCommands.AddRange(scheduledCommands); await db.SaveChangesAsync(); } Guid poisonedMessageId = (from c in scheduledCommands orderby c.GetHashCode() select c.MessageId).First(); IScheduledMessageBus scheduledMessageBus = Mock.Of <IScheduledMessageBus>(); var exception = new InvalidOperationException(); Mock.Get(scheduledMessageBus) .Setup(x => x.Send(It.Is <ScheduledEnvelope>(p => p.Envelope.MessageId == poisonedMessageId), CancellationToken.None)) .ThrowsAsync(exception); var sut = new SqlCommandPublisher( () => new FooProcessManagerDbContext(), serializer, Mock.Of <IMessageBus>(), scheduledMessageBus); // Act Func <Task> action = () => sut.FlushCommands(processManager.Id, CancellationToken.None); // Assert action.ShouldThrow <InvalidOperationException>().Which.Should().BeSameAs(exception); using (var db = new FooProcessManagerDbContext()) { IQueryable <PendingScheduledCommand> query = from c in db.PendingScheduledCommands where c.ProcessManagerId == processManager.Id select c; List <PendingScheduledCommand> actual = await query.ToListAsync(); actual.ShouldAllBeEquivalentTo(scheduledCommands, opts => opts.RespectingRuntimeTypes()); } }
public async Task SaveProcessManagerAndPublishCommands_commits_once() { // Arrange var context = new FooProcessManagerDbContext(); var sut = new SqlProcessManagerDataContext <FooProcessManager>( context, new JsonMessageSerializer(), Mock.Of <ICommandPublisher>()); var processManager = new FooProcessManager(); // Act await sut.SaveProcessManagerAndPublishCommands(processManager); // Assert context.CommitCount.Should().Be(1); }
public async Task FlushCommands_absorbs_exception_caused_by_that_some_pending_scheduled_command_already_deleted_since_loaded() { // Arrange var scheduledMessageBus = new CompletableScheduledMessageBus(); var serializer = new JsonMessageSerializer(); var sut = new SqlCommandPublisher( () => new FooProcessManagerDbContext(), serializer, Mock.Of <IMessageBus>(), scheduledMessageBus); var processManager = new FooProcessManager(); using (var db = new FooProcessManagerDbContext()) { db.PendingScheduledCommands.AddRange( from command in new Fixture().CreateMany <FooCommand>() let envelope = new Envelope(command) let scheduledEnvelope = new ScheduledEnvelope(envelope, DateTimeOffset.Now) select PendingScheduledCommand.FromScheduledEnvelope(processManager, scheduledEnvelope, serializer)); await db.SaveChangesAsync(); } // Act Func <Task> action = async() => { Task flushTask = sut.FlushCommands(processManager.Id, CancellationToken.None); using (var db = new FooProcessManagerDbContext()) { List <PendingScheduledCommand> pendingScheduledCommands = await db .PendingScheduledCommands .Where(c => c.ProcessManagerId == processManager.Id) .OrderByDescending(c => c.Id) .Take(1) .ToListAsync(); db.PendingScheduledCommands.RemoveRange(pendingScheduledCommands); await db.SaveChangesAsync(); } scheduledMessageBus.Complete(); await flushTask; }; // Assert action.ShouldNotThrow(); }
public async Task FlushCommands_deletes_all_commands_associated_with_specified_process_manager() { // Arrange var serializer = new JsonMessageSerializer(); var processManager = new FooProcessManager(); var noiseProcessManager = new FooProcessManager(); const int noiseCommandCount = 3; using (var db = new FooProcessManagerDbContext()) { var commands = new List <PendingCommand>( from command in Enumerable.Repeat(new FooCommand(), 3) let envelope = new Envelope(command) select PendingCommand.FromEnvelope(processManager, envelope, serializer)); commands.AddRange( from command in Enumerable.Repeat(new FooCommand(), noiseCommandCount) let envelope = new Envelope(command) select PendingCommand.FromEnvelope(noiseProcessManager, envelope, serializer)); var random = new Random(); db.PendingCommands.AddRange( from command in commands orderby random.Next() select command); await db.SaveChangesAsync(); } var sut = new SqlCommandPublisher( () => new FooProcessManagerDbContext(), serializer, Mock.Of <IMessageBus>(), Mock.Of <IScheduledMessageBus>()); // Act await sut.FlushCommands(processManager.Id, CancellationToken.None); // Assert using (var db = new FooProcessManagerDbContext()) { (await db.PendingCommands.AnyAsync(c => c.ProcessManagerId == processManager.Id)).Should().BeFalse(); (await db.PendingCommands.CountAsync(c => c.ProcessManagerId == noiseProcessManager.Id)).Should().Be(noiseCommandCount); } }
public async Task SaveProcessManagerAndPublishCommands_inserts_pending_scheduled_commands_sequentially() { // Arrange var fixture = new Fixture(); IEnumerable <(FooCommand command, DateTimeOffset scheduledTime)> scheduledCommands = fixture.CreateMany <(FooCommand, DateTimeOffset)>(); var processManager = new FooProcessManager( from e in scheduledCommands select new ScheduledCommand(e.command, e.scheduledTime)); var operationId = Guid.NewGuid(); var correlationId = Guid.NewGuid(); string contributor = fixture.Create <string>(); var serializer = new JsonMessageSerializer(); // Act using (var sut = new SqlProcessManagerDataContext <FooProcessManager>( new FooProcessManagerDbContext(), serializer, Mock.Of <ICommandPublisher>())) { await sut.SaveProcessManagerAndPublishCommands(processManager, operationId, correlationId, contributor); } // Assert using (var db = new FooProcessManagerDbContext()) { IQueryable <PendingScheduledCommand> query = from c in db.PendingScheduledCommands where c.ProcessManagerId == processManager.Id orderby c.Id select c; var pendingScheduledCommands = query.ToList(); pendingScheduledCommands.Should().HaveCount(scheduledCommands.Count()); foreach (((FooCommand command, DateTimeOffset scheduledTime)expected, PendingScheduledCommand actual)t in scheduledCommands.Zip(pendingScheduledCommands, (expected, actual) => (expected, actual))) { t.actual.ProcessManagerType.Should().Be(typeof(FooProcessManager).FullName); t.actual.ProcessManagerId.Should().Be(processManager.Id); t.actual.MessageId.Should().NotBeEmpty(); t.actual.OperationId.Should().Be(operationId); t.actual.CorrelationId.Should().Be(correlationId); t.actual.Contributor.Should().Be(contributor); serializer.Deserialize(t.actual.CommandJson).ShouldBeEquivalentTo(t.expected.command, opts => opts.RespectingRuntimeTypes()); t.actual.ScheduledTime.Should().Be(t.expected.scheduledTime); } } }
public async Task SaveProcessManagerAndPublishCommands_publishes_commands() { // Arrange var fixture = new Fixture(); FooProcessManager processManager = fixture.Create <FooProcessManager>(); ICommandPublisher publisher = Mock.Of <ICommandPublisher>(); var sut = new SqlProcessManagerDataContext <FooProcessManager>( new FooProcessManagerDbContext(), new JsonMessageSerializer(), publisher); // Act await sut.SaveProcessManagerAndPublishCommands(processManager); // Assert Mock.Get(publisher).Verify(x => x.FlushCommands(processManager.Id, CancellationToken.None), Times.Once()); }
public async Task given_message_bus_fails_FlushCommands_deletes_no_command() { // Arrange var serializer = new JsonMessageSerializer(); var processManager = new FooProcessManager(); var fixture = new Fixture(); var commands = new List <PendingCommand>( from command in fixture.CreateMany <FooCommand>() let envelope = new Envelope(command) select PendingCommand.FromEnvelope(processManager, envelope, serializer)); using (var db = new FooProcessManagerDbContext()) { db.PendingCommands.AddRange(commands); await db.SaveChangesAsync(); } IMessageBus messageBus = Mock.Of <IMessageBus>(); var exception = new InvalidOperationException(); Mock.Get(messageBus) .Setup(x => x.Send(It.IsAny <IEnumerable <Envelope> >(), It.IsAny <CancellationToken>())) .ThrowsAsync(exception); var sut = new SqlCommandPublisher( () => new FooProcessManagerDbContext(), serializer, messageBus, Mock.Of <IScheduledMessageBus>()); // Act Func <Task> action = () => sut.FlushCommands(processManager.Id, CancellationToken.None); // Assert action.ShouldThrow <InvalidOperationException>().Which.Should().BeSameAs(exception); using (var db = new FooProcessManagerDbContext()) { IQueryable <PendingCommand> query = from c in db.PendingCommands where c.ProcessManagerId == processManager.Id select c; List <PendingCommand> actual = await query.ToListAsync(); actual.ShouldAllBeEquivalentTo(commands, opts => opts.RespectingRuntimeTypes()); } }
public async Task FindProcessManager_returns_null_if_process_manager_that_satisfies_predicate_not_found() { // Arrange var sut = new SqlProcessManagerDataContext <FooProcessManager>( new FooProcessManagerDbContext(), new JsonMessageSerializer(), Mock.Of <ICommandPublisher>()); using (sut) { Expression <Func <FooProcessManager, bool> > predicate = x => x.Id == Guid.NewGuid(); // Act FooProcessManager actual = await sut.FindProcessManager(predicate, CancellationToken.None); // Assert actual.Should().BeNull(); } }
public async Task FindProcessManager_returns_process_manager_that_satisfies_predicate() { // Arrange var processManagers = Enumerable .Repeat <Func <FooProcessManager> >(() => new FooProcessManager { AggregateId = Guid.NewGuid() }, 10) .Select(f => f.Invoke()) .ToList(); FooProcessManager expected = processManagers.First(); using (var db = new FooProcessManagerDbContext()) { var random = new Random(); foreach (FooProcessManager processManager in from p in processManagers orderby random.Next() select p) { db.FooProcessManagers.Add(processManager); } await db.SaveChangesAsync(); } var sut = new SqlProcessManagerDataContext <FooProcessManager>( new FooProcessManagerDbContext(), new JsonMessageSerializer(), Mock.Of <ICommandPublisher>()); using (sut) { Expression <Func <FooProcessManager, bool> > predicate = x => x.AggregateId == expected.AggregateId; // Act FooProcessManager actual = await sut.FindProcessManager(predicate, CancellationToken.None); // Assert actual.Should().NotBeNull(); actual.Id.Should().Be(expected.Id); } }
public void given_fails_to_commit_SaveProcessManagerAndPublishCommands_does_not_publish_commands() { // Arrange var fixture = new Fixture(); ICommandPublisher publisher = Mock.Of <ICommandPublisher>(); FooProcessManager processManager = fixture.Create <FooProcessManager>(); processManager.SetValidationError("invalid process manager state"); var sut = new SqlProcessManagerDataContext <FooProcessManager>( new FooProcessManagerDbContext(), new JsonMessageSerializer(), publisher); // Act Func <Task> action = () => sut.SaveProcessManagerAndPublishCommands(processManager); // Assert action.ShouldThrow <DbEntityValidationException>(); Mock.Get(publisher).Verify(x => x.FlushCommands(processManager.Id, CancellationToken.None), Times.Never()); }
public async Task EnqueueAll_publishes_all_pending_scheduled_commands_asynchronously() { // Arrange var serializer = new JsonMessageSerializer(); var fixture = new Fixture(); using (var db = new FooProcessManagerDbContext()) { for (int i = 0; i < 3; i++) { var processManager = new FooProcessManager(); db.PendingScheduledCommands.AddRange(from command in Enumerable.Repeat(new FooCommand(), 3) let envelope = new Envelope(command) let scheduledEnvelope = new ScheduledEnvelope(envelope, DateTimeOffset.Now) select PendingScheduledCommand.FromScheduledEnvelope(processManager, scheduledEnvelope, serializer)); } await db.SaveChangesAsync(); } var sut = new SqlCommandPublisher( () => new FooProcessManagerDbContext(), serializer, Mock.Of <IMessageBus>(), Mock.Of <IScheduledMessageBus>()); // Act sut.EnqueueAll(CancellationToken.None); // Assert using (var db = new FooProcessManagerDbContext()) { int maximumRetryCount = 5; var retryPolicy = new RetryPolicy <bool>( maximumRetryCount, new DelegatingTransientFaultDetectionStrategy <bool>(any => any == true), new ConstantRetryIntervalStrategy(TimeSpan.FromSeconds(1.0))); (await retryPolicy.Run(db.PendingScheduledCommands.AnyAsync, CancellationToken.None)).Should().BeFalse(); } }
public async Task FlushCommands_sends_all_scheduled_commands_associated_with_specified_process_manager_sequentially() { // Arrange var serializer = new JsonMessageSerializer(); var processManager = new FooProcessManager(); var noiseProcessManager = new FooProcessManager(); var fixture = new Fixture(); var scheduledEnvelopes = new List <ScheduledEnvelope>( from command in fixture.CreateMany <FooCommand>() let envelope = new Envelope(Guid.NewGuid(), command, null, Guid.NewGuid(), null) select new ScheduledEnvelope(envelope, fixture.Create <DateTimeOffset>())); using (var db = new FooProcessManagerDbContext()) { db.PendingScheduledCommands.AddRange( from scheduledEnvelope in scheduledEnvelopes select PendingScheduledCommand.FromScheduledEnvelope(processManager, scheduledEnvelope, serializer)); db.PendingScheduledCommands.AddRange( from scheduledEnvelope in fixture.CreateMany <ScheduledEnvelope>() select PendingScheduledCommand.FromScheduledEnvelope(noiseProcessManager, scheduledEnvelope, serializer)); await db.SaveChangesAsync(); } var scheduledMessageBus = new ScheduledMessageBus(); var sut = new SqlCommandPublisher( () => new FooProcessManagerDbContext(), serializer, Mock.Of <IMessageBus>(), scheduledMessageBus); // Act await sut.FlushCommands(processManager.Id, CancellationToken.None); // Assert scheduledMessageBus.Sent.ShouldAllBeEquivalentTo(scheduledEnvelopes, opts => opts.WithStrictOrdering().RespectingRuntimeTypes()); }
public async Task FindProcessManager_flushes_pending_commands() { // Arrange ICommandPublisher publisher = Mock.Of <ICommandPublisher>(); var processManager = new FooProcessManager(); var sut = new SqlProcessManagerDataContext <FooProcessManager>( new FooProcessManagerDbContext(), new JsonMessageSerializer(), publisher); using (var db = new FooProcessManagerDbContext()) { db.FooProcessManagers.Add(processManager); await db.SaveChangesAsync(); } // Act await sut.FindProcessManager(p => p.Id == processManager.Id, CancellationToken.None); // Assert Mock.Get(publisher).Verify(x => x.FlushCommands(processManager.Id, CancellationToken.None), Times.Once()); }
public async Task SaveProcessManagerAndPublishCommands_updates_existing_process_manager() { // Arrange var fixture = new Fixture(); FooProcessManager processManager = fixture.Create <FooProcessManager>(); using (var db = new FooProcessManagerDbContext()) { db.FooProcessManagers.Add(processManager); await db.SaveChangesAsync(); } string statusValue = fixture.Create <string>(); var sut = new SqlProcessManagerDataContext <FooProcessManager>( new FooProcessManagerDbContext(), new JsonMessageSerializer(), Mock.Of <ICommandPublisher>()); using (sut) { processManager = await sut.FindProcessManager(x => x.Id == processManager.Id); processManager.StatusValue = statusValue; // Act await sut.SaveProcessManagerAndPublishCommands(processManager); } // Assert using (var db = new FooProcessManagerDbContext()) { FooProcessManager actual = await db.FooProcessManagers.SingleOrDefaultAsync(x => x.Id == processManager.Id); actual.StatusValue.Should().Be(statusValue); } }