public async Task ProcessFileAsync_Failure_RetriesAndLeavesFailureStatusFile() { string testFile = WriteTestFile("dat"); FunctionResult result = new FunctionResult(false); mockExecutor.Setup(p => p.TryExecuteAsync(It.IsAny<TriggeredFunctionData>(), It.IsAny<CancellationToken>())).ReturnsAsync(result); FileSystemEventArgs eventArgs = new FileSystemEventArgs(WatcherChangeTypes.Created, Path.GetDirectoryName(testFile), Path.GetFileName(testFile)); bool fileProcessedSuccessfully = await processor.ProcessFileAsync(eventArgs, CancellationToken.None); Assert.False(fileProcessedSuccessfully); Assert.True(File.Exists(testFile)); string statusFilePath = processor.GetStatusFile(testFile); StatusFileEntry[] entries = File.ReadAllLines(statusFilePath).Select(p => JsonConvert.DeserializeObject<StatusFileEntry>(p)).ToArray(); Assert.Equal(6, entries.Length); // verify each of the retries for (int i = 0; i < processor.MaxProcessCount; i++) { // verify the processing entry Assert.Equal(ProcessingState.Processing, entries[i * 2].State); Assert.Equal(i + 1, entries[i * 2].ProcessCount); // verify the failure entry Assert.Equal(ProcessingState.Failed, entries[(i * 2) + 1].State); Assert.Equal(i + 1, entries[(i * 2) + 1].ProcessCount); } }
protected override async Task ReleaseMessageAsync(CloudQueueMessage message, FunctionResult result, TimeSpan visibilityTimeout, CancellationToken cancellationToken) { //Use exponential backoff. Default implementation retries the operation way too quickly. visibilityTimeout = TimeSpan.FromSeconds(Math.Pow(3, message.DequeueCount)); await base.ReleaseMessageAsync(message, result, visibilityTimeout, cancellationToken); }
public void Constructor_Exception() { Exception exception = new Exception("Kaboom!"); FunctionResult result = new FunctionResult(exception); Assert.False(result.Succeeded); Assert.Same(exception, result.Exception); }
/// <summary> /// This method completes processing of the specified message, after the job function has been invoked. /// </summary> /// <param name="message">The message to complete processing for.</param> /// <param name="result">The <see cref="FunctionResult"/> from the job invocation.</param> /// <param name="cancellationToken">The <see cref="CancellationToken"/> to use</param> /// <returns></returns> public virtual async Task CompleteProcessingMessageAsync(BrokeredMessage message, FunctionResult result, CancellationToken cancellationToken) { if (!result.Succeeded) { cancellationToken.ThrowIfCancellationRequested(); await message.AbandonAsync(); } }
public virtual Task EndMessageArrivedAsync(string message, FunctionResult result, CancellationToken cancellationToken) { if (!result.Succeeded) { cancellationToken.ThrowIfCancellationRequested(); } return Task.FromResult(0); }
public async Task CompleteProcessingMessageAsync_DefaultOnMessageOptions() { MessageProcessor processor = new MessageProcessor(new OnMessageOptions()); BrokeredMessage message = new BrokeredMessage(); FunctionResult result = new FunctionResult(true); await processor.CompleteProcessingMessageAsync(message, result, CancellationToken.None); }
public void Constructor_Boolean() { FunctionResult result = new FunctionResult(true); Assert.True(result.Succeeded); Assert.Null(result.Exception); result = new FunctionResult(false); Assert.False(result.Succeeded); Assert.Null(result.Exception); }
public void Constructor_BooleanAndException() { Exception exception = new Exception("Kaboom!"); FunctionResult result = new FunctionResult(true, exception); Assert.True(result.Succeeded); Assert.Same(exception, result.Exception); result = new FunctionResult(false, exception); Assert.False(result.Succeeded); Assert.Same(exception, result.Exception); }
public async Task <FunctionResult> TryExecuteAsync(TriggeredFunctionData input, CancellationToken cancellationToken) { IFunctionInstance instance = _instanceFactory.Create((TTriggerValue)input.TriggerValue, input.ParentId); IDelayedException exception = await _executor.TryExecuteAsync(instance, cancellationToken); FunctionResult result = exception != null ? new FunctionResult(exception.Exception) : new FunctionResult(true); return(result); }
public async Task ProcessFileAsync_Failure_LeavesInProgressStatusFile() { string testFile = WriteTestFile("dat"); FunctionResult result = new FunctionResult(false); mockExecutor.Setup(p => p.TryExecuteAsync(It.IsAny<TriggeredFunctionData>(), It.IsAny<CancellationToken>())).ReturnsAsync(result); FileSystemEventArgs eventArgs = new FileSystemEventArgs(WatcherChangeTypes.Created, Path.GetDirectoryName(testFile), Path.GetFileName(testFile)); bool fileProcessedSuccessfully = await processor.ProcessFileAsync(eventArgs, CancellationToken.None); Assert.False(fileProcessedSuccessfully); Assert.True(File.Exists(testFile)); string statusFilePath = processor.GetStatusFile(testFile); StatusFileEntry entry = processor.GetLastStatus(statusFilePath); Assert.Equal(ProcessingState.Processing, entry.State); }
public async Task ProcessMessageAsync_Success() { BrokeredMessage message = new BrokeredMessage(); CancellationToken cancellationToken = new CancellationToken(); _mockMessageProcessor.Setup(p => p.BeginProcessingMessageAsync(message, cancellationToken)).ReturnsAsync(true); FunctionResult result = new FunctionResult(true); _mockExecutor.Setup(p => p.TryExecuteAsync(It.Is<TriggeredFunctionData>(q => q.TriggerValue == message), cancellationToken)).ReturnsAsync(result); _mockMessageProcessor.Setup(p => p.CompleteProcessingMessageAsync(message, result, cancellationToken)).Returns(Task.FromResult(0)); await _listener.ProcessMessageAsync(message, CancellationToken.None); _mockMessageProcessor.VerifyAll(); _mockExecutor.VerifyAll(); _mockMessageProcessor.VerifyAll(); }
/// <summary> /// This method completes processing of the specified message, after the job function has been invoked. /// </summary> /// <param name="message">The message to complete processing for.</param> /// <param name="result">The <see cref="FunctionResult"/> from the job invocation.</param> /// <param name="cancellationToken">The <see cref="CancellationToken"/> to use</param> /// <returns>A <see cref="Task"/> that will complete the message processing.</returns> public virtual async Task CompleteProcessingMessageAsync(BrokeredMessage message, FunctionResult result, CancellationToken cancellationToken) { if (result.Succeeded) { if (!MessageOptions.AutoComplete) { // AutoComplete is true by default, but if set to false // we need to complete the message cancellationToken.ThrowIfCancellationRequested(); await message.CompleteAsync(); } } else { cancellationToken.ThrowIfCancellationRequested(); await message.AbandonAsync(); } }
public async Task CompleteProcessingMessageAsync_Failure_AbandonsMessage() { OnMessageOptions options = new OnMessageOptions { AutoComplete = false }; MessageProcessor processor = new MessageProcessor(options); BrokeredMessage message = new BrokeredMessage(); FunctionResult result = new FunctionResult(false); var ex = await Assert.ThrowsAsync<InvalidOperationException>(async () => { await processor.CompleteProcessingMessageAsync(message, result, CancellationToken.None); }); // this verifies that we initiated the abandon Assert.True(ex.ToString().Contains("Microsoft.ServiceBus.Messaging.BrokeredMessage.BeginAbandon")); }
public async Task CompleteProcessingMessageAsync_Success_CompletesMessage_WhenAutoCompleteFalse() { OnMessageOptions options = new OnMessageOptions { AutoComplete = false }; MessageProcessor processor = new MessageProcessor(options); BrokeredMessage message = new BrokeredMessage(); FunctionResult result = new FunctionResult(true); var ex = await Assert.ThrowsAsync<InvalidOperationException>(async () => { await processor.CompleteProcessingMessageAsync(message, result, CancellationToken.None); }); // The service bus APIs aren't unit testable, so in this test suite // we rely on exception stacks to verify APIs are called as expected. // this verifies that we initiated the completion Assert.True(ex.ToString().Contains("Microsoft.ServiceBus.Messaging.BrokeredMessage.BeginComplete")); }
public override Task CompleteProcessingMessageAsync(CloudQueueMessage message, FunctionResult result, CancellationToken cancellationToken) { return base.CompleteProcessingMessageAsync(message, result, cancellationToken); }
public async Task ProcessFileAsync_ChangeTypeCreate_Success() { FileTriggerAttribute attribute = new FileTriggerAttribute(attributeSubPath, "*.dat"); processor = CreateTestProcessor(attribute); string testFile = WriteTestFile("dat"); FunctionResult result = new FunctionResult(true); mockExecutor.Setup(p => p.TryExecuteAsync(It.IsAny<TriggeredFunctionData>(), It.IsAny<CancellationToken>())).ReturnsAsync(result); string testFilePath = Path.GetDirectoryName(testFile); string testFileName = Path.GetFileName(testFile); FileSystemEventArgs eventArgs = new FileSystemEventArgs(WatcherChangeTypes.Created, testFilePath, testFileName); bool fileProcessedSuccessfully = await processor.ProcessFileAsync(eventArgs, CancellationToken.None); Assert.True(fileProcessedSuccessfully); string expectedStatusFile = processor.GetStatusFile(testFile); Assert.True(File.Exists(testFile)); Assert.True(File.Exists(expectedStatusFile)); string[] lines = File.ReadAllLines(expectedStatusFile); Assert.Equal(2, lines.Length); StatusFileEntry entry = (StatusFileEntry)_serializer.Deserialize(new StringReader(lines[0]), typeof(StatusFileEntry)); Assert.Equal(ProcessingState.Processing, entry.State); Assert.Equal(WatcherChangeTypes.Created, entry.ChangeType); Assert.Equal(InstanceId.Substring(0, 20), entry.InstanceId); entry = (StatusFileEntry)_serializer.Deserialize(new StringReader(lines[1]), typeof(StatusFileEntry)); Assert.Equal(ProcessingState.Processed, entry.State); Assert.Equal(WatcherChangeTypes.Created, entry.ChangeType); Assert.Equal(InstanceId.Substring(0, 20), entry.InstanceId); }
protected override async Task ReleaseMessageAsync(CloudQueueMessage message, FunctionResult result, TimeSpan visibilityTimeout, CancellationToken cancellationToken) { // demonstrates how visibility timeout for failed messages can be customized // the logic here could implement exponential backoff, etc. visibilityTimeout = TimeSpan.FromSeconds(message.DequeueCount); await base.ReleaseMessageAsync(message, result, visibilityTimeout, cancellationToken); }
public async Task ProcessFileAsync_JobFunctionSucceeds() { string testFile = WriteTestFile("dat"); FunctionResult result = new FunctionResult(true); mockExecutor.Setup(p => p.TryExecuteAsync(It.IsAny<TriggeredFunctionData>(), It.IsAny<CancellationToken>())).ReturnsAsync(result); FileSystemEventArgs eventArgs = new FileSystemEventArgs(WatcherChangeTypes.Created, combinedTestFilePath, Path.GetFileName(testFile)); await processor.ProcessFileAsync(eventArgs, CancellationToken.None); Assert.Equal(2, Directory.GetFiles(combinedTestFilePath).Length); string expectedStatusFile = processor.GetStatusFile(testFile); Assert.True(File.Exists(testFile)); Assert.True(File.Exists(expectedStatusFile)); string[] statusLines = File.ReadAllLines(expectedStatusFile); Assert.Equal(2, statusLines.Length); StatusFileEntry entry = (StatusFileEntry)_serializer.Deserialize(new StringReader(statusLines[0]), typeof(StatusFileEntry)); Assert.Equal(ProcessingState.Processing, entry.State); Assert.Equal(WatcherChangeTypes.Created, entry.ChangeType); Assert.Equal(processor.InstanceId, entry.InstanceId); entry = (StatusFileEntry)_serializer.Deserialize(new StringReader(statusLines[1]), typeof(StatusFileEntry)); Assert.Equal(ProcessingState.Processed, entry.State); Assert.Equal(WatcherChangeTypes.Created, entry.ChangeType); Assert.Equal(processor.InstanceId, entry.InstanceId); }
protected override async Task ReleaseMessageAsync(CloudQueueMessage message, FunctionResult result, TimeSpan visibilityTimeout, CancellationToken cancellationToken) { FakeStorageQueueMessage fakeMessage = new FakeStorageQueueMessage(message); await _queue.UpdateMessageAsync(fakeMessage, visibilityTimeout, MessageUpdateFields.Visibility, cancellationToken); }
/// <summary> /// Release the specified failed message back to the queue. /// </summary> /// <param name="message">The message to release</param> /// <param name="result">The <see cref="FunctionResult"/> from the job invocation.</param> /// <param name="visibilityTimeout">The visibility timeout to set for the message</param> /// <param name="cancellationToken">The <see cref="CancellationToken"/> to use</param> /// <returns></returns> protected virtual async Task ReleaseMessageAsync(CloudQueueMessage message, FunctionResult result, TimeSpan visibilityTimeout, CancellationToken cancellationToken) { try { // We couldn't process the message. Let someone else try. await _queue.UpdateMessageAsync(message, visibilityTimeout, MessageUpdateFields.Visibility, cancellationToken); } catch (StorageException exception) { if (exception.IsBadRequestPopReceiptMismatch()) { // Someone else already took over the message; no need to do anything. return; } else if (exception.IsNotFoundMessageOrQueueNotFound() || exception.IsConflictQueueBeingDeletedOrDisabled()) { // The message or queue is gone, or the queue is down; no need to release the message. return; } else { throw; } } }
public void ExecuteAsync_IfInnerExecutorFails_ReturnsFailureResult() { // Arrange string functionId = "FunctionId"; string matchingETag = "ETag"; IBlobETagReader eTagReader = CreateStubETagReader(matchingETag); IBlobCausalityReader causalityReader = CreateStubCausalityReader(); FunctionResult expectedResult = new FunctionResult(false); Mock<ITriggeredFunctionExecutor> mock = new Mock<ITriggeredFunctionExecutor>(MockBehavior.Strict); mock.Setup(e => e.TryExecuteAsync( It.IsAny<TriggeredFunctionData>(), It.IsAny<CancellationToken>())) .ReturnsAsync(expectedResult) .Verifiable(); BlobQueueTriggerExecutor product = CreateProductUnderTest(eTagReader, causalityReader); ITriggeredFunctionExecutor innerExecutor = mock.Object; BlobQueueRegistration registration = new BlobQueueRegistration { BlobClient = CreateClient(), Executor = innerExecutor }; product.Register(functionId, registration); IStorageQueueMessage message = CreateMessage(functionId, matchingETag); // Act Task<FunctionResult> task = product.ExecuteAsync(message, CancellationToken.None); // Assert Assert.False(task.Result.Succeeded); }
/// <summary> /// This method completes processing of the specified message, after the job function has been invoked. /// </summary> /// <remarks> /// If the message was processed successfully, the message should be deleted. If message processing failed, the /// message should be release back to the queue, or if the maximum dequeue count has been exceeded, the message /// should be moved to the poison queue (if poison queue handling is configured for the queue). /// </remarks> /// <param name="message">The message to complete processing for.</param> /// <param name="result">The <see cref="FunctionResult"/> from the job invocation.</param> /// <param name="cancellationToken">The <see cref="CancellationToken"/> to use</param> /// <returns></returns> public virtual async Task CompleteProcessingMessageAsync(CloudQueueMessage message, FunctionResult result, CancellationToken cancellationToken) { if (result.Succeeded) { await DeleteMessageAsync(message, cancellationToken); } else if (_poisonQueue != null) { if (message.DequeueCount >= _maxDequeueCount) { await CopyMessageToPoisonQueueAsync(message, cancellationToken); await DeleteMessageAsync(message, cancellationToken); } else { await ReleaseMessageAsync(message, result, TimeSpan.Zero, cancellationToken); } } else { // For queues without a corresponding poison queue, leave the message invisible when processing // fails to prevent a fast infinite loop. // Specifically, don't call ReleaseMessage(message) } }
public async Task ExecuteAsync_IfBlobIsUnchanged_CallsInnerExecutor() { // Arrange string functionId = "FunctionId"; string matchingETag = "ETag"; Guid expectedParentId = Guid.NewGuid(); IStorageQueueMessage message = CreateMessage(functionId, matchingETag); IBlobETagReader eTagReader = CreateStubETagReader(matchingETag); IBlobCausalityReader causalityReader = CreateStubCausalityReader(expectedParentId); FunctionResult expectedResult = new FunctionResult(true); Mock<ITriggeredFunctionExecutor> mock = new Mock<ITriggeredFunctionExecutor>(MockBehavior.Strict); mock.Setup(e => e.TryExecuteAsync(It.IsAny<TriggeredFunctionData>(), It.IsAny<CancellationToken>())) .Callback<TriggeredFunctionData, CancellationToken>( (mockInput, mockCancellationToken) => { Assert.Equal(expectedParentId, mockInput.ParentId); StorageBlockBlob resultBlob = (StorageBlockBlob)mockInput.TriggerValue; Assert.Equal(TestBlobName, resultBlob.Name); }) .ReturnsAsync(expectedResult) .Verifiable(); ITriggeredFunctionExecutor innerExecutor = mock.Object; BlobQueueTriggerExecutor product = CreateProductUnderTest(eTagReader, causalityReader); BlobQueueRegistration registration = new BlobQueueRegistration { BlobClient = CreateClient(), Executor = innerExecutor }; product.Register(functionId, registration); // Act FunctionResult result = await product.ExecuteAsync(message, CancellationToken.None); // Assert Assert.Same(expectedResult, result); mock.Verify(); }
public async Task ProcessMessageAsync_FunctionInvocationFails() { CancellationToken cancellationToken = new CancellationToken(); FunctionResult result = new FunctionResult(false); _mockQueueProcessor.Setup(p => p.BeginProcessingMessageAsync(_storageMessage.SdkObject, cancellationToken)).ReturnsAsync(true); _mockTriggerExecutor.Setup(p => p.ExecuteAsync(_storageMessage, cancellationToken)).ReturnsAsync(result); _mockQueueProcessor.Setup(p => p.CompleteProcessingMessageAsync(_storageMessage.SdkObject, result, cancellationToken)).Returns(Task.FromResult(true)); await _listener.ProcessMessageAsync(_storageMessage, TimeSpan.FromMinutes(10), cancellationToken); }
public override Task CompleteProcessingMessageAsync(BrokeredMessage message, FunctionResult result, CancellationToken cancellationToken) { // perform any post processing after the job function has been invoked return base.CompleteProcessingMessageAsync(message, result, cancellationToken); }
/// <summary> /// Gets the next retry interval to use. /// </summary> /// <param name="result">The <see cref="FunctionResult"/> for the last failure.</param> /// <param name="count">The current execution count (the number of times the function has /// been invoked).</param> /// <returns>A <see cref="TimeSpan"/> representing the delay interval that should be used.</returns> protected virtual TimeSpan GetRetryInterval(FunctionResult result, int count) { if (result == null) { throw new ArgumentNullException("result"); } return TimeSpan.FromSeconds(3); }
private void CreateTestListener(string expression, bool useMonitor = true) { _attribute = new TimerTriggerAttribute(expression); _attribute.UseMonitor = useMonitor; _config = new TimersConfiguration(); _mockScheduleMonitor = new Mock<ScheduleMonitor>(MockBehavior.Strict); _config.ScheduleMonitor = _mockScheduleMonitor.Object; _mockTriggerExecutor = new Mock<ITriggeredFunctionExecutor>(MockBehavior.Strict); FunctionResult result = new FunctionResult(true); _mockTriggerExecutor.Setup(p => p.TryExecuteAsync(It.IsAny<TriggeredFunctionData>(), It.IsAny<CancellationToken>())) .Callback<TriggeredFunctionData, CancellationToken>((mockFunctionData, mockToken) => { _triggeredFunctionData = mockFunctionData; }) .Returns(Task.FromResult(result)); _listener = new TimerListener(_attribute, _testTimerName, _config, _mockTriggerExecutor.Object, new TestTraceWriter()); }
public async Task ConcurrentListeners_ProcessFilesCorrectly(int concurrentListenerCount, int inputFileCount) { // mock out the executor so we can capture function invocations Mock<ITriggeredFunctionExecutor> mockExecutor = new Mock<ITriggeredFunctionExecutor>(MockBehavior.Strict); ConcurrentBag<string> processedFiles = new ConcurrentBag<string>(); FunctionResult result = new FunctionResult(true); mockExecutor.Setup(p => p.TryExecuteAsync(It.IsAny<TriggeredFunctionData>(), It.IsAny<CancellationToken>())) .Callback<TriggeredFunctionData, CancellationToken>(async (mockData, mockToken) => { await Task.Delay(50); FileSystemEventArgs fileEvent = mockData.TriggerValue as FileSystemEventArgs; processedFiles.Add(fileEvent.Name); }) .ReturnsAsync(result); FilesConfiguration config = new FilesConfiguration() { RootPath = rootPath }; FileTriggerAttribute attribute = new FileTriggerAttribute(attributeSubPath, changeTypes: WatcherChangeTypes.Created | WatcherChangeTypes.Changed, filter: "*.dat"); // create a bunch of listeners and start them CancellationTokenSource tokenSource = new CancellationTokenSource(); CancellationToken cancellationToken = tokenSource.Token; List<Task> listenerStartupTasks = new List<Task>(); List<FileListener> listeners = new List<FileListener>(); for (int i = 0; i < concurrentListenerCount; i++) { FileListener listener = new FileListener(config, attribute, mockExecutor.Object, new TestTraceWriter()); listeners.Add(listener); listenerStartupTasks.Add(listener.StartAsync(cancellationToken)); }; await Task.WhenAll(listenerStartupTasks); // now start creating files List<string> expectedFiles = new List<string>(); for (int i = 0; i < inputFileCount; i++) { string file = WriteTestFile(); await Task.Delay(50); expectedFiles.Add(Path.GetFileName(file)); } // wait for all files to be processed await TestHelpers.Await(() => { return processedFiles.Count >= inputFileCount; }); Assert.Equal(inputFileCount, processedFiles.Count); // verify that each file was only processed once Assert.True(expectedFiles.OrderBy(p => p).SequenceEqual(processedFiles.OrderBy(p => p))); Assert.Equal(expectedFiles.Count * 2, Directory.GetFiles(testFileDir).Length); // verify contents of each status file FileProcessor processor = listeners[0].Processor; foreach (string processedFile in processedFiles) { string statusFilePath = processor.GetStatusFile(Path.Combine(testFileDir, processedFile)); string[] statusLines = File.ReadAllLines(statusFilePath); Assert.Equal(2, statusLines.Length); StatusFileEntry statusEntry = JsonConvert.DeserializeObject<StatusFileEntry>(statusLines[0]); Assert.Equal(ProcessingState.Processing, statusEntry.State); Assert.Equal(WatcherChangeTypes.Created, statusEntry.ChangeType); statusEntry = JsonConvert.DeserializeObject<StatusFileEntry>(statusLines[1]); Assert.Equal(ProcessingState.Processed, statusEntry.State); Assert.Equal(WatcherChangeTypes.Created, statusEntry.ChangeType); } // Now test concurrency handling for updates by updating some files // and verifying the updates are only processed once string[] filesToUpdate = processedFiles.Take(50).Select(p => Path.Combine(testFileDir, p)).ToArray(); string item; while (!processedFiles.IsEmpty) { processedFiles.TryTake(out item); } await Task.Delay(1000); foreach (string fileToUpdate in filesToUpdate) { await Task.Delay(50); File.AppendAllText(fileToUpdate, "update"); } // wait for all files to be processed await TestHelpers.Await(() => { return processedFiles.Count >= filesToUpdate.Length; }); Assert.Equal(filesToUpdate.Length, processedFiles.Count); Assert.Equal(expectedFiles.Count * 2, Directory.GetFiles(testFileDir).Length); // verify the status files are correct for each of the updated files foreach (string updatedFile in filesToUpdate) { string statusFilePath = processor.GetStatusFile(updatedFile); string[] statusLines = File.ReadAllLines(statusFilePath); Assert.Equal(4, statusLines.Length); StatusFileEntry statusEntry = JsonConvert.DeserializeObject<StatusFileEntry>(statusLines[0]); Assert.Equal(ProcessingState.Processing, statusEntry.State); Assert.Equal(WatcherChangeTypes.Created, statusEntry.ChangeType); statusEntry = JsonConvert.DeserializeObject<StatusFileEntry>(statusLines[1]); Assert.Equal(ProcessingState.Processed, statusEntry.State); Assert.Equal(WatcherChangeTypes.Created, statusEntry.ChangeType); statusEntry = JsonConvert.DeserializeObject<StatusFileEntry>(statusLines[2]); Assert.Equal(ProcessingState.Processing, statusEntry.State); Assert.Equal(WatcherChangeTypes.Changed, statusEntry.ChangeType); statusEntry = JsonConvert.DeserializeObject<StatusFileEntry>(statusLines[3]); Assert.Equal(ProcessingState.Processed, statusEntry.State); Assert.Equal(WatcherChangeTypes.Changed, statusEntry.ChangeType); } // Now call purge to clean up all processed files processor.CleanupProcessedFiles(); Assert.Equal(0, Directory.GetFiles(testFileDir).Length); foreach (FileListener listener in listeners) { listener.Dispose(); } }
public void ExecuteAsync_IfInnerExecutorSucceeds_ReturnsSuccessResult() { // Arrange string functionId = "FunctionId"; string matchingETag = "ETag"; IBlobETagReader eTagReader = CreateStubETagReader(matchingETag); IBlobCausalityReader causalityReader = CreateStubCausalityReader(); FunctionResult expectedResult = new FunctionResult(true); Mock<ITriggeredFunctionExecutor<IStorageBlob>> mock = new Mock<ITriggeredFunctionExecutor<IStorageBlob>>(MockBehavior.Strict); mock.Setup(e => e.TryExecuteAsync( It.IsAny<TriggeredFunctionData<IStorageBlob>>(), It.IsAny<CancellationToken>())) .ReturnsAsync(expectedResult) .Verifiable(); BlobQueueTriggerExecutor product = CreateProductUnderTest(eTagReader, causalityReader); ITriggeredFunctionExecutor<IStorageBlob> innerExecutor = mock.Object; product.Register(functionId, innerExecutor); IStorageQueueMessage message = CreateMessage(functionId, matchingETag); // Act Task<FunctionResult> task = product.ExecuteAsync(message, CancellationToken.None); // Assert Assert.Same(expectedResult, task.Result); }