public async Task TearDown()
        {
            var nameProvider = new DelayedDeliveryTableNameProvider();

            await DeleteTable(nameProvider.GetStagingTableName(endpointName));
            await DeleteTable(nameProvider.GetDelayedDeliveryTableName(endpointName));
        }
        public void When_Migrating_Endpoint_With_No_DelayedDeliveryTableOverride_DelayedDeliveryTableName_Is_Computed()
        {
            // Arrange
            var delayedDeliveryTableNameGenerator = new DelayedDeliveryTableNameProvider(null);

            // Act
            var delayedDeliveryTableName = delayedDeliveryTableNameGenerator.GetDelayedDeliveryTableName("EndpointName");

            // Assert
            Assert.IsTrue(delayedDeliveryTableName.StartsWith("delays"));
        }
        public void When_Migrating_Endpoint_With_DelayedDeliveryTableOverride_DelayedDeliveryTableName_Is_NotComputed()
        {
            // Arrange
            var delayedDeliveryTableNameGenerator = new DelayedDeliveryTableNameProvider("overriden delayed delivery table name");

            // Act
            var delayedDeliveryTableName = delayedDeliveryTableNameGenerator.GetDelayedDeliveryTableName("EndpointName");

            // Assert
            Assert.AreEqual("overriden delayed delivery table name", delayedDeliveryTableName);
        }
        public async Task Prepare_creates_the_staging_queue()
        {
            // Arrange
            var nameProvider  = new DelayedDeliveryTableNameProvider();
            var timeoutTarget = new ASQTarget(connectionString, nameProvider);

            // Act
            await using var migrator = await timeoutTarget.PrepareTargetEndpointBatchMigrator(endpointName);

            // Assert
            Assert.IsTrue(await DoesTableExist(nameProvider.GetStagingTableName(endpointName)).ConfigureAwait(true));
        }
        public async Task Completing_with_large_entities_batches_respecting_size_limitations()
        {
            // Arrange
            var nameProvider = new DelayedDeliveryTableNameProvider();
            var endpointName = nameof(Completing_with_large_entities_batches_respecting_size_limitations);
            var cutOffDate   = DateTime.UtcNow;
            var random       = new Random();
            var timeouts     = new List <StagedDelayedMessageEntity>();

            var target           = new ASQTarget(connectionString, nameProvider);
            var stagingTableName = nameProvider.GetStagingTableName(endpointName);

            await CreateTimeoutTable(stagingTableName);
            await CreateTimeoutTable(nameProvider.GetDelayedDeliveryTableName(endpointName));

            var stagingTable = tableClient.GetTableReference(stagingTableName);

            // the entity will roughly be 98 KB and we will store 50 of those which makes the actual payload be around 5 MB
            for (var x = 0; x < 50; x++)
            {
                var dateTime  = cutOffDate.AddDays(random.Next(1, 5));
                var messageId = Guid.NewGuid().ToString();
                var entity    = new StagedDelayedMessageEntity
                {
                    Destination  = new string('a', 32 * 1024),
                    Headers      = new string('a', 32 * 1024),
                    Time         = dateTime,
                    MessageId    = messageId,
                    Body         = new byte[32 * 1024],
                    RowKey       = $"{messageId}_{dateTime.ToString($"yyyyMMddHHmmss")}",
                    PartitionKey = "1"
                };

                await stagingTable.ExecuteAsync(TableOperation.Insert(entity));

                timeouts.Add(entity);
            }

            // Act
            var result = await target.PrepareTargetEndpointBatchMigrator(endpointName);

            var completeResult = await result.CompleteBatch(1);

            // Assert
            Assert.IsNotNull(completeResult);
            Assert.AreEqual(timeouts.Count, completeResult);
        }
        public async Task Complete_Removes_Staging_Queue_If_Empty()
        {
            // Arrange
            var nameProvider = new DelayedDeliveryTableNameProvider();

            await CreateTimeoutTable(nameProvider.GetDelayedDeliveryTableName(endpointName));

            var timeoutTarget = new ASQTarget(connectionString, nameProvider);

            await using var migrator = await timeoutTarget.PrepareTargetEndpointBatchMigrator(endpointName);

            await migrator.StageBatch(new List <TimeoutData>
            {
                new TimeoutData
                {
                    Id      = "SomeID",
                    Headers = new Dictionary <string, string>
                    {
                        { "NServiceBus.MessageId", "SomeMessageId" }
                    },
                    Destination = "SomeDestination",
                    State       = new byte[2],
                    Time        = new DateTime(2021, 12, 12, 12, 12, 12, DateTimeKind.Utc)
                },
                new TimeoutData
                {
                    Id      = "SomeOtherId",
                    Headers = new Dictionary <string, string>
                    {
                        { "NServiceBus.MessageId", "SomeOtherMessageId" }
                    },
                    Destination = "SomeOtherDestination",
                    State       = new byte[2],
                    Time        = new DateTime(2021, 12, 12, 12, 13, 13, DateTimeKind.Utc)
                },
            }, 1);

            await migrator.CompleteBatch(1);

            // Act
            await timeoutTarget.Complete(endpointName);

            // Assert
            var stagingTableExists = await DoesTableExist(nameProvider.GetStagingTableName(endpointName));

            Assert.IsFalse(stagingTableExists);
        }
        public async Task CompleteBatch_Moves_All_Entries_From_Staging_To_DelayedMessageTable()
        {
            // Arrange
            var nameProvider = new DelayedDeliveryTableNameProvider();

            await CreateTimeoutTable(nameProvider.GetDelayedDeliveryTableName(endpointName));

            var timeoutTarget = new ASQTarget(connectionString, nameProvider);

            await using var migrator = await timeoutTarget.PrepareTargetEndpointBatchMigrator(endpointName);

            await migrator.StageBatch(new List <TimeoutData>
            {
                new TimeoutData
                {
                    Id      = "SomeID",
                    Headers = new Dictionary <string, string>
                    {
                        { "NServiceBus.MessageId", "SomeMessageId" }
                    },
                    Destination = "SomeDestination",
                    State       = new byte[2],
                    Time        = new DateTime(2021, 12, 12, 12, 12, 12, DateTimeKind.Utc)
                },
                new TimeoutData
                {
                    Id      = "SomeOtherId",
                    Headers = new Dictionary <string, string>
                    {
                        { "NServiceBus.MessageId", "SomeOtherMessageId" }
                    },
                    Destination = "SomeOtherDestination",
                    State       = new byte[2],
                    Time        = new DateTime(2021, 12, 12, 12, 13, 13, DateTimeKind.Utc)
                },
            }, 1);

            // Act
            var numberCompleted = await migrator.CompleteBatch(1);

            // Assert
            var recordsInTimeoutTable = await ReadTimeoutsFromTable(nameProvider.GetDelayedDeliveryTableName(endpointName));

            Assert.AreEqual(2, recordsInTimeoutTable.Count);
            Assert.AreEqual(2, numberCompleted);
        }
        public async Task Complete_Throws_If_Messages_Are_Still_Staged()
        {
            // Arrange
            var nameProvider = new DelayedDeliveryTableNameProvider();

            await CreateTimeoutTable(nameProvider.GetDelayedDeliveryTableName(endpointName));

            var timeoutTarget = new ASQTarget(connectionString, nameProvider);

            await using var migrator = await timeoutTarget.PrepareTargetEndpointBatchMigrator(endpointName);

            var numberStaged = await migrator.StageBatch(new List <TimeoutData>
            {
                new TimeoutData
                {
                    Id      = "SomeID",
                    Headers = new Dictionary <string, string>
                    {
                        { "NServiceBus.MessageId", "SomeMessageId" }
                    },
                    Destination = "SomeDestination",
                    State       = new byte[2],
                    Time        = new DateTime(2021, 12, 12, 12, 12, 12, DateTimeKind.Utc)
                },
                new TimeoutData
                {
                    Id      = "SomeOtherId",
                    Headers = new Dictionary <string, string>
                    {
                        { "NServiceBus.MessageId", "SomeOtherMessageId" }
                    },
                    Destination = "SomeOtherDestination",
                    State       = new byte[2],
                    Time        = new DateTime(2021, 12, 12, 12, 13, 13, DateTimeKind.Utc)
                },
            }, 1);

            // Assert
            Assert.ThrowsAsync <Exception>(async() =>
            {
                // Act
                await timeoutTarget.Complete(endpointName);
            });
        }
        public async Task Abort_Removes_Staging_Table()
        {
            // Arrange
            var nameProvider = new DelayedDeliveryTableNameProvider();

            await CreateTimeoutTable(nameProvider.GetDelayedDeliveryTableName(endpointName));

            var timeoutTarget = new ASQTarget(connectionString, nameProvider);

            await using var migrator = await timeoutTarget.PrepareTargetEndpointBatchMigrator(endpointName);

            // Act
            await timeoutTarget.Abort(endpointName);

            // Assert
            var stagingTableExists = await DoesTableExist(nameProvider.GetStagingTableName(endpointName));

            Assert.IsFalse(stagingTableExists);
        }
        public async Task Staging_with_large_entities_batches_respecting_size_limitations()
        {
            // Arrange
            var nameProvider = new DelayedDeliveryTableNameProvider();
            var endpointName = nameof(Staging_with_large_entities_batches_respecting_size_limitations);
            var cutOffDate   = DateTime.UtcNow;
            var random       = new Random();
            var target       = new ASQTarget(connectionString, nameProvider);
            var timeouts     = new List <TimeoutData>();

            // the entity will roughly be 98 KB and we will store 50 of those which makes the actual payload be around 5 MB
            for (var x = 0; x < 50; x++)
            {
                var dateTime = cutOffDate.AddDays(random.Next(1, 5));

                timeouts.Add(new TimeoutData
                {
                    Destination = new string('a', 32 * 1024),
                    Headers     = new Dictionary <string, string> {
                        { "a", new string('h', 16 * 1024) }
                    },
                    Time   = dateTime,
                    Id     = Guid.NewGuid().ToString(),
                    State  = new byte[32 * 1024],
                    SagaId = Guid.NewGuid(),
                    OwningTimeoutManager = this.endpointName
                });
            }

            // Act
            var result = await target.PrepareTargetEndpointBatchMigrator(endpointName);

            var stageResult = await result.StageBatch(timeouts, 1);

            // Assert
            Assert.IsNotNull(stageResult);
            Assert.AreEqual(timeouts.Count, stageResult);
        }