public async Task TimerExpiration()
        {
            string[] orchestratorFunctionNames =
            {
                nameof(TestOrchestrations.Approval)
            };
            using (JobHost host = TestHelpers.GetJobHost(this.loggerFactory, nameof(TimerExpiration)))
            {
                await host.StartAsync();

                var timeout = TimeSpan.FromSeconds(10);
                var client  = await host.StartOrchestratorAsync(orchestratorFunctionNames[0], timeout, this.output);

                // Need to wait for the instance to start before sending events to it.
                // TODO: This requirement may not be ideal and should be revisited.
                // BUG: https://github.com/Azure/azure-functions-durable-extension/issues/101
                await client.WaitForStartupAsync(TimeSpan.FromSeconds(10), this.output);

                // Don't send any notification - let the internal timeout expire

                var status = await client.WaitForCompletionAsync(TimeSpan.FromSeconds(20), this.output);

                Assert.Equal(OrchestrationRuntimeStatus.Completed, status?.RuntimeStatus);
                Assert.Equal("Expired", status?.Output);

                await host.StopAsync();
            }

            if (this.useTestLogger)
            {
                TestHelpers.AssertLogMessageSequence(loggerProvider, "TimerExpiration", orchestratorFunctionNames);
            }
        }
        public async Task TerminateOrchestration()
        {
            string[] orchestratorFunctionNames =
            {
                nameof(TestOrchestrations.Counter)
            };

            using (JobHost host = TestHelpers.GetJobHost(this.loggerFactory, nameof(TerminateOrchestration)))
            {
                await host.StartAsync();

                // Using the counter orchestration because it will wait indefinitely for input.
                var client = await host.StartOrchestratorAsync(orchestratorFunctionNames[0], 0, this.output);

                // Need to wait for the instance to start before we can terminate it.
                // TODO: This requirement may not be ideal and should be revisited.
                // BUG: https://github.com/Azure/azure-functions-durable-extension/issues/101
                await client.WaitForStartupAsync(TimeSpan.FromSeconds(10), this.output);

                await client.TerminateAsync("sayōnara");

                var status = await client.WaitForCompletionAsync(TimeSpan.FromSeconds(10), this.output);

                Assert.Equal(OrchestrationRuntimeStatus.Terminated, status?.RuntimeStatus);
                Assert.Equal("sayōnara", status?.Output);

                await host.StopAsync();
            }

            if (this.useTestLogger)
            {
                TestHelpers.AssertLogMessageSequence(loggerProvider, "TerminateOrchestration",
                                                     orchestratorFunctionNames);
            }
        }
        public async Task OrchestrationConcurrency()
        {
            using (JobHost host = GetJobHost())
            {
                await host.StartAsync();

                Func <Task> orchestrationStarter = async delegate()
                {
                    var timeout = TimeSpan.FromSeconds(10);
                    var client  = await host.StartFunctionAsync(nameof(Orchestrations.Approval), timeout, this.output);

                    await client.WaitForCompletionAsync(TimeSpan.FromSeconds(20), this.output);

                    // Don't send any notification - let the internal timeout expire
                };

                int iterations = 100;
                var tasks      = new Task[iterations];
                for (int i = 0; i < iterations; i++)
                {
                    tasks[i] = orchestrationStarter();
                }

                // The 100 orchestrations above (which each delay for 10 seconds) should all complete in less than 30 seconds.
                Task parallelOrchestrations = Task.WhenAll(tasks);
                Task timeoutTask            = Task.Delay(TimeSpan.FromSeconds(30));

                Task winner = await Task.WhenAny(parallelOrchestrations, timeoutTask);

                Assert.Equal(parallelOrchestrations, winner);

                await host.StopAsync();
            }
        }
コード例 #4
0
        public async Task AggregatorOnly()
        {
            using (_functionCompletedEvent = new ManualResetEvent(initialState: false))
            {
                // enable the aggregator
                _hostConfig.Aggregator.IsEnabled = true;
                _hostConfig.Aggregator.BatchSize = 1;

                JobHost host = new JobHost(_hostConfig);

                await host.StartAsync();

                await host.CallAsync(typeof(AsyncChainEndToEndTests).GetMethod("WriteStartDataMessageToQueue"));

                await WaitForFunctionCompleteAsync();

                // ensure all logs have had a chance to flush
                await Task.Delay(3000);

                await host.StopAsync();

                // Make sure the aggregator was logged to
                var logger = _loggerProvider.CreatedLoggers.Where(l => l.Category == LogCategories.Aggregator).Single();
                var count  = logger.GetLogMessages().Count;

                Assert.True(count == 4, $"Expected 4. Actual {count}.{Environment.NewLine}{_loggerProvider.GetLogString()}");
            }
        }
コード例 #5
0
        public async void JobIsTriggeredForNewFiles()
        {
            JobHost host = CreateTestJobHost();
            await host.StartAsync();

            int count = ApiHubFileTestJobs.Processed.Count;

            // now write a file to trigger the job
            var fileItem = await _fixture.WriteTestFile();

            await TestHelpers.Await(() =>
            {
                return(_fixture.RootFolder.FileExistsAsync(fileItem.Path).GetAwaiter().GetResult());
            });

            await TestHelpers.Await(() =>
            {
                return(ApiHubFileTestJobs.Processed.Count > count);
            });

            Assert.True(ApiHubFileTestJobs.Processed.Contains(Path.GetFileName(fileItem.Path)));

            await host.StopAsync();

            await fileItem.DeleteAsync();
        }
        public async Task SequentialOrchestration()
        {
            using (JobHost host = TestHelpers.GetJobHost(this.loggerFactory, nameof(SequentialOrchestration)))
            {
                await host.StartAsync();

                var client = await host.StartOrchestratorAsync(nameof(TestOrchestrations.Factorial), 10, this.output);

                var status = await client.WaitForCompletionAsync(TimeSpan.FromSeconds(30), this.output);

                Assert.Equal(OrchestrationRuntimeStatus.Completed, status?.RuntimeStatus);
                Assert.Equal(10, status?.Input);
                Assert.Equal(3628800, status?.Output);

                await host.StopAsync();
            }

            // Assert log entry count
            if (this.useTestLogger)
            {
                var logger      = loggerProvider.CreatedLoggers.Single(l => l.Category == TestHelpers.LogCategory);
                var logMessages = logger.LogMessages.ToList();
                Assert.Equal(153, logMessages.Count);
            }
        }
