public async Task SequentialOrchestration() { using (JobHost host = TestHelpers.GetJobHost(this.loggerFactory, nameof(SequentialOrchestration))) { await host.StartAsync(); var client = await host.StartFunctionAsync(nameof(TestOrchestrations.Factorial), 10, this.output); var status = await client.WaitForCompletionAsync(TimeSpan.FromSeconds(30), this.output); Assert.Equal("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); } }
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.StartFunctionAsync(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-webjobs-sdk-script-pr/issues/37 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("Completed", status?.RuntimeStatus); Assert.Equal("Expired", status?.Output); await host.StopAsync(); } if (this.useTestLogger) { TestHelpers.AssertLogMessageSequence(loggerProvider, "TimerExpiration", orchestratorFunctionNames); } }
public async Task Orchestration_Activity() { string[] orchestratorFunctionNames = { nameof(TestOrchestrations.OrchestratorGreeting), nameof(TestOrchestrations.SayHelloWithActivity) }; string activityFunctionName = nameof(TestActivities.Hello); using (JobHost host = TestHelpers.GetJobHost(this.loggerFactory, nameof(Orchestration_Activity))) { await host.StartAsync(); var client = await host.StartFunctionAsync(orchestratorFunctionNames[0], null, this.output); var status = await client.WaitForCompletionAsync(TimeSpan.FromSeconds(20), this.output); Assert.NotNull(status); Assert.Equal("Completed", status.RuntimeStatus); Assert.Equal(client.InstanceId, status.InstanceId); Assert.Equal(orchestratorFunctionNames[0], status?.Name); await host.StopAsync(); } if (this.useTestLogger) { TestHelpers.AssertLogMessageSequence(loggerProvider, "Orchestration_Activity", orchestratorFunctionNames, activityFunctionName); } }
public async Task OrchestrationConcurrency() { using (JobHost host = TestHelpers.GetJobHost(this.loggerFactory, nameof(OrchestrationConcurrency))) { await host.StartAsync(); Func <Task> orchestrationStarter = async delegate() { var timeout = TimeSpan.FromSeconds(10); var client = await host.StartFunctionAsync(nameof(TestOrchestrations.Approval), timeout, this.output); await client.WaitForCompletionAsync(TimeSpan.FromSeconds(60), 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 70 seconds. Task parallelOrchestrations = Task.WhenAll(tasks); Task timeoutTask = Task.Delay(TimeSpan.FromSeconds(70)); Task winner = await Task.WhenAny(parallelOrchestrations, timeoutTask); Assert.Equal(parallelOrchestrations, winner); await host.StopAsync(); } }
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.StartFunctionAsync(orchestratorFunctionNames[0], null, this.output); var status = await client.WaitForCompletionAsync(TimeSpan.FromSeconds(50), this.output); Assert.Equal("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); } }
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.StartFunctionAsync(orchestratorFunctionNames[0], message, this.output); var status = await client.WaitForCompletionAsync(TimeSpan.FromSeconds(40), this.output); Assert.Equal("Failed", status?.RuntimeStatus); // There aren't any exception details in the output: https://github.com/Azure/azure-webjobs-sdk-script-pr/issues/36 Assert.True(status?.Output.ToString().Contains("Exception")); await host.StopAsync(); } if (this.useTestLogger) { TestHelpers.AssertLogMessageSequence(loggerProvider, "UnhandledActivityExceptionWithRetry", orchestratorFunctionNames, activityFunctionName); } }
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.StartFunctionAsync(orchestratorFunctionNames[0], "World", this.output); var status = await client.WaitForCompletionAsync(TimeSpan.FromSeconds(30), this.output); Assert.Equal("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); } }
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.StartFunctionAsync(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-webjobs-sdk-script-pr/issues/37 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("Terminated", status?.RuntimeStatus); Assert.Equal("sayōnara", status?.Output); await host.StopAsync(); } if (this.useTestLogger) { TestHelpers.AssertLogMessageSequence(loggerProvider, "TerminateOrchestration", orchestratorFunctionNames); } }
public async Task ActivityTriggerAsPOCO() { using (JobHost host = TestHelpers.GetJobHost(loggerFactory, nameof(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.StartFunctionAsync(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("Completed", status?.RuntimeStatus); Assert.Equal(input.Foo, status?.Output); await host.StopAsync(); } }
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 Orchestration_OnValidOrchestrator() { const string greetingName = "ValidOrchestrator"; const string validOrchestratorName = "SayHelloWithActivity"; string[] orchestratorFunctionNames = { nameof(TestOrchestrations.CallOrchestrator), validOrchestratorName }; string activityFunctionName = nameof(TestActivities.Hello); var input = new { Foo = greetingName }; var inputJson = JsonConvert.SerializeObject(input); using (JobHost host = TestHelpers.GetJobHost(this.loggerFactory, nameof(Orchestration_OnValidOrchestrator))) { await host.StartAsync(); var startArgs = new StartOrchestrationArgs { FunctionName = orchestratorFunctionNames[1], Input = input }; // Function type call chain: 'CallActivity' (orchestrator) -> 'SayHelloWithActivity' (orchestrator) -> 'Hello' (activity) var client = await host.StartFunctionAsync(orchestratorFunctionNames[0], startArgs, this.output); var status = await client.WaitForCompletionAsync(TimeSpan.FromSeconds(30), this.output); var statusInput = JsonConvert.DeserializeObject <Dictionary <string, object> >(status?.Input.ToString()); Assert.NotNull(status); Assert.Equal("Completed", status.RuntimeStatus); Assert.Equal(client.InstanceId, status.InstanceId); Assert.Equal(validOrchestratorName, statusInput["FunctionName"].ToString()); Assert.Contains(greetingName, statusInput["Input"].ToString()); Assert.Equal($"Hello, [{inputJson}]!", status.Output.ToString()); await host.StopAsync(); if (this.useTestLogger) { TestHelpers.AssertLogMessageSequence(loggerProvider, "Orchestration_OnValidOrchestrator", orchestratorFunctionNames, activityFunctionName); } } }
public async Task HandledActivityException() { using (JobHost host = TestHelpers.GetJobHost(this.loggerFactory, nameof(HandledActivityException))) { await host.StartAsync(); // Empty string input should result in ArgumentNullException in the orchestration code. var client = await host.StartFunctionAsync(nameof(TestOrchestrations.TryCatchLoop), 5, this.output); var status = await client.WaitForCompletionAsync(TimeSpan.FromSeconds(10), this.output); Assert.Equal("Completed", status?.RuntimeStatus); Assert.Equal(5, status?.Output); await host.StopAsync(); } }
public async Task HelloWorldOrchestration_Activity() { using (JobHost host = GetJobHost()) { await host.StartAsync(); var client = await host.StartFunctionAsync(nameof(Orchestrations.SayHelloWithActivity), "World", this.output); var status = await client.WaitForCompletionAsync(TimeSpan.FromSeconds(30), this.output); Assert.Equal("Completed", status?.RuntimeStatus); Assert.Equal("World", status?.Input); Assert.Equal("Hello, World!", status?.Output); await host.StopAsync(); } }
public async Task SequentialOrchestration() { using (JobHost host = GetJobHost()) { await host.StartAsync(); var client = await host.StartFunctionAsync(nameof(Orchestrations.Factorial), 10, this.output); var status = await client.WaitForCompletionAsync(TimeSpan.FromSeconds(30), this.output); Assert.Equal("Completed", status?.RuntimeStatus); Assert.Equal(10, status?.Input); Assert.Equal(3628800, status?.Output); await host.StopAsync(); } }
public async Task ParallelOrchestration() { using (JobHost host = TestHelpers.GetJobHost(this.loggerFactory, nameof(ParallelOrchestration))) { await host.StartAsync(); var client = await host.StartFunctionAsync(nameof(TestOrchestrations.DiskUsage), Environment.CurrentDirectory, this.output); var status = await client.WaitForCompletionAsync(TimeSpan.FromSeconds(90), this.output); Assert.Equal("Completed", status?.RuntimeStatus); Assert.Equal(Environment.CurrentDirectory, status?.Input); Assert.True((long?)status?.Output > 0L); await host.StopAsync(); } }
public async Task ActivityWithRetry_NullRetryOptions() { using (JobHost host = TestHelpers.GetJobHost(loggerFactory, nameof(ActivityWithRetry_NullRetryOptions))) { await host.StartAsync(); string message = "Kah-BOOOOM!!!"; string orchestratorFunctionName = nameof(TestOrchestrations.ActivityWithRetry_NullRetryOptions); var client = await host.StartFunctionAsync(orchestratorFunctionName, message, this.output); var status = await client.WaitForCompletionAsync(TimeSpan.FromSeconds(40), this.output); Assert.Equal("Failed", status?.RuntimeStatus); Assert.True(status?.Output.ToString().Contains("Value cannot be null.")); Assert.True(status?.Output.ToString().Contains("Parameter name: retryOptions")); await host.StopAsync(); } }
public async Task UnhandledOrchestrationException() { using (JobHost host = GetJobHost()) { await host.StartAsync(); // Null input should result in ArgumentNullException in the orchestration code. var client = await host.StartFunctionAsync(nameof(Orchestrations.Throw), null, this.output); var status = await client.WaitForCompletionAsync(TimeSpan.FromSeconds(10), this.output); Assert.Equal("Failed", status?.RuntimeStatus); // There aren't any exception details in the output: https://github.com/Azure/azure-webjobs-sdk-script-pr/issues/36 Assert.True(status?.Output.ToString().Contains("Value cannot be null")); await host.StopAsync(); } }
public async Task UnhandledActivityException() { using (JobHost host = GetJobHost()) { await host.StartAsync(); string message = "Kah-BOOOOM!!!"; var client = await host.StartFunctionAsync(nameof(Orchestrations.Throw), message, this.output); var status = await client.WaitForCompletionAsync(TimeSpan.FromSeconds(10), this.output); Assert.Equal("Failed", status?.RuntimeStatus); // There aren't any exception details in the output: https://github.com/Azure/azure-webjobs-sdk-script-pr/issues/36 Assert.True(status?.Output.ToString().Contains("Exception")); await host.StopAsync(); } }
public async Task ThrowExceptionOnLongTimer() { using (JobHost host = TestHelpers.GetJobHost(this.loggerFactory, nameof(Orchestration_OnValidOrchestrator))) { await host.StartAsync(); // Right now, the limit for timers is 6 days. In the future, we'll extend this and update this test. // https://github.com/Azure/azure-functions-durable-extension/issues/14 DateTime fireAt = DateTime.UtcNow.AddDays(7); var client = await host.StartFunctionAsync(nameof(TestOrchestrations.Timer), fireAt, this.output); var status = await client.WaitForCompletionAsync(TimeSpan.FromSeconds(30), this.output); Assert.NotNull(status); Assert.Equal("Failed", status.RuntimeStatus); Assert.True(status.Output.ToString().Contains("fireAt")); await host.StopAsync(); } }
public async Task Orchestration_OnUnregisteredOrchestrator() { const string unregisteredOrchestrator = "UnregisteredOrchestrator"; string[] orchestratorFunctionNames = { nameof(TestOrchestrations.CallOrchestrator), unregisteredOrchestrator }; string errorMessage = $"The function '{unregisteredOrchestrator}' doesn't exist, is disabled, or is not an orchestrator function"; using (JobHost host = TestHelpers.GetJobHost(this.loggerFactory, nameof(Orchestration_OnUnregisteredActivity))) { await host.StartAsync(); var startArgs = new StartOrchestrationArgs { FunctionName = unregisteredOrchestrator, Input = new { Foo = "Bar" } }; var client = await host.StartFunctionAsync(orchestratorFunctionNames[0], startArgs, this.output); var status = await client.WaitForCompletionAsync(TimeSpan.FromSeconds(30), this.output); Assert.Equal("Failed", status?.RuntimeStatus); // There aren't any exception details in the output: https://github.com/Azure/azure-webjobs-sdk-script-pr/issues/36 Assert.True(status?.Output.ToString().Contains(errorMessage)); await host.StopAsync(); } if (this.useTestLogger) { TestHelpers.AssertLogMessageSequence(loggerProvider, "Orchestration_OnUnregisteredOrchestrator", orchestratorFunctionNames); } }
public async Task OrchestrationWithRetry_NullRetryOptions() { string[] orchestratorFunctionNames = { nameof(TestOrchestrations.OrchestratorWithRetry_NullRetryOptions) }; using (JobHost host = TestHelpers.GetJobHost(loggerFactory, nameof(OrchestrationWithRetry_NullRetryOptions))) { await host.StartAsync(); // Null input should result in ArgumentNullException in the orchestration code. var client = await host.StartFunctionAsync(orchestratorFunctionNames[0], null, this.output); var status = await client.WaitForCompletionAsync(TimeSpan.FromSeconds(50), this.output); Assert.Equal("Failed", status?.RuntimeStatus); Assert.True(status?.Output.ToString().Contains("Value cannot be null.")); Assert.True(status?.Output.ToString().Contains("Parameter name: retryOptions")); await host.StopAsync(); } }
public async Task ActivityTriggerAsJObject() { using (JobHost host = TestHelpers.GetJobHost(loggerFactory, nameof(ActivityTriggerAsJObject))) { 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.BindToJObject); startArgs.Input = new { Foo = "Bar" }; var timeout = Debugger.IsAttached ? TimeSpan.FromMinutes(5) : TimeSpan.FromSeconds(30); var client = await host.StartFunctionAsync(nameof(TestOrchestrations.CallActivity), startArgs, this.output); var status = await client.WaitForCompletionAsync(timeout, this.output); // The function checks to see if there is a property called "Foo" which is set to a value // called "Bar" and returns true if this is the case. Otherwise returns false. Assert.Equal("Completed", status?.RuntimeStatus); Assert.Equal(true, status?.Output); await host.StopAsync(); } }
public async Task TimerExpiration() { using (JobHost host = GetJobHost()) { await host.StartAsync(); var timeout = TimeSpan.FromSeconds(10); var client = await host.StartFunctionAsync(nameof(Orchestrations.Approval), 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-webjobs-sdk-script-pr/issues/37 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("Completed", status?.RuntimeStatus); Assert.Equal("Expired", status?.Output); await host.StopAsync(); } }
public async Task TerminateOrchestration() { using (JobHost host = GetJobHost()) { await host.StartAsync(); // Using the counter orchestration because it will wait indefinitely for input. var client = await host.StartFunctionAsync(nameof(Orchestrations.Counter), 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-webjobs-sdk-script-pr/issues/37 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("Terminated", status?.RuntimeStatus); Assert.Equal("sayōnara", status?.Output); await host.StopAsync(); } }
public async Task StartOrchestration_OnUnregisteredOrchestrator() { const string activityFunctionName = "UnregisteredOrchestrator"; string errorMessage = $"The function '{activityFunctionName}' doesn't exist, is disabled, or is not an orchestrator function"; using (JobHost host = TestHelpers.GetJobHost(this.loggerFactory, nameof(StartOrchestration_OnUnregisteredOrchestrator))) { await host.StartAsync(); Exception ex = await Assert.ThrowsAsync <FunctionInvocationException>(async() => await host.StartFunctionAsync("UnregisteredOrchestrator", "Unregistered", this.output)); Assert.NotNull(ex.InnerException); Assert.Contains(errorMessage, ex.InnerException?.ToString()); await host.StopAsync(); } }