示例#1
0
        public void TestEquals_AccessionDirectoryMessage()
        {
            var msg1 = new AccessionDirectoryMessage();
            var msg2 = new AccessionDirectoryMessage();

            Assert.AreEqual(msg1, msg2);
            Assert.AreEqual(msg1.GetHashCode(), msg2.GetHashCode());

            msg1.NationalPACSAccessionNumber = "500";
            msg2.NationalPACSAccessionNumber = "500";

            Assert.AreEqual(msg1, msg2);
            Assert.AreEqual(msg1.GetHashCode(), msg2.GetHashCode());

            msg1.NationalPACSAccessionNumber = "999";
            Assert.AreNotEqual(msg1, msg2);
            Assert.AreNotEqual(msg1.GetHashCode(), msg2.GetHashCode());

            msg1.NationalPACSAccessionNumber = "500";

            msg1.DirectoryPath = @"c:\temp";
            msg2.DirectoryPath = @"c:\temp";

            Assert.AreEqual(msg1, msg2);
            Assert.AreEqual(msg1.GetHashCode(), msg2.GetHashCode());

            msg2.DirectoryPath = @"C:\Temp"; //caps is relevant

            Assert.AreNotEqual(msg1, msg2);
            Assert.AreNotEqual(msg1.GetHashCode(), msg2.GetHashCode());
        }
示例#2
0
        private void SetUpDefaults()
        {
            Options = new GlobalOptionsFactory().Load();

            AccessionConsumerOptions = Options.DicomTagReaderOptions;

            new ProducerOptions
            {
                ExchangeName = Options.RabbitOptions.FatalLoggingExchange
            };

            TestAccessionDirectoryMessage = new AccessionDirectoryMessage
            {
                DirectoryPath = @"C:\Temp\",
                NationalPACSAccessionNumber = "1234"
            };

            TestSeriesModel = new Mock <IProducerModel>();
            TestImageModel  = new Mock <IProducerModel>();

            MockFileSystem = new MockFileSystem();
            MockFileSystem.AddDirectory(@"C:\Temp");

            TestDir = new DirectoryInfo("DicomTagReaderTests");
            TestDir.Create();

            foreach (FileInfo f in TestDir.GetFiles())
            {
                f.Delete();
            }

            TestData.Create(new FileInfo(Path.Combine(TestDir.FullName, "MyTestFile.dcm")));
        }
示例#3
0
        public void FindingAccessionDirectory()
        {
            var fileSystem = new MockFileSystem(new Dictionary <string, MockFileData>
            {
                { Path.GetFullPath("/PACS/2019/01/01/foo/01/a.dcm"), new MockFileData(new byte[] { 0x12, 0x34, 0x56, 0xd2 }) },
                { Path.GetFullPath("/PACS/2019/01/02/foo/02/a.dcm"), new MockFileData(new byte[] { 0x12, 0x34, 0x56, 0xd2 }) },
            });

            var m1 = new AccessionDirectoryMessage
            {
                NationalPACSAccessionNumber = "01",
                //NOTE: These can't be rooted, so can't easily use Path.GetFullPath
                DirectoryPath = "2019/01/01/foo/01".Replace('/', Path.DirectorySeparatorChar)
            };

            var m2 = new AccessionDirectoryMessage
            {
                NationalPACSAccessionNumber = "02",
                DirectoryPath = "2019/01/02/foo/02".Replace('/', Path.DirectorySeparatorChar)
            };

            string rootDir           = Path.GetFullPath("/PACS");
            var    mockProducerModel = new Mock <IProducerModel>();
            var    ddf = new BasicDicomDirectoryFinder(rootDir, fileSystem, "*.dcm", mockProducerModel.Object);

            ddf.SearchForDicomDirectories(rootDir);

            mockProducerModel.Verify(pm => pm.SendMessage(m1, null, It.IsAny <string>()));
            mockProducerModel.Verify(pm => pm.SendMessage(m2, null, It.IsAny <string>()));
        }
