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_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.Equal("BlobHasNoETag", logMessage.EventId.Name);
            Assert.Equal(LogLevel.Debug, logMessage.Level);
            Assert.Equal(5, logMessage.State.Count());
            Assert.Equal(context.Blob.BlobClient.Name, logMessage.GetStateValue <string>("blobName"));
            Assert.Equal("FunctionIdLogName", logMessage.GetStateValue <string>("functionName"));
            Assert.Equal(context.PollId, logMessage.GetStateValue <string>("pollId"));
            Assert.Equal(context.TriggerSource, logMessage.GetStateValue <BlobTriggerSource>("triggerSource"));
            Assert.True(!string.IsNullOrWhiteSpace(logMessage.GetStateValue <string>("{OriginalFormat}")));
        }
        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.Equal(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.Equal("BlobAlreadyProcessed", logMessage.EventId.Name);
            Assert.Equal(LogLevel.Debug, logMessage.Level);
            Assert.Equal(6, logMessage.State.Count());
            Assert.Equal("FunctionIdLogName", logMessage.GetStateValue <string>("functionName"));
            Assert.Equal(context.Blob.BlobClient.Name, logMessage.GetStateValue <string>("blobName"));
            Assert.Equal(expectedETag, logMessage.GetStateValue <string>("eTag"));
            Assert.Equal(context.PollId, logMessage.GetStateValue <string>("pollId"));
            Assert.Equal(context.TriggerSource, logMessage.GetStateValue <BlobTriggerSource>("triggerSource"));
            Assert.True(!string.IsNullOrWhiteSpace(logMessage.GetStateValue <string>("{OriginalFormat}")));
        }
        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.Same(expectedException, exception);
        }
        public void ExecuteAsync_IfBlobDoesNotMatchPattern_ReturnsSuccessfulResult()
        {
            // Arrange
            var    client         = account.CreateBlobServiceClient();
            string containerName  = ContainerName;
            var    container      = client.GetBlobContainerClient(containerName);
            var    otherContainer = client.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 = BlobTriggerSource.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.Equal("BlobDoesNotMatchPattern", logMessage.EventId.Name);
            Assert.Equal(LogLevel.Debug, logMessage.Level);
            Assert.Equal(6, logMessage.State.Count());
            Assert.Equal("FunctionIdLogName", logMessage.GetStateValue <string>("functionName"));
            Assert.Equal(containerName + "/{name}", logMessage.GetStateValue <string>("pattern"));
            Assert.Equal(blob.Name, logMessage.GetStateValue <string>("blobName"));
            Assert.Null(logMessage.GetStateValue <string>("pollId"));
            Assert.Equal(context.TriggerSource, logMessage.GetStateValue <BlobTriggerSource>("triggerSource"));
            Assert.True(!string.IsNullOrWhiteSpace(logMessage.GetStateValue <string>("{OriginalFormat}")));
        }
        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_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.Equal("BlobMessageEnqueued", logMessage.EventId.Name);
            Assert.Equal(LogLevel.Debug, logMessage.Level);
            Assert.Equal(7, logMessage.State.Count());
            Assert.Equal("FunctionIdLogName", logMessage.GetStateValue <string>("functionName"));
            Assert.Equal(context.Blob.BlobClient.Name, logMessage.GetStateValue <string>("blobName"));
            Assert.Equal("testQueueName", logMessage.GetStateValue <string>("queueName"));
            Assert.Equal("testMessageId", logMessage.GetStateValue <string>("messageId"));
            Assert.Equal(context.PollId, logMessage.GetStateValue <string>("pollId"));
            Assert.Equal(context.TriggerSource, logMessage.GetStateValue <BlobTriggerSource>("triggerSource"));
            Assert.True(!string.IsNullOrWhiteSpace(logMessage.GetStateValue <string>("{OriginalFormat}")));
        }