コード例 #7
0
        private async Task AsyncChainEndToEndInternal()
        {
            _resolver = new RandomNameResolver();

            JobHostConfiguration hostConfiguration = new JobHostConfiguration()
            {
                NameResolver = _resolver,
                TypeLocator  = new FakeTypeLocator(typeof(AsyncChainEndToEndTests))
            };

            _storageAccount = CloudStorageAccount.Parse(hostConfiguration.StorageConnectionString);

            JobHost host = new JobHost(hostConfiguration);

            await host.StartAsync();

            await host.CallAsync(typeof(AsyncChainEndToEndTests).GetMethod("WriteStartDataMessageToQueue"));

            _functionCompletedEvent.WaitOne();

            // Stop async waits for the function to complete
            await host.StopAsync();

            await host.CallAsync(typeof(AsyncChainEndToEndTests).GetMethod("ReadResultBlob"));

            Assert.Equal("async works", _finalBlobContent);
        }
        public async Task UnhandledOrchestrationExceptionWithRetry()
        {
            string[] orchestratorFunctionNames =
            {
                nameof(TestOrchestrations.OrchestratorThrowWithRetry)
            };

            using (JobHost host =
                       TestHelpers.GetJobHost(loggerFactory, nameof(UnhandledOrchestrationExceptionWithRetry)))
            {
                await host.StartAsync();

                // Null input should result in ArgumentNullException in the orchestration code.
                var client = await host.StartOrchestratorAsync(orchestratorFunctionNames[0], null, this.output);

                var status = await client.WaitForCompletionAsync(TimeSpan.FromSeconds(50), this.output);

                Assert.Equal(OrchestrationRuntimeStatus.Failed, status?.RuntimeStatus);
                Assert.True(status?.Output.ToString().Contains("Value cannot be null"));

                await host.StopAsync();
            }

            if (this.useTestLogger)
            {
                TestHelpers.UnhandledOrchesterationExceptionWithRetry_AssertLogMessageSequence(loggerProvider, "UnhandledOrchestrationExceptionWithRetry",
                                                                                               orchestratorFunctionNames);
            }
        }
コード例 #9
0
        public async void JobIsTriggeredForNewFiles()
        {
            JobHost host = CreateTestJobHost();

            await host.StartAsync();

            Assert.Equal(0, FilesTestJobs.Processed.Count);

            // now write a file to trigger the job
            string testFilePath = WriteTestFile();

            await Task.Delay(2000);

            Assert.Equal(1, FilesTestJobs.Processed.Count);
            Assert.Equal(Path.GetFileName(testFilePath), FilesTestJobs.Processed.Single());
            Assert.True(File.Exists(testFilePath));

            // write a non .dat file - don't expect it to trigger the job
            string ignoreFilePath = WriteTestFile("txt");

            await Task.Delay(2000);

            Assert.Equal(1, FilesTestJobs.Processed.Count);
            Assert.True(File.Exists(ignoreFilePath));

            await host.StopAsync();
        }
コード例 #10
0
        private async Task IndexBindings(Type testType, bool includeDefaultUri = true)
        {
            // Just start the jobhost -- this should fail if function indexing fails.

            ExplicitTypeLocator locator = new ExplicitTypeLocator(testType);
            var nameResolver            = new TestNameResolver();

            if (includeDefaultUri)
            {
                nameResolver.Values.Add(MobileAppsConfiguration.AzureWebJobsMobileAppUriName, "https://default");
            }
            JobHostConfiguration config = new JobHostConfiguration
            {
                NameResolver = nameResolver,
                TypeLocator  = locator,
            };

            config.UseMobileApps();

            JobHost host = new JobHost(config);

            await host.StartAsync();

            await host.StopAsync();
        }
        private Tuple <JobHost, IHost> BuildHost <T>()
        {
            JobHost jobHost = null;

            var config = new ConfigurationBuilder()
                         .AddEnvironmentVariables()
                         .AddTestSettings()
                         .Build();

            const string connectionName = "AzureWebJobsTestHubConnection";
            string       connection     = config.GetConnectionStringOrSetting(connectionName);

            Assert.True(!string.IsNullOrEmpty(connection), $"Required test connection string '{connectionName}' is missing.");

            IHost host = new HostBuilder()
                         .ConfigureDefaultTestHost <T>(b =>
            {
                b.AddEventHubs(options =>
                {
                    options.EventProcessorOptions.EnableReceiverRuntimeMetric = true;
                    options.AddSender(TestHubName, connection);
                    options.AddReceiver(TestHubName, connection);
                });
            })
                         .ConfigureLogging(b =>
            {
                b.SetMinimumLevel(LogLevel.Debug);
            })
                         .Build();

            jobHost = host.GetJobHost();
            jobHost.StartAsync().GetAwaiter().GetResult();

            return(new Tuple <JobHost, IHost>(jobHost, host));
        }