示例#4
0
        public void PopulateDeadLetterQueue(int nMessages)
        {
            var testMessage = new AccessionDirectoryMessage
            {
                NationalPACSAccessionNumber = "1234",
                DirectoryPath = @"C:\temp"
            };

            for (var i = 0; i < nMessages; ++i)
            {
                _testHelper.TestProducer.SendMessage(testMessage, null, DeadLetterTestHelper.TestRoutingKey);
            }

            new TestTimelineAwaiter().Await(() => _testHelper.TestModel.MessageCount(DeadLetterTestHelper.TestDlQueueName) == nMessages);
        }
示例#5
0
        public void TestMessageRejectLoop()
        {
            Assert.True(_deadLetterCollection.CountDocuments(FilterDefinition <MongoDeadLetterDocument> .Empty) == 0);
            Assert.True(_deadLetterGraveyard.CountDocuments(FilterDefinition <MongoDeadLetterGraveyardDocument> .Empty) == 0);

            // Setup a test message and send it, then wait for it to be rejected
            var testMessage = new AccessionDirectoryMessage
            {
                NationalPACSAccessionNumber = "1234",
                DirectoryPath = TestContext.CurrentContext.TestDirectory
            };

            // Set so the host just pushes the message through each time
            _cliOptions.FlushMessages = true;

            // Send the message
            IMessageHeader originalHeader = _testHelper.TestProducer.SendMessage(testMessage, null, DeadLetterTestHelper.TestRoutingKey);

            for (var i = 0; i < _testHelper.GlobalOptions.DeadLetterReprocessorOptions.MaxRetryLimit + 1; i++)
            {
                Assert.True(_deadLetterGraveyard.CountDocuments(FilterDefinition <MongoDeadLetterGraveyardDocument> .Empty) == 0);

                int count = i + 1;
                new TestTimelineAwaiter().Await(() => _testHelper.MessageRejectorConsumer.NackCount == count);

                // Check 1 message on the DLQ
                Assert.AreEqual(1, _testHelper.TestModel.MessageCount(DeadLetterTestHelper.TestDlQueueName));

                // Start the host and check message has been read from DLQ into store
                var host = new DeadLetterReprocessorHost(_testHelper.GlobalOptions, _cliOptions);
                host.Start();

                Assert.True(_deadLetterCollection.CountDocuments(FilterDefinition <MongoDeadLetterDocument> .Empty) == 0);
            }

            // Message should now be in the graveyard
            Assert.True(_deadLetterGraveyard.CountDocuments(FilterDefinition <MongoDeadLetterGraveyardDocument> .Empty) == 1);

            MongoDeadLetterGraveyardDocument graveyardDoc = _deadLetterGraveyard.Find(FilterDefinition <MongoDeadLetterGraveyardDocument> .Empty).Single();

            Assert.AreEqual(graveyardDoc.MessageGuid, graveyardDoc.DeadLetter.MessageGuid);
            Assert.AreEqual(originalHeader.MessageGuid, graveyardDoc.DeadLetter.Props.MessageHeader.Parents[0]);
            Assert.AreEqual("MaxRetryCount exceeded", graveyardDoc.Reason);
            Assert.True((DateTime.UtcNow - graveyardDoc.KilledAt) < TimeSpan.FromSeconds(5));
        }
示例#6
0
        public void TestCaptureOfXDeathHeaders()
        {
            var testMessage = new AccessionDirectoryMessage
            {
                NationalPACSAccessionNumber = "1234",
                DirectoryPath = @"C:\temp"
            };

            _testHelper.TestProducer.SendMessage(testMessage, null, DeadLetterTestHelper.TestRoutingKey);

            new TestTimelineAwaiter().Await(() => _testHelper.MessageRejectorConsumer.NackCount == 1);

            BasicGetResult getResult = _testHelper.TestModel.BasicGet(DeadLetterTestHelper.TestDlQueueName, true);

            var xHeaders = new RabbitMqXDeathHeaders(getResult.BasicProperties.Headers, Encoding.UTF8);

            // Can't really get the expected time, just have to copy from the result
            long expectedTime = xHeaders.XDeaths[0].Time;

            var expectedHeaders = new RabbitMqXDeathHeaders
            {
                XDeaths = new List <RabbitMqXDeath>
                {
                    new RabbitMqXDeath
                    {
                        Count       = 1,
                        Exchange    = DeadLetterTestHelper.RejectExchangeName,
                        Queue       = DeadLetterTestHelper.RejectQueueName,
                        Reason      = "rejected",
                        RoutingKeys = new List <string>
                        {
                            DeadLetterTestHelper.TestRoutingKey
                        },
                        Time = expectedTime
                    }
                },
                XFirstDeathExchange = DeadLetterTestHelper.RejectExchangeName,
                XFirstDeathQueue    = DeadLetterTestHelper.RejectQueueName,
                XFirstDeathReason   = "rejected",
            };

            Assert.AreEqual(expectedHeaders, xHeaders);
        }
