public async Task Test_StatsAreCorrectlyUpdated_AfterDequeue_NoTaskType()
        {
            PostgreSqlTaskQueueInfo taskQueueInfo =
                CreateTaskQueueInfo(() => mDataSource.LastPostedAt);

            using (PostgreSqlTaskQueueConsumer taskQueueConsumer =
                       CreateTaskQueueConsumer(() => mDataSource.LastPostedAt))
                using (TaskQueueMetricsDiffChecker diff = new TaskQueueMetricsDiffChecker(async()
                                                                                          => await taskQueueInfo.ComputeMetricsAsync()))
                {
                    await diff.CaptureInitialMetricsAsync();

                    IQueuedTaskToken dequeuedToken = await taskQueueConsumer
                                                     .DequeueAsync();

                    QueuedTaskStatus origStatus = mDataSource
                                                  .GetOriginalTokenData(dequeuedToken.DequeuedTask.Id)
                                                  .LastQueuedTaskResult
                                                  .Status;

                    await diff.CaptureNewMetricsAndAssertCorrectDiff(delta : new TaskQueueMetrics(
                                                                         totalUnprocessed: origStatus == QueuedTaskStatus.Unprocessed ? -1 : 0,
                                                                         totalProcessing: 1,
                                                                         totalErrored: origStatus == QueuedTaskStatus.Error ? -1 : 0,
                                                                         totalFaulted: origStatus == QueuedTaskStatus.Faulted ? -1 : 0,
                                                                         totalFataled: origStatus == QueuedTaskStatus.Fatal ? -1 : 0,
                                                                         totalProcessed: origStatus == QueuedTaskStatus.Processed ? -1 : 0));
                }
        }
        public async Task Test_StatsAreUpdatedCorrectly_AfterEnqueue_RepostExistingTask()
        {
            Faker faker =
                new Faker();

            DateTimeOffset postedAt = mDataSource.LastPostedAt
                                      .AddTicks(1);

            PostgreSqlTaskQueueProducer taskQueueProducer =
                CreateTaskQueueProducer(() => postedAt);

            PostgreSqlTaskQueueInfo taskQueueInfo =
                CreateTaskQueueInfo(() => postedAt);

            foreach (IQueuedTaskToken token in mDataSource.CanBeRepostedSeededTaskTokens)
            {
                using (TaskQueueMetricsDiffChecker diff = new TaskQueueMetricsDiffChecker(async()
                                                                                          => await taskQueueInfo.ComputeMetricsAsync()))
                {
                    QueuedTaskStatus prevStatus = token
                                                  .LastQueuedTaskResult
                                                  .Status;

                    await diff.CaptureInitialMetricsAsync();

                    QueuedTaskInfo repostTaskInfo = new QueuedTaskInfo()
                    {
                        Id            = token.DequeuedTask.Id,
                        Priority      = faker.Random.Int(1, 100),
                        Payload       = token.DequeuedTask.Payload,
                        Source        = nameof(Test_StatsAreUpdatedCorrectly_AfterEnqueue_RepostExistingTask),
                        Type          = token.DequeuedTask.Type,
                        LockedUntilTs = postedAt.AddMinutes(faker.Random.Long(1000, 10000))
                    };

                    //Remove task record from DB - only dequeued tasks get reposted
                    await mDataSource.RemoveQueuedTaskFromDbByIdAsync(token
                                                                      .DequeuedTask
                                                                      .Id);

                    await taskQueueProducer.EnqueueAsync(repostTaskInfo);

                    await diff.CaptureNewMetricsAndAssertCorrectDiff(delta : new TaskQueueMetrics(
                                                                         totalUnprocessed: prevStatus != QueuedTaskStatus.Unprocessed ? 1 : 0,
                                                                         totalProcessing: prevStatus == QueuedTaskStatus.Processing ? -1 : 0,
                                                                         totalErrored: prevStatus == QueuedTaskStatus.Error ? -1 : 0,
                                                                         totalFaulted: prevStatus == QueuedTaskStatus.Faulted ? -1 : 0,
                                                                         totalFataled: prevStatus == QueuedTaskStatus.Fatal ? -1 : 0,
                                                                         totalProcessed: prevStatus == QueuedTaskStatus.Processed ? -1 : 0));
                }
            }
        }
        private async Task AssertAllTasksCompletedWithStatus(int expectedTaskResultCount,
                                                             QueuedTaskStatus expectedStatus,
                                                             int expectedErrorCount)
        {
            string countCompletedSql = $@"SELECT COUNT(1) 
				FROM {TestOptions.DefaultMapping.ResultsQueueTableName}
				WHERE task_status = {( int )expectedStatus} 
					AND task_error_count = {expectedErrorCount}"                    ;

            using (NpgsqlConnection conn = await OpenDbConnectionAsync(CommonConnectionString))
                using (NpgsqlCommand cmd = new NpgsqlCommand(countCompletedSql, conn))
                {
                    long completedCount = ( long )await cmd.ExecuteScalarAsync();

                    Assert.AreEqual(expectedTaskResultCount, completedCount);
                }
        }
        public async Task <TaskQueueMetrics> ComputeMetricsAsync()
        {
            long totalUnprocessed = 0,
                 totalProcessing  = 0,
                 totalErrored     = 0,
                 totalFaulted     = 0,
                 totalFataled     = 0,
                 totalProcessed   = 0;

            CheckNotDisposedOrThrow();

            string statsResultsSql = $@"SELECT q.task_status, 
					COUNT(q.task_status) AS task_status_count 
				FROM {mOptions.Mapping.ResultsQueueTableName} AS q 
				GROUP BY q.task_status"                ;

            using (NpgsqlConnection conn = await OpenConnectionAsync())
                using (NpgsqlCommand statsResultsCmd = new NpgsqlCommand(statsResultsSql, conn))
                    using (NpgsqlDataReader statsResultsRdr = await statsResultsCmd.ExecuteReaderAsync())
                    {
                        while (statsResultsRdr.Read())
                        {
                            long count = await statsResultsRdr.GetFieldValueAsync("task_status_count",
                                                                                  defaultValue : 0);

                            QueuedTaskStatus status = ( QueuedTaskStatus )(await statsResultsRdr.GetFieldValueAsync("task_status",
                                                                                                                    defaultValue: 0));

                            switch (status)
                            {
                            case QueuedTaskStatus.Unprocessed:
                                totalUnprocessed = count;
                                break;

                            case QueuedTaskStatus.Processing:
                                totalProcessing = count;
                                break;

                            case QueuedTaskStatus.Error:
                                totalErrored = count;
                                break;

                            case QueuedTaskStatus.Faulted:
                                totalFaulted = count;
                                break;

                            case QueuedTaskStatus.Fatal:
                                totalFataled = count;
                                break;

                            case QueuedTaskStatus.Processed:
                                totalProcessed = count;
                                break;
                            }
                        }

                        await statsResultsRdr.CloseAsync();

                        await conn.CloseAsync();
                    }

            return(new TaskQueueMetrics(totalUnprocessed,
                                        totalProcessing,
                                        totalErrored,
                                        totalFaulted,
                                        totalFataled,
                                        totalProcessed));
        }