public async Task CollectEvents_does_not_fail_for_empty_event_list(
     IMessageBus eventBus, Guid streamId)
 {
     TableEventStore <State1> sut = GenerateEventStore(eventBus);
     Func <Task> action           = () => sut.CollectEvents(streamId, startVersion: 1, Array.Empty <object>());
     await action.Should().NotThrowAsync();
 }
        public async Task Handle_is_idempotent(
            Guid streamId,
            long startVersion,
            Event1[] events,
            string commandId,
            TracingProperties tracingProperties,
            MessageBusDouble eventBus)
        {
            // Arrange
            var brokenEventBus = new MessageBusDouble(errors: 1);
            TableEventStore <State1> eventStore = GenerateEventStore(brokenEventBus);

            await TryCatchIgnore(() => eventStore.CollectEvents(streamId, startVersion, events));

            TableFlushEventsCommandExecutor sut = GenerateSut(eventBus);
            var command = new FlushEvents(TypeResolver.ResolveTypeName <State1>(), streamId);
            var message = new Message(id: commandId, data: command, tracingProperties);

            // Act
            await sut.Handle(message);

            await sut.Handle(message);

            // Assert
            eventBus.Calls.Should().BeEquivalentTo(brokenEventBus.Calls);
        }
        public async Task sut_supports_multiple_state_types_having_same_stream_id(
            IMessageBus eventBus, Guid streamId, Event1 evt1, Event2 evt2)
        {
            // Arrange
            TableEventStore <State1> store1 = GenerateEventStore <State1>(eventBus);
            TableEventStore <State2> store2 = GenerateEventStore <State2>(eventBus);

            int startVersion = 1;

            // Act
            Func <Task> action = async() =>
            {
                await store1.CollectEvents(streamId, startVersion, new[] { evt1 });

                await store2.CollectEvents(streamId, startVersion, new[] { evt2 });
            };

            // Assert
            await action.Should().NotThrowAsync();

            IEnumerable <object> actual1 = await store1.QueryEvents(streamId, fromVersion : 1);

            actual1.Should().BeEquivalentTo(evt1);

            IEnumerable <object> actual2 = await store2.QueryEvents(streamId, fromVersion : 1);

            actual2.Should().BeEquivalentTo(evt2);
        }
        public async Task sut_sends_flush_commands_for_streams_containing_cold_pending_events(
            Guid streamId, long startVersion, Event1[] events, MessageBusDouble commandBus)
        {
            // Arrange
            var eventBus = new MessageBusDouble(errors: 1);
            TableEventStore <State1> eventStore = GenerateEventStore(eventBus);

            await TryCatchIgnore(() => eventStore.CollectEvents(streamId, startVersion, events));

            var sut = new TablePendingEventScanner(Table, commandBus);

            // Act
            await sut.ScanPendingEvents();

            // Assert
            commandBus.Calls.Should().ContainSingle();
            (ImmutableArray <Message> messages, string partitionKey) = commandBus.Calls.Single();

            messages.Should().ContainSingle();
            Message message = messages.Single();

            Guid.TryParse(message.Id, out Guid id).Should().BeTrue();
            id.Should().NotBeEmpty();

            message.Data.Should().BeOfType <FlushEvents>();
            message.Data.Should().BeEquivalentTo(new
            {
                StateType = TypeResolver.ResolveTypeName <State1>(),
                StreamId  = streamId,
            });

            partitionKey.Should().Be($"{streamId}");
        }
        public async Task sut_does_not_send_flush_command_for_streams_containing_only_hot_pending_events(
            Guid streamId, long startVersion, Event1[] events, MessageBusDouble commandBus)
        {
            // Arrange
            var eventBus = new MessageBusDouble(errors: 1);
            TableEventStore <State1> eventStore = GenerateEventStore(eventBus);

            await TryCatchIgnore(() => eventStore.CollectEvents(streamId, startVersion, events));

            var minimumPendingTime = TimeSpan.FromSeconds(1);
            var sut = new TablePendingEventScanner(Table, commandBus, minimumPendingTime);

            // Act
            await sut.ScanPendingEvents();

            // Assert
            commandBus.Calls.Should().BeEmpty();
        }
        public async Task sut_distincts_streams(
            Guid streamId, long startVersion, Event1[] events, MessageBusDouble commandBus)
        {
            // Arrange
            var eventBus = new MessageBusDouble(errors: 2);
            TableEventStore <State1> eventStore = GenerateEventStore(eventBus);

            await TryCatchIgnore(() => eventStore.CollectEvents(streamId, startVersion, events));
            await TryCatchIgnore(() => eventStore.CollectEvents(streamId, startVersion + events.Length, events));

            var sut = new TablePendingEventScanner(Table, commandBus);

            // Act
            await sut.ScanPendingEvents();

            // Assert
            commandBus.Calls.Should().ContainSingle();
        }
        public async Task sut_sets_tracing_properties_correctly(
            Guid streamId, long startVersion, Event1[] events, MessageBusDouble commandBus)
        {
            // Arrange
            var eventBus = new MessageBusDouble(errors: 1);
            TableEventStore <State1> eventStore = GenerateEventStore(eventBus);

            await TryCatchIgnore(() => eventStore.CollectEvents(streamId, startVersion, events));

            var sut = new TablePendingEventScanner(Table, commandBus);

            // Act
            await sut.ScanPendingEvents();

            // Assert
            TracingProperties actual = commandBus.Calls.Single().messages.Single().TracingProperties;

            actual.OperationId.Should().NotBeNullOrWhiteSpace();
            actual.Contributor.Should().Be("Loom.EventSourcing.Azure.TablePendingEventScanner");
            actual.ParentId.Should().BeNull();
        }