コード例 #12
0
        public async Task AggregatorOnly()
        {
            using (_functionCompletedEvent = new ManualResetEvent(initialState: false))
            {
                // enable the aggregator
                _hostConfig.Aggregator.IsEnabled = true;
                _hostConfig.Aggregator.BatchSize = 1;

                JobHost host = new JobHost(_hostConfig);

                await host.StartAsync();

                await host.CallAsync(typeof(AsyncChainEndToEndTests).GetMethod("WriteStartDataMessageToQueue"));

                await TestHelpers.Await(() => _functionCompletedEvent.WaitOne(200), 30000);

                // ensure all logs have had a chance to flush
                await Task.Delay(3000);

                await host.StopAsync();

                // Make sure the aggregator was logged to
                var logger = _loggerProvider.CreatedLoggers.Where(l => l.Category == LogCategories.Aggregator).Single();
                Assert.Equal(4, logger.LogMessages.Count);
            }
        }
        public async Task HelloWorldOrchestration_Activity()
        {
            string[] orchestratorFunctionNames =
            {
                nameof(TestOrchestrations.SayHelloWithActivity)
            };
            string activityFunctionName = nameof(TestActivities.Hello);

            using (JobHost host = TestHelpers.GetJobHost(this.loggerFactory, nameof(HelloWorldOrchestration_Activity)))
            {
                await host.StartAsync();

                var client = await host.StartOrchestratorAsync(orchestratorFunctionNames[0], "World", this.output);

                var status = await client.WaitForCompletionAsync(TimeSpan.FromSeconds(30), this.output);

                Assert.Equal(OrchestrationRuntimeStatus.Completed, status?.RuntimeStatus);
                Assert.Equal("World", status?.Input);
                Assert.Equal("Hello, World!", status?.Output);

                await host.StopAsync();
            }

            if (this.useTestLogger)
            {
                TestHelpers.AssertLogMessageSequence(loggerProvider, "HelloWorldOrchestration_Activity",
                                                     orchestratorFunctionNames, activityFunctionName);
            }
        }
コード例 #14
0
        public async Task ExistingFilesAreBatchProcessedOnStartup()
        {
            JobHost host = CreateTestJobHost();

            // create a bunch of preexisting files
            List <string> filesToProcess       = new List <string>();
            int           preexistingFileCount = 10;

            for (int i = 0; i < preexistingFileCount; i++)
            {
                string testFilePath = WriteTestFile();
                filesToProcess.Add(Path.GetFileName(testFilePath));
            }

            // write a non .dat file - don't expect it to be processed
            WriteTestFile("txt");

            await host.StartAsync();

            await TestHelpers.Await(() =>
            {
                return(FilesTestJobs.Processed.Count == preexistingFileCount);
            });

            Assert.True(FilesTestJobs.Processed.OrderBy(p => p).SequenceEqual(filesToProcess.OrderBy(p => p)));

            await host.StopAsync();
        }
        public async Task BigReturnValue_Activity()
        {
            string taskHub = nameof(BigReturnValue_Activity);

            using (JobHost host = TestHelpers.GetJobHost(this.loggerFactory, taskHub))
            {
                await host.StartAsync();

                var orchestrator = nameof(TestOrchestrations.CallActivity);
                var timeout      = Debugger.IsAttached ? TimeSpan.FromMinutes(5) : TimeSpan.FromSeconds(30);

                // The expected maximum payload size is 60 KB.
                // Strings in Azure Storage are encoded in UTF-32, which is 4 bytes per character.
                int stringLength = (61 * 1024) / 4;
                var input        = new StartOrchestrationArgs
                {
                    FunctionName = nameof(TestActivities.BigReturnValue),
                    Input        = stringLength
                };

                var client = await host.StartOrchestratorAsync(orchestrator, input, this.output);

                var status = await client.WaitForCompletionAsync(timeout, this.output);

                Assert.Equal(OrchestrationRuntimeStatus.Failed, status?.RuntimeStatus);

                // Activity function exception details are not captured in the orchestrator output:
                // https://github.com/Azure/azure-functions-durable-extension/issues/84
                ////Assert.True(status?.Output.ToString().Contains("The UTF-32 size of the JSON-serialized payload must not exceed 60 KB"));
                Assert.StartsWith($"The activity function '{input.FunctionName}' failed.", (string)status?.Output);

                await host.StopAsync();
            }
        }
        public async Task BigReturnValue_Orchestrator()
        {
            string taskHub = nameof(BigReturnValue_Orchestrator);

            using (JobHost host = TestHelpers.GetJobHost(this.loggerFactory, taskHub))
            {
                await host.StartAsync();

                var orchestrator = nameof(TestOrchestrations.BigReturnValue);
                var timeout      = Debugger.IsAttached ? TimeSpan.FromMinutes(5) : TimeSpan.FromSeconds(30);

                // The expected maximum payload size is 60 KB.
                // Strings in Azure Storage are encoded in UTF-32, which is 4 bytes per character.
                int stringLength = (61 * 1024) / 4;

                var client = await host.StartOrchestratorAsync(orchestrator, stringLength, this.output);

                var status = await client.WaitForCompletionAsync(timeout, this.output);

                Assert.Equal(OrchestrationRuntimeStatus.Failed, status?.RuntimeStatus);
                Assert.True(status?.Output.ToString().Contains("The UTF-32 size of the JSON-serialized payload must not exceed 60 KB"));

                await host.StopAsync();
            }
        }
        public async Task UnhandledActivityExceptionWithRetry()
        {
            string[] orchestratorFunctionNames =
            {
                nameof(TestOrchestrations.ActivityThrowWithRetry)
            };
            string activityFunctionName = nameof(TestActivities.Throw);

            using (JobHost host = TestHelpers.GetJobHost(loggerFactory, nameof(UnhandledActivityExceptionWithRetry)))
            {
                await host.StartAsync();

                string message = "Kah-BOOOOM!!!";
                var    client  = await host.StartOrchestratorAsync(orchestratorFunctionNames[0], message, this.output);

                var status = await client.WaitForCompletionAsync(TimeSpan.FromSeconds(40), this.output);

                Assert.Equal(OrchestrationRuntimeStatus.Failed, status?.RuntimeStatus);

                // There aren't any exception details in the output: https://github.com/Azure/azure-functions-durable-extension/issues/84
                Assert.StartsWith($"The activity function '{activityFunctionName}' failed.", (string)status?.Output);

                await host.StopAsync();
            }

            if (this.useTestLogger)
            {
                TestHelpers.AssertLogMessageSequence(loggerProvider, "UnhandledActivityExceptionWithRetry",
                                                     orchestratorFunctionNames, activityFunctionName);
            }
        }