示例#7
0
        protected override List <DicomFileMessage> ReadTagsImpl(IEnumerable <FileInfo> dicomFilePaths,
                                                                AccessionDirectoryMessage accMessage)
        {
            var fileMessages     = new List <DicomFileMessage>();
            var fileMessagesLock = new object();

            Parallel.ForEach(dicomFilePaths, _parallelOptions, dicomFilePath =>
            {
                Logger.Trace("TagReader: Processing " + dicomFilePath);

                DicomFileMessage fileMessage;

                try
                {
                    fileMessage = ReadTagsFromFile(dicomFilePath);
                }
                catch (Exception e)
                {
                    if (NackIfAnyFileErrors)
                    {
                        throw new ApplicationException(
                            "Exception processing file and NackIfAnyFileErrors option set. File was: " + dicomFilePath,
                            e);
                    }

                    Logger.Error(e,
                                 "Error processing file " + dicomFilePath +
                                 ". Ignoring and moving on since NackIfAnyFileErrors is false");

                    return;
                }

                fileMessage.NationalPACSAccessionNumber = accMessage.NationalPACSAccessionNumber;

                lock (fileMessagesLock)
                    fileMessages.Add(fileMessage);

                Interlocked.Increment(ref NFilesProcessed);
            });

            return(fileMessages);
        }
示例#8
0
        /// <summary>
        /// Handled when a new DICOM directory is found. Writes an AccessionDirectoryMessage to the message exchange
        /// </summary>
        /// <param name="dir">Full path to a directory that has been found to contain a DICOM file</param>
        protected void FoundNewDicomDirectory(string dir)
        {
            Logger.Debug("DicomDirectoryFinder: Found " + dir);

            string dirPath = Path.GetFullPath(dir).TrimEnd(Path.DirectorySeparatorChar);

            if (dirPath.StartsWith(FileSystemRoot))
            {
                dirPath = dirPath.Remove(0, FileSystemRoot.Length);
            }

            dirPath = dirPath.TrimStart(Path.DirectorySeparatorChar);

            var message = new AccessionDirectoryMessage
            {
                DirectoryPath = dirPath,
                NationalPACSAccessionNumber = GetNationalPacsAccessionNumberFrom(dirPath)
            };

            _directoriesProducerModel.SendMessage(message, null);
            ++TotalSent;
        }
示例#9
0
        public void FindRandomDicomsOrZipsDirectory()
        {
            var fileSystem = new MockFileSystem(new Dictionary <string, MockFileData>
            {
                { Path.GetFullPath("/PACS/FFF/DDD/a.dcm"), new MockFileData(new byte[] { 0x12, 0x34, 0x56, 0xd2 }) },
                { Path.GetFullPath("/PACS/FFF/b.dcm"), new MockFileData(new byte[] { 0x12, 0x34, 0x56, 0xd2 }) },
                { Path.GetFullPath("/PACS/CCC/c.zip"), new MockFileData(new byte[] { 0x12, 0x34, 0x56, 0xd2 }) },
            });

            var m1 = new AccessionDirectoryMessage
            {
                NationalPACSAccessionNumber = null,
                //NOTE: These can't be rooted, so can't easily use Path.GetFullPath
                DirectoryPath = "CCC".Replace('/', Path.DirectorySeparatorChar)
            };

            var m2 = new AccessionDirectoryMessage
            {
                NationalPACSAccessionNumber = null,
                DirectoryPath = "FFF".Replace('/', Path.DirectorySeparatorChar)
            };


            var m3 = new AccessionDirectoryMessage
            {
                NationalPACSAccessionNumber = null,
                DirectoryPath = "FFF/DDD".Replace('/', Path.DirectorySeparatorChar)
            };

            string rootDir           = Path.GetFullPath("/PACS");
            var    mockProducerModel = new Mock <IProducerModel>();
            var    ddf = new ZipDicomDirectoryFinder(rootDir, fileSystem, "*.dcm", mockProducerModel.Object);

            ddf.SearchForDicomDirectories(rootDir);

            mockProducerModel.Verify(pm => pm.SendMessage(m1, null, It.IsAny <string>()));
            mockProducerModel.Verify(pm => pm.SendMessage(m2, null, It.IsAny <string>()));
            mockProducerModel.Verify(pm => pm.SendMessage(m3, null, It.IsAny <string>()));
        }
