public void ExecuteAsync_IfBlobDoesNotExist_ReturnsSuccessfulResult() { // Arrange BlobTriggerExecutorContext context = CreateExecutorContext(createBlob: false); IBlobPathSource input = CreateBlobPath(context.Blob); ITriggerExecutor <BlobTriggerExecutorContext> product = CreateProductUnderTest(input); // Act Task <FunctionResult> task = product.ExecuteAsync(context, CancellationToken.None); // Assert Assert.True(task.Result.Succeeded); // Validate log is written var logMessage = _loggerProvider.GetAllLogMessages().Single(); Assert.AreEqual("BlobHasNoETag", logMessage.EventId.Name); Assert.AreEqual(LogLevel.Debug, logMessage.Level); Assert.AreEqual(5, logMessage.State.Count()); Assert.AreEqual(context.Blob.BlobClient.Name, logMessage.GetStateValue <string>("blobName")); Assert.AreEqual("FunctionIdLogName", logMessage.GetStateValue <string>("functionName")); Assert.AreEqual(context.PollId, logMessage.GetStateValue <string>("pollId")); Assert.AreEqual(context.TriggerSource, logMessage.GetStateValue <BlobTriggerScanSource>("triggerSource")); Assert.True(!string.IsNullOrWhiteSpace(logMessage.GetStateValue <string>("{OriginalFormat}"))); }
private async Task NotifyRegistrationsAsync(BlobWithContainer <BlobBaseClient> blob, ICollection <BlobWithContainer <BlobBaseClient> > failedNotifications, CancellationToken cancellationToken) { // Blob written notifications are host-wide, so filter out things that aren't in the container list. if (!_registrations.ContainsKey(blob.BlobContainerClient)) { return; } foreach (ITriggerExecutor <BlobTriggerExecutorContext> registration in _registrations[blob.BlobContainerClient]) { cancellationToken.ThrowIfCancellationRequested(); BlobTriggerExecutorContext context = new BlobTriggerExecutorContext { Blob = blob, PollId = null, TriggerSource = BlobTriggerSource.ContainerScan }; FunctionResult result = await registration.ExecuteAsync(context, cancellationToken).ConfigureAwait(false); if (!result.Succeeded) { // If notification failed, try again on the next iteration. failedNotifications.Add(blob); } } }
public void ExecuteAsync_IfLeasedReceiptBecameCompleted_ReleasesLeaseAndReturnsSuccessResult() { // Arrange BlobTriggerExecutorContext context = CreateExecutorContext(); IBlobPathSource input = CreateBlobPath(context.Blob); Mock <IBlobReceiptManager> mock = CreateReceiptManagerReferenceMock(); int calls = 0; mock.Setup(m => m.TryReadAsync(It.IsAny <BlockBlobClient>(), It.IsAny <CancellationToken>())) .Returns(() => { int call = calls++; return(Task.FromResult(call == 0 ? BlobReceipt.Incomplete : BlobReceipt.Complete)); }); mock.Setup(m => m.TryAcquireLeaseAsync(It.IsAny <BlockBlobClient>(), It.IsAny <CancellationToken>())) .Returns(Task.FromResult("LeaseId")); mock.Setup(m => m.ReleaseLeaseAsync(It.IsAny <BlockBlobClient>(), It.IsAny <string>(), It.IsAny <CancellationToken>())) .Returns(Task.FromResult(0)) .Verifiable(); IBlobReceiptManager receiptManager = mock.Object; ITriggerExecutor <BlobTriggerExecutorContext> product = CreateProductUnderTest(input, receiptManager); // Act Task <FunctionResult> task = product.ExecuteAsync(context, CancellationToken.None); // Assert task.WaitUntilCompleted(); mock.Verify(); Assert.True(task.Result.Succeeded); }
public void ExecuteAsync_IfTryAcquireLeaseSucceeds_ReadsLatestReceipt() { // Arrange BlobTriggerExecutorContext context = CreateExecutorContext(); IBlobPathSource input = CreateBlobPath(context.Blob); Mock <IBlobReceiptManager> mock = CreateReceiptManagerReferenceMock(); int calls = 0; mock.Setup(m => m.TryReadAsync(It.IsAny <BlockBlobClient>(), It.IsAny <CancellationToken>())) .Returns(() => { return(Task.FromResult(calls++ == 0 ? BlobReceipt.Incomplete : BlobReceipt.Complete)); }); mock.Setup(m => m.TryAcquireLeaseAsync(It.IsAny <BlockBlobClient>(), It.IsAny <CancellationToken>())) .Returns(Task.FromResult("LeaseId")); mock.Setup(m => m.ReleaseLeaseAsync(It.IsAny <BlockBlobClient>(), It.IsAny <string>(), It.IsAny <CancellationToken>())) .Returns(Task.FromResult(0)); IBlobReceiptManager receiptManager = mock.Object; ITriggerExecutor <BlobTriggerExecutorContext> product = CreateProductUnderTest(input, receiptManager); // Act Task <FunctionResult> task = product.ExecuteAsync(context, CancellationToken.None); // Assert task.GetAwaiter().GetResult(); Assert.AreEqual(2, calls); }
public void ExecuteAsync_IfTryCreateReceiptSucceeds_TriesToAcquireLease() { // Arrange BlobTriggerExecutorContext context = CreateExecutorContext(); IBlobPathSource input = CreateBlobPath(context.Blob); Mock <IBlobReceiptManager> mock = CreateReceiptManagerReferenceMock(); mock.Setup(m => m.TryReadAsync(It.IsAny <BlockBlobClient>(), It.IsAny <CancellationToken>())) .Returns(Task.FromResult <BlobReceipt>(null)); mock.Setup(m => m.TryCreateAsync(It.IsAny <BlockBlobClient>(), It.IsAny <CancellationToken>())) .Returns(Task.FromResult(true)); mock.Setup(m => m.TryAcquireLeaseAsync(It.IsAny <BlockBlobClient>(), It.IsAny <CancellationToken>())) .Returns(Task.FromResult <string>(null)) .Verifiable(); IBlobReceiptManager receiptManager = mock.Object; ITriggerExecutor <BlobTriggerExecutorContext> product = CreateProductUnderTest(input, receiptManager); // Act Task <FunctionResult> task = product.ExecuteAsync(context, CancellationToken.None); // Assert task.GetAwaiter().GetResult(); mock.Verify(); }
public void ExecuteAsync_IfCompletedBlobReceiptExists_ReturnsSuccessfulResult() { // Arrange BlobTriggerExecutorContext context = CreateExecutorContext(); IBlobPathSource input = CreateBlobPath(context.Blob); string expectedETag = context.Blob.BlobClient.GetProperties().Value.ETag.ToString(); IBlobReceiptManager receiptManager = CreateCompletedReceiptManager(); ITriggerExecutor <BlobTriggerExecutorContext> product = CreateProductUnderTest(input, receiptManager); // Act Task <FunctionResult> task = product.ExecuteAsync(context, CancellationToken.None); // Assert Assert.True(task.Result.Succeeded); // Validate log is written var logMessage = _loggerProvider.GetAllLogMessages().Single(); Assert.AreEqual("BlobAlreadyProcessed", logMessage.EventId.Name); Assert.AreEqual(LogLevel.Debug, logMessage.Level); Assert.AreEqual(6, logMessage.State.Count()); Assert.AreEqual("FunctionIdLogName", logMessage.GetStateValue <string>("functionName")); Assert.AreEqual(context.Blob.BlobClient.Name, logMessage.GetStateValue <string>("blobName")); Assert.AreEqual(expectedETag, logMessage.GetStateValue <string>("eTag")); Assert.AreEqual(context.PollId, logMessage.GetStateValue <string>("pollId")); Assert.AreEqual(context.TriggerSource, logMessage.GetStateValue <BlobTriggerScanSource>("triggerSource")); Assert.True(!string.IsNullOrWhiteSpace(logMessage.GetStateValue <string>("{OriginalFormat}"))); }
private async Task NotifyRegistrationsAsync(BlobWithContainer <BlobBaseClient> blob, string pollId, CancellationToken cancellationToken) { // Log listening is client-wide and blob written notifications are host-wide, so filter out things that // aren't in the container list. if (!_registrations.ContainsKey(blob.BlobContainerClient)) { return; } foreach (ITriggerExecutor <BlobTriggerExecutorContext> registration in _registrations[blob.BlobContainerClient]) { cancellationToken.ThrowIfCancellationRequested(); BlobTriggerExecutorContext context = new BlobTriggerExecutorContext { Blob = blob, PollId = pollId, TriggerSource = BlobTriggerSource.LogScan }; FunctionResult result = await registration.ExecuteAsync(context, cancellationToken).ConfigureAwait(false); if (!result.Succeeded) { // If notification failed, try again on the next iteration. BlobNotification notification = new BlobNotification(blob, pollId); _blobsFoundFromScanOrNotification.Enqueue(notification); } } }
public void ExecuteAsync_IfEnqueueAsyncThrows_ReleasesLease() { // Arrange BlobTriggerExecutorContext context = CreateExecutorContext(); IBlobPathSource input = CreateBlobPath(context.Blob); InvalidOperationException expectedException = new InvalidOperationException(); Mock <IBlobReceiptManager> mock = CreateReceiptManagerReferenceMock(); mock.Setup(m => m.TryReadAsync(It.IsAny <BlockBlobClient>(), It.IsAny <CancellationToken>())) .Returns(Task.FromResult(BlobReceipt.Incomplete)); mock.Setup(m => m.TryAcquireLeaseAsync(It.IsAny <BlockBlobClient>(), It.IsAny <CancellationToken>())) .Returns(Task.FromResult("LeaseId")); mock.Setup(m => m.ReleaseLeaseAsync(It.IsAny <BlockBlobClient>(), It.IsAny <string>(), It.IsAny <CancellationToken>())) .Returns(Task.FromResult(0)) .Verifiable(); IBlobReceiptManager receiptManager = mock.Object; Mock <IBlobTriggerQueueWriter> queueWriterMock = new Mock <IBlobTriggerQueueWriter>(MockBehavior.Strict); queueWriterMock .Setup(w => w.EnqueueAsync(It.IsAny <BlobTriggerMessage>(), It.IsAny <CancellationToken>())) .Throws(expectedException); IBlobTriggerQueueWriter queueWriter = queueWriterMock.Object; ITriggerExecutor <BlobTriggerExecutorContext> product = CreateProductUnderTest(input, receiptManager, queueWriter); // Act Task <FunctionResult> task = product.ExecuteAsync(context, CancellationToken.None); // Assert task.WaitUntilCompleted(); mock.Verify(); InvalidOperationException exception = Assert.Throws <InvalidOperationException>( () => task.GetAwaiter().GetResult()); Assert.AreSame(expectedException, exception); }
public void ExecuteAsync_IfTryAcquireLeaseFails_ReturnsFailureResult() { // Arrange BlobTriggerExecutorContext context = CreateExecutorContext(); IBlobPathSource input = CreateBlobPath(context.Blob); Mock <IBlobReceiptManager> mock = CreateReceiptManagerReferenceMock(); mock.Setup(m => m.TryReadAsync(It.IsAny <BlockBlobClient>(), It.IsAny <CancellationToken>())) .Returns(Task.FromResult(BlobReceipt.Incomplete)); mock.Setup(m => m.TryAcquireLeaseAsync(It.IsAny <BlockBlobClient>(), It.IsAny <CancellationToken>())) .Returns(Task.FromResult <string>(null)); IBlobReceiptManager receiptManager = mock.Object; ITriggerExecutor <BlobTriggerExecutorContext> product = CreateProductUnderTest(input, receiptManager); // Act Task <FunctionResult> task = product.ExecuteAsync(context, CancellationToken.None); // Assert Assert.False(task.Result.Succeeded); }
public void ExecuteAsync_IfBlobDoesNotMatchPattern_ReturnsSuccessfulResult() { // Arrange string containerName = ContainerName; var container = _blobServiceClient.GetBlobContainerClient(containerName); var otherContainer = _blobServiceClient.GetBlobContainerClient("other"); IBlobPathSource input = BlobPathSource.Create(containerName + "/{name}"); ITriggerExecutor <BlobTriggerExecutorContext> product = CreateProductUnderTest(input); // Note: this test does not set the PollId. This ensures that we work okay with null values for these. var blob = otherContainer.GetBlockBlobClient("nonmatch"); var context = new BlobTriggerExecutorContext { Blob = new BlobWithContainer <BlobBaseClient>(otherContainer, blob), TriggerSource = BlobTriggerScanSource.ContainerScan }; // Act Task <FunctionResult> task = product.ExecuteAsync(context, CancellationToken.None); // Assert Assert.True(task.Result.Succeeded); // Validate log is written var logMessage = _loggerProvider.GetAllLogMessages().Single(); Assert.AreEqual("BlobDoesNotMatchPattern", logMessage.EventId.Name); Assert.AreEqual(LogLevel.Debug, logMessage.Level); Assert.AreEqual(6, logMessage.State.Count()); Assert.AreEqual("FunctionIdLogName", logMessage.GetStateValue <string>("functionName")); Assert.AreEqual(containerName + "/{name}", logMessage.GetStateValue <string>("pattern")); Assert.AreEqual(blob.Name, logMessage.GetStateValue <string>("blobName")); Assert.Null(logMessage.GetStateValue <string>("pollId")); Assert.AreEqual(context.TriggerSource, logMessage.GetStateValue <BlobTriggerScanSource>("triggerSource")); Assert.True(!string.IsNullOrWhiteSpace(logMessage.GetStateValue <string>("{OriginalFormat}"))); }
public void ExecuteAsync_IfLeasedIncompleteReceipt_EnqueuesMessageMarksCompletedReleasesLeaseAndReturnsSuccessResult() { // Arrange string expectedFunctionId = "FunctionId"; BlobTriggerExecutorContext context = CreateExecutorContext(); string expectedETag = context.Blob.BlobClient.GetProperties().Value.ETag.ToString(); IBlobPathSource input = CreateBlobPath(context.Blob); Mock <IBlobReceiptManager> managerMock = CreateReceiptManagerReferenceMock(); managerMock .Setup(m => m.TryReadAsync(It.IsAny <BlockBlobClient>(), It.IsAny <CancellationToken>())) .Returns(Task.FromResult(BlobReceipt.Incomplete)); managerMock .Setup(m => m.TryAcquireLeaseAsync(It.IsAny <BlockBlobClient>(), It.IsAny <CancellationToken>())) .Returns(Task.FromResult("LeaseId")); managerMock .Setup(m => m.MarkCompletedAsync(It.IsAny <BlockBlobClient>(), It.IsAny <string>(), It.IsAny <CancellationToken>())) .Returns(Task.FromResult(0)) .Verifiable(); managerMock .Setup(m => m.ReleaseLeaseAsync(It.IsAny <BlockBlobClient>(), It.IsAny <string>(), It.IsAny <CancellationToken>())) .Returns(Task.FromResult(0)) .Verifiable(); IBlobReceiptManager receiptManager = managerMock.Object; Mock <IBlobTriggerQueueWriter> queueWriterMock = new Mock <IBlobTriggerQueueWriter>(MockBehavior.Strict); queueWriterMock .Setup(w => w.EnqueueAsync(It.IsAny <BlobTriggerMessage>(), It.IsAny <CancellationToken>())) .Returns(Task.FromResult(("testQueueName", "testMessageId"))); IBlobTriggerQueueWriter queueWriter = queueWriterMock.Object; ITriggerExecutor <BlobTriggerExecutorContext> product = CreateProductUnderTest(expectedFunctionId, input, receiptManager, queueWriter); // Act Task <FunctionResult> task = product.ExecuteAsync(context, CancellationToken.None); // Assert task.WaitUntilCompleted(); queueWriterMock .Verify( w => w.EnqueueAsync(It.Is <BlobTriggerMessage>(m => m != null && m.FunctionId == expectedFunctionId /*&& m.BlobType == StorageBlobType.BlockBlob $$$ */ && m.BlobName == context.Blob.BlobClient.Name && m.ContainerName == context.Blob.BlobClient.BlobContainerName && m.ETag == expectedETag), It.IsAny <CancellationToken>()), Times.Once()); managerMock.Verify(); Assert.True(task.Result.Succeeded); // Validate log is written var logMessage = _loggerProvider.GetAllLogMessages().Single(); Assert.AreEqual("BlobMessageEnqueued", logMessage.EventId.Name); Assert.AreEqual(LogLevel.Debug, logMessage.Level); Assert.AreEqual(7, logMessage.State.Count()); Assert.AreEqual("FunctionIdLogName", logMessage.GetStateValue <string>("functionName")); Assert.AreEqual(context.Blob.BlobClient.Name, logMessage.GetStateValue <string>("blobName")); Assert.AreEqual("testQueueName", logMessage.GetStateValue <string>("queueName")); Assert.AreEqual("testMessageId", logMessage.GetStateValue <string>("messageId")); Assert.AreEqual(context.PollId, logMessage.GetStateValue <string>("pollId")); Assert.AreEqual(context.TriggerSource, logMessage.GetStateValue <BlobTriggerScanSource>("triggerSource")); Assert.True(!string.IsNullOrWhiteSpace(logMessage.GetStateValue <string>("{OriginalFormat}"))); }