コード例 #18
0
        public async Task EventCollectorOnly()
        {
            using (_functionCompletedEvent = new ManualResetEvent(initialState: false))
            {
                // disable the aggregator
                _hostConfig.Aggregator.IsEnabled = false;

                // add a FunctionEventCollector
                var eventCollector = new TestFunctionEventCollector();
                _hostConfig.AddService <IAsyncCollector <FunctionInstanceLogEntry> >(eventCollector);

                JobHost host = new JobHost(_hostConfig);

                await host.StartAsync();

                await host.CallAsync(typeof(AsyncChainEndToEndTests).GetMethod("WriteStartDataMessageToQueue"));

                await WaitForFunctionCompleteAsync();

                // ensure all logs have had a chance to flush
                await Task.Delay(3000);

                await host.StopAsync();

                // Make sure the aggregator was logged to
                var logger = _loggerProvider.CreatedLoggers.Where(l => l.Category == LogCategories.Aggregator).SingleOrDefault();
                Assert.Null(logger);

                // Make sure the eventCollector was logged
                eventCollector.AssertFunctionCount(4, _loggerProvider.GetLogString());
            }
        }
コード例 #19
0
        public async Task FunctionTraceLevelOverride_ProducesExpectedOutput()
        {
            TestTraceWriter trace = new TestTraceWriter(TraceLevel.Verbose);

            _hostConfig.Tracing.Tracers.Add(trace);
            JobHost host = new JobHost(_hostConfig);

            try
            {
                using (_functionCompletedEvent = new ManualResetEvent(initialState: false))
                {
                    await host.StartAsync();

                    CloudQueueMessage message = new CloudQueueMessage("test message");
                    _testQueue.AddMessage(message);

                    _functionCompletedEvent.WaitOne();

                    // wait for logs to flush
                    await Task.Delay(3000);

                    // expect no function output
                    TraceEvent[] traces = trace.Traces.ToArray();
                    Assert.Equal(5, traces.Length);
                    Assert.False(traces.Any(p => p.Message.Contains("test message")));
                }
            }
            finally
            {
                host.Stop();
            }
        }
コード例 #20
0
        public async Task ActivityTriggerAsPOCO()
        {
            using (JobHost host = TestHelpers.GetJobHost(this.loggerFactory, nameof(this.ActivityTriggerAsPOCO)))
            {
                await host.StartAsync();

                // Using StartOrchestrationArgs to start an activity function because it's easier than creating a new type.
                var startArgs = new StartOrchestrationArgs();
                startArgs.FunctionName = nameof(TestActivities.BindToPOCO);

                var input = new { Foo = "Bar" };
                startArgs.Input = input;

                var timeout = Debugger.IsAttached ? TimeSpan.FromMinutes(5) : TimeSpan.FromSeconds(30);
                var client  = await host.StartOrchestratorAsync(nameof(TestOrchestrations.CallActivity), startArgs, this.output);

                var status = await client.WaitForCompletionAsync(timeout, this.output);

                // The function echos back the 'Foo' input property value
                Assert.Equal(OrchestrationRuntimeStatus.Completed, status?.RuntimeStatus);
                Assert.Equal(input.Foo, status?.Output);

                await host.StopAsync();
            }
        }
コード例 #21
0
        private async Task RunTimeoutTest(IWebJobsExceptionHandler exceptionHandler, Type expectedExceptionType, string functionName)
        {
            TestTraceWriter trace = new TestTraceWriter(TraceLevel.Verbose);

            _hostConfig.Tracing.Tracers.Add(trace);
            _hostConfig.AddService <IWebJobsExceptionHandler>(exceptionHandler);
            JobHost host = new JobHost(_hostConfig);

            try
            {
                await host.StartAsync();

                MethodInfo methodInfo = GetType().GetMethod(functionName);
                Exception  ex         = await Assert.ThrowsAnyAsync <Exception>(async() =>
                {
                    await host.CallAsync(methodInfo);
                });

                Assert.IsType(expectedExceptionType, ex);
            }
            finally
            {
                host.Stop();
            }

            // We expect 3 error messages total
            TraceEvent[] traceErrors = trace.Traces.Where(p => p.Level == TraceLevel.Error).ToArray();
            Assert.Equal(3, traceErrors.Length);
            Assert.True(traceErrors[0].Message.StartsWith(string.Format("Timeout value of 00:00:01 exceeded by function 'AsyncChainEndToEndTests.{0}'", functionName)));
            Assert.True(traceErrors[1].Message.StartsWith(string.Format("Executed: 'AsyncChainEndToEndTests.{0}' (Failed)", functionName)));
            Assert.True(traceErrors[2].Message.Trim().StartsWith("Function had errors. See Azure WebJobs SDK dashboard for details."));
        }