示例#10
0
        /// <summary>
        /// Process files from the directory referenced in the message
        /// </summary>
        /// <param name="header"></param>
        /// <param name="message"></param>
        public void ReadTags(IMessageHeader header, AccessionDirectoryMessage message)
        {
            _stopwatch.Restart();

            string dirPath = message.GetAbsolutePath(_filesystemRoot);

            Logger.Debug("TagReader: About to process files in " + dirPath);

            if (!_fs.Directory.Exists(dirPath))
            {
                throw new ApplicationException("Directory not found: " + dirPath);
            }

            if (!dirPath.StartsWith(_filesystemRoot, StringComparison.CurrentCultureIgnoreCase))
            {
                throw new ApplicationException("Directory " + dirPath + " is not below the given FileSystemRoot (" +
                                               _filesystemRoot + ")");
            }
            long beginEnumerate = _stopwatch.ElapsedTicks;

            string[] dicomFilePaths = _fs.Directory.EnumerateFiles(dirPath, _searchPattern).Where(Include).ToArray();
            string[] zipFilePaths   = _fs.Directory.EnumerateFiles(dirPath).Where(ZipHelper.IsZip).Where(Include).ToArray();

            _swTotals[0] += _stopwatch.ElapsedTicks - beginEnumerate;
            Logger.Debug("TagReader: Found " + dicomFilePaths.Length + " dicom files to process");
            Logger.Debug("TagReader: Found " + zipFilePaths.Length + " zip files to process");

            int toProcess = dicomFilePaths.Length + zipFilePaths.Length;

            if (toProcess == 0)
            {
                throw new ApplicationException("No dicom/zip files found in " + dirPath);
            }

            // We have files to process, let's do it!

            long beginRead = _stopwatch.ElapsedTicks;

            List <DicomFileMessage> fileMessages = ReadTagsImpl(dicomFilePaths.Select(p => new FileInfo(p)), message);

            fileMessages.AddRange(ReadZipFilesImpl(zipFilePaths.Select(p => new FileInfo(p)), message));

            _swTotals[1] += (_stopwatch.ElapsedTicks - beginRead) / toProcess;

            var seriesMessages = new Dictionary <string, SeriesMessage>();

            foreach (DicomFileMessage fileMessage in fileMessages)
            {
                string seriesUID = fileMessage.SeriesInstanceUID;

                // If we've already seen this seriesUID, just update the image count
                if (seriesMessages.ContainsKey(seriesUID))
                {
                    seriesMessages[seriesUID].ImagesInSeries++;
                    continue;
                }

                // Else create a new SeriesMessage
                var seriesMessage = new SeriesMessage
                {
                    NationalPACSAccessionNumber = fileMessage.NationalPACSAccessionNumber,
                    DirectoryPath = message.DirectoryPath,

                    StudyInstanceUID  = fileMessage.StudyInstanceUID,
                    SeriesInstanceUID = seriesUID,

                    ImagesInSeries = 1,

                    DicomDataset = fileMessage.DicomDataset
                };

                seriesMessages.Add(seriesUID, seriesMessage);
            }

            Logger.Debug("TagReader: Finished processing directory, sending messages");

            // Only send if have processed all files in the directory ok

            if (!fileMessages.Any())
            {
                throw new ApplicationException("No DicomFileMessage(s) to send after processing the directory");
            }

            if (!seriesMessages.Any())
            {
                throw new ApplicationException("No SeriesMessage(s) to send but we have file messages");
            }

            Logger.Info($"Sending {fileMessages.Count} DicomFileMessage(s)");

            long beginSend = _stopwatch.ElapsedTicks;
            var  headers   = new List <IMessageHeader>();

            foreach (DicomFileMessage fileMessage in fileMessages)
            {
                headers.Add(_fileMessageProducerModel.SendMessage(fileMessage, header));
            }

            _fileMessageProducerModel.WaitForConfirms();

            headers.ForEach(x => x.Log(Logger, LogLevel.Trace, $"Sent {header?.MessageGuid}"));

            Logger.Info($"Sending {seriesMessages.Count} SeriesMessage(s)");

            headers.Clear();
            foreach (KeyValuePair <string, SeriesMessage> kvp in seriesMessages)
            {
                headers.Add(_seriesMessageProducerModel.SendMessage(kvp.Value, header));
            }

            _seriesMessageProducerModel.WaitForConfirms();
            headers.ForEach(x => x.Log(Logger, LogLevel.Trace, $"Sent {x.MessageGuid}"));

            _swTotals[2]   += _stopwatch.ElapsedTicks - beginSend;
            _swTotals[3]   += _stopwatch.ElapsedTicks;
            _nMessagesSent += fileMessages.Count + seriesMessages.Count;

            if (++_nAccMessagesProcessed % 10 == 0)
            {
                LogRates();
            }
        }
