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()); }
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"))); }
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>())); }
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); }
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)); }
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); }
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); }
/// <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; }
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>())); }
/// <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(); } }
protected abstract List <DicomFileMessage> ReadTagsImpl(IEnumerable <FileInfo> dicomFilePaths, AccessionDirectoryMessage accMessage);
/// <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)); } } } } } }
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); }
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); }
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"); }