コード例 #22
0
        public void StartAsync_WhenStopping_Throws()
        {
            // Arrange
            using (JobHost host = new JobHost(CreateConfiguration()))
            {
                host.Start();

                // Replace (and cleanup) the exsiting runner to hook StopAsync.
                IListener oldListener = host.Listener;
                oldListener.StopAsync(CancellationToken.None).GetAwaiter().GetResult();

                TaskCompletionSource <object> stopTaskSource = new TaskCompletionSource <object>();
                Mock <IListener> listenerMock = new Mock <IListener>(MockBehavior.Strict);
                listenerMock.Setup(r => r.StopAsync(It.IsAny <CancellationToken>())).Returns(stopTaskSource.Task);
                listenerMock.Setup(r => r.Dispose());
                host.Listener = listenerMock.Object;

                Task stopping = host.StopAsync();

                // Act & Assert
                ExceptionAssert.ThrowsInvalidOperation(() => host.StartAsync(), "Start has already been called.");

                // Cleanup
                stopTaskSource.SetResult(null);
                stopping.GetAwaiter().GetResult();
            }
        }
コード例 #23
0
        public async Task BindToBlobViaPOCO()
        {
            using (JobHost host = TestHelpers.GetJobHost(this.loggerFactory, nameof(this.ActivityTriggerAsNumber)))
            {
                await host.StartAsync();

                string connectionString     = AmbientConnectionStringProvider.Instance.GetConnectionString(ConnectionStringNames.Storage);
                CloudStorageAccount account = CloudStorageAccount.Parse(connectionString);
                this.output.WriteLine($"Using storage account: {account.Credentials.AccountName}");

                // Blob and container names need to be kept in sync with the activity code.
                var data = new
                {
                    InputPrefix  = "Input",
                    OutputPrefix = "Output",
                    Suffix       = 42,
                };

                const string ContainerName  = "test";
                string       inputBlobName  = $"{data.InputPrefix}-{data.Suffix}";
                string       outputBlobName = $"{data.OutputPrefix}-{data.Suffix}";

                CloudBlobClient    blobClient = account.CreateCloudBlobClient();
                CloudBlobContainer container  = blobClient.GetContainerReference(ContainerName);
                if (await container.CreateIfNotExistsAsync())
                {
                    this.output.WriteLine($"Created container '{container.Name}'.");
                }

                string randomData = Guid.NewGuid().ToString("N");

                this.output.WriteLine($"Creating blob named {outputBlobName}...");
                CloudBlockBlob blob = container.GetBlockBlobReference(inputBlobName);
                await blob.UploadTextAsync(randomData);

                this.output.WriteLine($"Uploaded text '{randomData}' to {blob.Name}.");

                // Using StartOrchestrationArgs to start an activity function because it's easier than creating a new type.
                var startArgs = new StartOrchestrationArgs();
                startArgs.FunctionName = nameof(TestActivities.BindToBlobViaJsonPayload);
                startArgs.Input        = data;

                var timeout = Debugger.IsAttached ? TimeSpan.FromMinutes(5) : TimeSpan.FromSeconds(30);
                var client  = await host.StartOrchestratorAsync(nameof(TestOrchestrations.CallActivity), startArgs, this.output);

                var status = await client.WaitForCompletionAsync(timeout, this.output);

                Assert.Equal(OrchestrationRuntimeStatus.Completed, status?.RuntimeStatus);

                this.output.WriteLine($"Searching for blob named {outputBlobName}...");
                CloudBlockBlob newBlob    = container.GetBlockBlobReference(outputBlobName);
                string         copiedData = await newBlob.DownloadTextAsync();

                this.output.WriteLine($"Downloaded text '{copiedData}' from {newBlob.Name}.");

                Assert.Equal(randomData, copiedData);

                await host.StopAsync();
            }
        }
コード例 #24
0
        public async Task ApplicationInsights_SuccessfulFunction()
        {
            string            testName = nameof(TestApplicationInsightsInformation);
            LogCategoryFilter filter   = new LogCategoryFilter();

            filter.DefaultLevel = LogLevel.Information;

            var loggerFactory = new LoggerFactory()
                                .AddApplicationInsights(
                new TestTelemetryClientFactory(filter.Filter, _channel));

            JobHostConfiguration config = new JobHostConfiguration
            {
                LoggerFactory = loggerFactory,
                TypeLocator   = new FakeTypeLocator(GetType()),
            };

            config.Aggregator.IsEnabled = false;
            config.AddService <IWebJobsExceptionHandler>(new TestExceptionHandler());

            using (JobHost host = new JobHost(config))
            {
                await host.StartAsync();

                var methodInfo = GetType().GetMethod(testName, BindingFlags.Public | BindingFlags.Static);
                await host.CallAsync(methodInfo, new { input = "function input" });

                await host.StopAsync();
            }

            Assert.Equal(7, _channel.Telemetries.Count);

            // Validate the traces. Order by message string as the requests may come in
            // slightly out-of-order or on different threads
            TraceTelemetry[] telemetries = _channel.Telemetries
                                           .OfType <TraceTelemetry>()
                                           .OrderBy(t => t.Message)
                                           .ToArray();

            ValidateTrace(telemetries[0], "Found the following functions:\r\n", LogCategories.Startup);
            ValidateTrace(telemetries[1], "Job host started", LogCategories.Startup);
            ValidateTrace(telemetries[2], "Job host stopped", LogCategories.Startup);
            ValidateTrace(telemetries[3], "Logger", LogCategories.Function, testName, hasCustomScope: true);
            ValidateTrace(telemetries[4], "Trace", LogCategories.Function, testName);

            // We should have 1 custom metric.
            MetricTelemetry metric = _channel.Telemetries
                                     .OfType <MetricTelemetry>()
                                     .Single();

            ValidateMetric(metric, testName);

            // Finally, validate the request
            RequestTelemetry request = _channel.Telemetries
                                       .OfType <RequestTelemetry>()
                                       .Single();

            ValidateRequest(request, testName, true);
        }