示例#11
0
 protected abstract List <DicomFileMessage> ReadTagsImpl(IEnumerable <FileInfo> dicomFilePaths,
                                                         AccessionDirectoryMessage accMessage);
示例#12
0
        /// <summary>
        /// Opens all zip files <paramref name="zipFilePaths"/> and generates a <see cref="DicomFileMessage"/> for each dcm file in the archive
        /// </summary>
        /// <param name="zipFilePaths">All the zip files that must be explored for dcm files</param>
        /// <param name="accMessage">The upstream message that suggested we look for dicom files in a given directory</param>
        /// <returns></returns>
        protected virtual IEnumerable <DicomFileMessage> ReadZipFilesImpl(IEnumerable <FileInfo> zipFilePaths, AccessionDirectoryMessage accMessage)
        {
            foreach (FileInfo zipFilePath in zipFilePaths)
            {
                using (var archive = ZipFile.Open(zipFilePath.FullName, ZipArchiveMode.Read))
                {
                    foreach (var entry in archive.Entries)
                    {
                        if (entry.FullName.EndsWith(".dcm", StringComparison.CurrentCultureIgnoreCase))
                        {
                            byte[] buffer = null;

                            buffer = ReadFully(entry.Open());

                            using (var memoryStream = new MemoryStream(buffer))
                            {
                                var dicom = DicomFile.Open(memoryStream);

                                yield return(DicomFileToMessage(dicom.Dataset, zipFilePath.FullName + "!" + entry.FullName, null));
                            }
                        }
                    }
                }
            }
        }
示例#13
0
        protected override List <DicomFileMessage> ReadTagsImpl(IEnumerable <FileInfo> dicomFilePaths, AccessionDirectoryMessage accMessage)
        {
            var fileMessages = new List <DicomFileMessage>();

            foreach (FileInfo dicomFilePath in dicomFilePaths)
            {
                Logger.Trace("TagReader: Processing " + dicomFilePath);

                DicomFileMessage fileMessage;

                try
                {
                    fileMessage = ReadTagsFromFile(dicomFilePath);
                    fileMessage.NationalPACSAccessionNumber = accMessage.NationalPACSAccessionNumber;
                }
                catch (Exception e)
                {
                    if (NackIfAnyFileErrors)
                    {
                        throw new ApplicationException(
                                  "Exception processing file and NackIfAnyFileErrors option set. File was: " + dicomFilePath,
                                  e);
                    }

                    Logger.Error(e,
                                 "Error processing file " + dicomFilePath +
                                 ". Ignoring and moving on since NackIfAnyFileErrors is false");

                    continue;
                }

                fileMessages.Add(fileMessage);
                ++NFilesProcessed;
            }

            return(fileMessages);
        }