コード例 #25
0
        public async Task Generate_EndToEnd()
        {
            // construct our TimerTrigger attribute ([TimerTrigger("00:00:02", RunOnStartup = true)])
            Collection <ParameterDescriptor> parameters = new Collection <ParameterDescriptor>();
            ParameterDescriptor    parameter            = new ParameterDescriptor("timerInfo", typeof(TimerInfo));
            ConstructorInfo        ctorInfo             = typeof(TimerTriggerAttribute).GetConstructor(new Type[] { typeof(string) });
            PropertyInfo           runOnStartupProperty = typeof(TimerTriggerAttribute).GetProperty("RunOnStartup");
            CustomAttributeBuilder attributeBuilder     = new CustomAttributeBuilder(
                ctorInfo,
                new object[] { "00:00:02" },
                new PropertyInfo[] { runOnStartupProperty },
                new object[] { true });

            parameter.CustomAttributes.Add(attributeBuilder);
            parameters.Add(parameter);

            // create the FunctionDefinition
            FunctionMetadata   metadata = new FunctionMetadata();
            TestInvoker        invoker  = new TestInvoker();
            FunctionDescriptor function = new FunctionDescriptor("TimerFunction", invoker, metadata, parameters, null, null, null);
            Collection <FunctionDescriptor> functions = new Collection <FunctionDescriptor>();

            functions.Add(function);

            // Get the Type Attributes (in this case, a TimeoutAttribute)
            ScriptHostConfiguration scriptConfig = new ScriptHostConfiguration();

            scriptConfig.FunctionTimeout = TimeSpan.FromMinutes(5);
            Collection <CustomAttributeBuilder> typeAttributes = new Collection <CustomAttributeBuilder>();

            // generate the Type
            Type functionType = FunctionGenerator.Generate("TestScriptHost", "TestFunctions", typeAttributes, functions);

            // verify the generated function
            MethodInfo            method           = functionType.GetMethod("TimerFunction");
            ParameterInfo         triggerParameter = method.GetParameters()[0];
            TimerTriggerAttribute triggerAttribute = triggerParameter.GetCustomAttribute <TimerTriggerAttribute>();

            Assert.NotNull(triggerAttribute);

            // start the JobHost which will start running the timer function
            JobHostConfiguration config = new JobHostConfiguration()
            {
                TypeLocator   = new TypeLocator(functionType),
                LoggerFactory = new LoggerFactory()
            };

            config.UseTimers();
            JobHost host = new JobHost(config);

            await host.StartAsync();

            await Task.Delay(3000);

            await host.StopAsync();

            // verify our custom invoker was called
            Assert.True(invoker.InvokeCount >= 2);
        }
        public async Task ActorOrchestration()
        {
            using (JobHost host = GetJobHost())
            {
                await host.StartAsync();

                int initialValue = 0;
                var client       = await host.StartFunctionAsync(nameof(Orchestrations.Counter), initialValue, this.output);

                // Need to wait for the instance to start before sending events to it.
                // TODO: This requirement may not be ideal and should be revisited.
                // BUG: https://github.com/Azure/azure-webjobs-sdk-script-pr/issues/37
                await client.WaitForStartupAsync(TimeSpan.FromSeconds(10), this.output);

                // Perform some operations
                await client.RaiseEventAsync("operation", "incr");

                // TODO: Sleeping to avoid a race condition where multiple ContinueAsNew messages
                //       are processed by the same instance at the same time, resulting in a corrupt
                //       storage failure in DTFx.
                // BUG: https://github.com/Azure/azure-webjobs-sdk-script-pr/issues/38
                await Task.Delay(2000);

                await client.RaiseEventAsync("operation", "incr");

                await Task.Delay(2000);

                await client.RaiseEventAsync("operation", "incr");

                await Task.Delay(2000);

                await client.RaiseEventAsync("operation", "decr");

                await Task.Delay(2000);

                await client.RaiseEventAsync("operation", "incr");

                await Task.Delay(2000);

                // Make sure it's still running and didn't complete early (or fail).
                var status = await client.GetStatusAsync();

                Assert.Equal("Running", status?.RuntimeStatus);

                // The end message will cause the actor to complete itself.
                await client.RaiseEventAsync("operation", "end");

                status = await client.WaitForCompletionAsync(TimeSpan.FromSeconds(10), this.output);

                Assert.Equal("Completed", status?.RuntimeStatus);
                Assert.Equal(3, (int)status?.Output);

                // When using ContinueAsNew, the original input is discarded and replaced with the most recent state.
                Assert.NotEqual(initialValue, status?.Input);

                await host.StopAsync();
            }
        }
        public async Task SubOrchestration_ComplexType()
        {
            const string TaskHub = nameof(SubOrchestration_ComplexType);

            using (JobHost host = TestHelpers.GetJobHost(this.loggerFactory, TaskHub))
            {
                await host.StartAsync();

                var complexTypeDataInput = new ComplexType
                {
                    A = -42,
                    B = new List <DateTime> {
                        DateTime.UtcNow, DateTime.UtcNow.AddYears(1)
                    },
                    C = ComplexType.CustomEnum.Value2,
                    D = new ComplexType.ComplexInnerType
                    {
                        E = Guid.NewGuid().ToString(),
                        F = TimeSpan.FromHours(1.5),
                    },
                };

                var input = new StartOrchestrationArgs
                {
                    FunctionName = nameof(TestOrchestrations.CallActivity),
                    Input        = new StartOrchestrationArgs
                    {
                        FunctionName = nameof(TestActivities.Echo),
                        Input        = complexTypeDataInput,
                    },
                };

                string parentOrchestrator = nameof(TestOrchestrations.CallOrchestrator);

                var client = await host.StartOrchestratorAsync(parentOrchestrator, input, this.output);

                var status = await client.WaitForCompletionAsync(
                    Debugger.IsAttached?TimeSpan.FromMinutes(5) : TimeSpan.FromSeconds(20),
                    this.output);

                Assert.NotNull(status);
                Assert.Equal(OrchestrationRuntimeStatus.Completed, status.RuntimeStatus);
                Assert.Equal(client.InstanceId, status.InstanceId);

                Assert.NotNull(status.Output);
                ComplexType complextTypeDataOutput = status.Output.ToObject <ComplexType>();
                Assert.NotNull(complextTypeDataOutput);
                Assert.Equal(complexTypeDataInput.A, complextTypeDataOutput.A);
                Assert.Equal(complexTypeDataInput.B[0], complextTypeDataOutput.B[0]);
                Assert.Equal(complexTypeDataInput.B[1], complextTypeDataOutput.B[1]);
                Assert.NotNull(complextTypeDataOutput.D);
                Assert.Equal(complexTypeDataInput.D.E, complextTypeDataOutput.D.E);
                Assert.Equal(complexTypeDataInput.D.F, complextTypeDataOutput.D.F);

                await host.StopAsync();
            }
        }
コード例 #28
0
 public void StartAsync_WhenNotStarted_DoesNotThrow()
 {
     // Arrange
     using (JobHost host = new JobHost(new OptionsWrapper <JobHostOptions>(new JobHostOptions()), new Mock <IJobHostContextFactory>().Object))
     {
         // Act & Assert
         host.StartAsync().GetAwaiter().GetResult();
     }
 }
コード例 #29
0
 public void StartAsync_WhenNotStarted_DoesNotThrow()
 {
     // Arrange
     using (JobHost host = new JobHost(CreateConfiguration()))
     {
         // Act & Assert
         host.StartAsync().GetAwaiter().GetResult();
     }
 }
コード例 #30
0
 public void StartAsync_WhenNotStarted_DoesNotThrow()
 {
     // Arrange
     using (JobHost host = new JobHost(CreateConfiguration()))
     {
         // Act & Assert
         host.StartAsync().GetAwaiter().GetResult();
     }
 }
コード例 #31
0
        public async Task Generate_EndToEnd()
        {
            // construct our TimerTrigger attribute ([TimerTrigger("00:00:02", RunOnStartup = true)])
            Collection<ParameterDescriptor> parameters = new Collection<ParameterDescriptor>();
            ParameterDescriptor parameter = new ParameterDescriptor("timerInfo", typeof(TimerInfo));
            ConstructorInfo ctorInfo = typeof(TimerTriggerAttribute).GetConstructor(new Type[] { typeof(string) });
            PropertyInfo runOnStartupProperty = typeof(TimerTriggerAttribute).GetProperty("RunOnStartup");
            CustomAttributeBuilder attributeBuilder = new CustomAttributeBuilder(
                ctorInfo,
                new object[] { "00:00:02" },
                new PropertyInfo[] { runOnStartupProperty },
                new object[] { true });
            parameter.CustomAttributes.Add(attributeBuilder);
            parameters.Add(parameter);

            // create the FunctionDefinition
            FunctionMetadata metadata = new FunctionMetadata();
            TestInvoker invoker = new TestInvoker();
            FunctionDescriptor function = new FunctionDescriptor("TimerFunction", invoker, metadata, parameters);
            Collection<FunctionDescriptor> functions = new Collection<FunctionDescriptor>();
            functions.Add(function);

            // Get the Type Attributes (in this case, a TimeoutAttribute)
            ScriptHostConfiguration scriptConfig = new ScriptHostConfiguration();
            scriptConfig.FunctionTimeout = TimeSpan.FromMinutes(5);
            Collection<CustomAttributeBuilder> typeAttributes = ScriptHost.CreateTypeAttributes(scriptConfig);

            // generate the Type
            Type functionType = FunctionGenerator.Generate("TestScriptHost", "TestFunctions", typeAttributes, functions);

            // verify the generated function
            MethodInfo method = functionType.GetMethod("TimerFunction");
            TimeoutAttribute timeoutAttribute = (TimeoutAttribute)functionType.GetCustomAttributes().Single();
            Assert.Equal(TimeSpan.FromMinutes(5), timeoutAttribute.Timeout);
            Assert.True(timeoutAttribute.ThrowOnTimeout);
            Assert.True(timeoutAttribute.TimeoutWhileDebugging);
            ParameterInfo triggerParameter = method.GetParameters()[0];
            TimerTriggerAttribute triggerAttribute = triggerParameter.GetCustomAttribute<TimerTriggerAttribute>();
            Assert.NotNull(triggerAttribute);

            // start the JobHost which will start running the timer function
            JobHostConfiguration config = new JobHostConfiguration()
            {
                TypeLocator = new TypeLocator(functionType)
            };
            config.UseTimers();
            JobHost host = new JobHost(config);

            await host.StartAsync();
            await Task.Delay(3000);
            await host.StopAsync();

            // verify our custom invoker was called
            Assert.True(invoker.InvokeCount >= 2);
        }