示例#14
0
        public void TestBasicOperation()
        {
            Assert.True(_deadLetterCollection.CountDocuments(FilterDefinition <MongoDeadLetterDocument> .Empty) == 0);
            Assert.True(_deadLetterGraveyard.CountDocuments(FilterDefinition <MongoDeadLetterGraveyardDocument> .Empty) == 0);

            // Setup a test message and send it, then wait for it to be rejected
            var testMessage = new AccessionDirectoryMessage
            {
                NationalPACSAccessionNumber = "1234",
                DirectoryPath = TestContext.CurrentContext.TestDirectory
            };

            _testHelper.TestProducer.SendMessage(testMessage, null, DeadLetterTestHelper.TestRoutingKey);
            new TestTimelineAwaiter().Await(() => _testHelper.MessageRejectorConsumer.NackCount == 1);

            IMessageHeader        originalHeader = _testHelper.MessageRejectorConsumer.LastHeader;
            BasicDeliverEventArgs originalArgs   = _testHelper.MessageRejectorConsumer.LastArgs;

            Assert.NotNull(originalHeader);
            Assert.NotNull(originalArgs);

            // Set so the next message is accepted
            _testHelper.MessageRejectorConsumer.AcceptNext = true;

            // Check 1 message on the DLQ
            Assert.AreEqual(1, _testHelper.TestModel.MessageCount(DeadLetterTestHelper.TestDlQueueName));

            // Start the host and check message has been read from DLQ into store
            var host = new DeadLetterReprocessorHost(_testHelper.GlobalOptions, _cliOptions);

            host.Start();

            Assert.True(_deadLetterCollection.CountDocuments(FilterDefinition <MongoDeadLetterDocument> .Empty) == 1);
            Assert.True(_deadLetterGraveyard.CountDocuments(FilterDefinition <MongoDeadLetterGraveyardDocument> .Empty) == 0);
            Assert.AreEqual(0, _testHelper.TestModel.MessageCount(DeadLetterTestHelper.TestDlQueueName));

            // Now run the host again with the FlushMessages option set
            _cliOptions.FlushMessages = true;
            host = new DeadLetterReprocessorHost(_testHelper.GlobalOptions, _cliOptions);
            host.Start();

            // Check the message has been sent back to the exchange and received by the consumer
            Assert.True(_deadLetterCollection.CountDocuments(FilterDefinition <MongoDeadLetterDocument> .Empty) == 0);
            Assert.True(_deadLetterGraveyard.CountDocuments(FilterDefinition <MongoDeadLetterGraveyardDocument> .Empty) == 0);
            new TestTimelineAwaiter().Await(() => _testHelper.MessageRejectorConsumer.AckCount == 1);

            IMessageHeader        reprocessedHeader = _testHelper.MessageRejectorConsumer.LastHeader;
            BasicDeliverEventArgs reprocessedArgs   = _testHelper.MessageRejectorConsumer.LastArgs;

            Assert.NotNull(reprocessedHeader);
            Assert.NotNull(reprocessedArgs);

            // Check the received message matches the original (where expected)
            Assert.AreNotEqual(originalHeader.MessageGuid, reprocessedHeader.MessageGuid);
            Assert.AreEqual(originalHeader.ProducerExecutableName, reprocessedHeader.ProducerExecutableName);
            Assert.AreEqual(originalHeader.ProducerProcessID, reprocessedHeader.ProducerProcessID);
            Assert.AreEqual(originalHeader.OriginalPublishTimestamp, reprocessedHeader.OriginalPublishTimestamp);
            Assert.True(reprocessedHeader.IsDescendantOf(originalHeader));

            // Check the xDeathHeaders
            var reprocessedXDeathHeaders = new RabbitMqXDeathHeaders(reprocessedArgs.BasicProperties.Headers, Encoding.UTF8);

            // Can't really get the expected time, just have to copy from the result
            long expectedTime = reprocessedXDeathHeaders.XDeaths[0].Time;

            var expectedXDeathHeaders = new RabbitMqXDeathHeaders
            {
                XDeaths = new List <RabbitMqXDeath>
                {
                    new RabbitMqXDeath
                    {
                        Count       = 1,
                        Exchange    = DeadLetterTestHelper.RejectExchangeName,
                        Queue       = DeadLetterTestHelper.RejectQueueName,
                        Reason      = "rejected",
                        RoutingKeys = new List <string>
                        {
                            DeadLetterTestHelper.TestRoutingKey
                        },
                        Time = expectedTime
                    }
                },
                XFirstDeathExchange = DeadLetterTestHelper.RejectExchangeName,
                XFirstDeathQueue    = DeadLetterTestHelper.RejectQueueName,
                XFirstDeathReason   = "rejected",
            };

            Assert.AreEqual(expectedXDeathHeaders, reprocessedXDeathHeaders);
        }