コード例 #32
0
        // $$$ Reconcile with TestJobHost.

        internal static async Task <TResult> RunTriggerAsync <TResult>(
            StorageAccount account,
            Type programType,
            Action <TaskCompletionSource <TResult> > setTaskSource,
            IEnumerable <string> ignoreFailureFunctions = null,
            bool signalOnFirst = false)
        {
            TaskCompletionSource <TResult> src = new TaskCompletionSource <TResult>();

            setTaskSource(src);

            var host = new HostBuilder()
                       .ConfigureDefaultTestHost(builder =>
            {
                builder.AddAzureStorageBlobs().AddAzureStorageQueues()
                .UseStorage(account)
                .ConfigureCatchFailures(src, signalOnFirst, ignoreFailureFunctions);

                builder.Services.AddSingleton <IConfigureOptions <QueuesOptions>, FakeQueuesOptionsSetup>();
            }, programType)
                       .Build();

            try
            {
                using (JobHost jobHost = host.GetJobHost())
                {
                    try
                    {
                        // start listeners. One of them will set the completition task
                        await jobHost.StartAsync();

                        var result = await src.Task.AwaitWithTimeout(); // blocks

                        return(result);
                    } finally
                    {
                        await jobHost.StopAsync();
                    }
                }
            }
            catch (Exception exception)
            {
                // Unwrap
                var e = exception;
                while (e != null)
                {
                    if (e is InvalidOperationException)
                    {
                        throw e;
                    }
                    e = e.InnerException;
                }
                throw;
            }
        }
コード例 #33
0
        public void StartAsync_WhenStarted_Throws()
        {
            // Arrange
            using (JobHost host = new JobHost(CreateConfiguration()))
            {
                host.Start();

                // Act & Assert
                ExceptionAssert.ThrowsInvalidOperation(() => host.StartAsync(), "Start has already been called.");
            }
        }
コード例 #34
0
        public async Task MultipleAccountTest()
        {
            try
            {
                TestTraceWriter trace = new TestTraceWriter(TraceLevel.Info);
                _serviceBusConfig = new ServiceBusConfiguration();
                _serviceBusConfig.MessagingProvider = new CustomMessagingProvider(_serviceBusConfig, trace);

                JobHostConfiguration config = new JobHostConfiguration()
                {
                    NameResolver = _nameResolver,
                    TypeLocator = new FakeTypeLocator(typeof(ServiceBusTestJobs))
                };
                config.Tracing.Tracers.Add(trace);
                config.UseServiceBus(_serviceBusConfig);
                JobHost host = new JobHost(config);

                string queueName = ResolveName(StartQueueName);
                string queuePrefix = queueName.Replace("-queue-start", "");
                string firstTopicName = string.Format("{0}-topic/Subscriptions/{0}-queue-topic-1", queuePrefix);

                WriteQueueMessage(_secondaryNamespaceManager, _secondaryConnectionString, queueName, "Test");

                _topicSubscriptionCalled1 = new ManualResetEvent(initialState: false);

                await host.StartAsync();

                _topicSubscriptionCalled1.WaitOne(SBTimeout);

                // ensure all logs have had a chance to flush
                await Task.Delay(3000);

                // Wait for the host to terminate
                await host.StopAsync();
                host.Dispose();

                Assert.Equal("Test-topic-1", _resultMessage1);
            }
            finally
            {
                Cleanup();
            }
        }
コード例 #35
0
        public void StartAsync_WhenStarting_Throws()
        {
            // Arrange
            TaskCompletionSource<IStorageAccount> getAccountTaskSource = new TaskCompletionSource<IStorageAccount>();
            TestJobHostConfiguration configuration = CreateConfiguration(new LambdaStorageAccountProvider(
                    (i1, i2) => getAccountTaskSource.Task));

            using (JobHost host = new JobHost(configuration))
            {
                Task starting = host.StartAsync();
                Assert.False(starting.IsCompleted); // Guard

                // Act & Assert
                ExceptionAssert.ThrowsInvalidOperation(() => host.StartAsync(), "Start has already been called.");

                // Cleanup
                getAccountTaskSource.SetResult(null);
                starting.GetAwaiter().GetResult();
            }
        }
コード例 #36
0
        public void StartAsync_WhenStopping_Throws()
        {
            // Arrange
            using (JobHost host = new JobHost(CreateConfiguration()))
            {
                host.Start();

                // Replace (and cleanup) the exsiting runner to hook StopAsync.
                IListener oldListener = host.Listener;
                oldListener.StopAsync(CancellationToken.None).GetAwaiter().GetResult();

                TaskCompletionSource<object> stopTaskSource = new TaskCompletionSource<object>();
                Mock<IListener> listenerMock = new Mock<IListener>(MockBehavior.Strict);
                listenerMock.Setup(r => r.StopAsync(It.IsAny<CancellationToken>())).Returns(stopTaskSource.Task);
                listenerMock.Setup(r => r.Dispose());
                host.Listener = listenerMock.Object;

                Task stopping = host.StopAsync();

                // Act & Assert
                ExceptionAssert.ThrowsInvalidOperation(() => host.StartAsync(), "Start has already been called.");

                // Cleanup
                stopTaskSource.SetResult(null);
                stopping.GetAwaiter().GetResult();
            }
        }
コード例 #37
0
        public void CallCancellationToken_WhenUsingTriggeredFunction_DoesNotTriggerCancellationToken()
        {
            using (CancellationTokenSource tokenSource = new CancellationTokenSource())
            using (JobHost host = new JobHost(_hostConfiguration))
            {
                _invokeInFunction = () => { tokenSource.Cancel(); };

                PrepareHostForTrigger(host, startHost: false);
                Assert.True(host.StartAsync(tokenSource.Token).WaitUntilCompleted(DefaultTimeout));

                EvaluateTriggeredCancellation(expectedCancellation: false);
            }
        }