示例#15
0
        public void TestQueueFilter()
        {
            Assert.True(_deadLetterCollection.CountDocuments(FilterDefinition <MongoDeadLetterDocument> .Empty) == 0);
            Assert.True(_deadLetterGraveyard.CountDocuments(FilterDefinition <MongoDeadLetterGraveyardDocument> .Empty) == 0);

            // Setup a test message and send it, then wait for it to be rejected
            var testMessage = new AccessionDirectoryMessage
            {
                NationalPACSAccessionNumber = "1234",
                DirectoryPath = TestContext.CurrentContext.TestDirectory
            };

            _testHelper.TestProducer.SendMessage(testMessage, null, DeadLetterTestHelper.TestRoutingKey);
            new TestTimelineAwaiter().Await(() => _testHelper.MessageRejectorConsumer.NackCount == 1);

            IMessageHeader        originalHeader = _testHelper.MessageRejectorConsumer.LastHeader;
            BasicDeliverEventArgs originalArgs   = _testHelper.MessageRejectorConsumer.LastArgs;

            Assert.NotNull(originalHeader);
            Assert.NotNull(originalArgs);

            // Set so the next message is accepted
            _testHelper.MessageRejectorConsumer.AcceptNext = true;

            // Check 1 message on the DLQ
            Assert.AreEqual(1, _testHelper.TestModel.MessageCount(DeadLetterTestHelper.TestDlQueueName));

            _cliOptions.FlushMessages = false;

            // Start the host and check message has been read from DLQ into store
            var host = new DeadLetterReprocessorHost(_testHelper.GlobalOptions, _cliOptions);

            host.Start();

            Assert.True(_deadLetterCollection.CountDocuments(FilterDefinition <MongoDeadLetterDocument> .Empty) == 1);
            Assert.True(_deadLetterGraveyard.CountDocuments(FilterDefinition <MongoDeadLetterGraveyardDocument> .Empty) == 0);
            Assert.AreEqual(0, _testHelper.TestModel.MessageCount(DeadLetterTestHelper.TestDlQueueName));

            host.Stop("Test over 1");

            _cliOptions.FlushMessages      = true;
            _cliOptions.ReprocessFromQueue = "FakeQueueName";

            host = new DeadLetterReprocessorHost(_testHelper.GlobalOptions, _cliOptions);
            host.Start();

            Assert.True(_deadLetterCollection.CountDocuments(FilterDefinition <MongoDeadLetterDocument> .Empty) == 1);
            Assert.True(_deadLetterGraveyard.CountDocuments(FilterDefinition <MongoDeadLetterGraveyardDocument> .Empty) == 0);
            Assert.AreEqual(0, _testHelper.TestModel.MessageCount(DeadLetterTestHelper.RejectQueueName));

            host.Stop("Test over 2");

            _cliOptions.ReprocessFromQueue = DeadLetterTestHelper.RejectQueueName;

            host = new DeadLetterReprocessorHost(_testHelper.GlobalOptions, _cliOptions);
            host.Start();

            // Check the message has been sent back to the exchange and received by the consumer
            Assert.True(_deadLetterCollection.CountDocuments(FilterDefinition <MongoDeadLetterDocument> .Empty) == 0);
            Assert.True(_deadLetterGraveyard.CountDocuments(FilterDefinition <MongoDeadLetterGraveyardDocument> .Empty) == 0);
            new TestTimelineAwaiter().Await(() => _testHelper.MessageRejectorConsumer.AckCount == 1);

            host.Stop("Test over 3");
        }