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 BindToBlobViaPOCO() { using (JobHost host = TestHelpers.GetJobHost(this.loggerProvider, nameof(this.BindToBlobViaPOCO), false)) { await host.StartAsync(); string connectionString = TestHelpers.GetStorageConnectionString(); 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(); } }
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 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(); } }
public async Task BindToBlobViaParameterName() { 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. const string OriginalBlobName = "MyBlob"; const string UpdatedBlobName = OriginalBlobName + "-archive"; const string ContainerName = "test"; 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"); CloudBlockBlob blob = container.GetBlockBlobReference(OriginalBlobName); 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.BindToBlobViaParameterName); startArgs.Input = OriginalBlobName; 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); CloudBlockBlob newBlob = container.GetBlockBlobReference(UpdatedBlobName); string copiedData = await newBlob.DownloadTextAsync(); this.output.WriteLine($"Downloaded text '{copiedData}' from {newBlob.Name}."); Assert.Equal(randomData, copiedData); 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 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 ActivityTriggerAsNumber() { using (JobHost host = TestHelpers.GetJobHost(this.loggerProvider, nameof(this.ActivityTriggerAsNumber), false)) { 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.BindToDouble); startArgs.Input = 3.14; var client = await host.StartOrchestratorAsync(nameof(TestOrchestrations.CallActivity), startArgs, this.output); var status = await client.WaitForCompletionAsync(this.output); // The function echos back the input value Assert.Equal(OrchestrationRuntimeStatus.Completed, status?.RuntimeStatus); Assert.Equal((double)startArgs.Input, status?.Output); await host.StopAsync(); } }
public async Task ActivityTriggerAsJObject() { using (JobHost host = TestHelpers.GetJobHost(this.loggerProvider, nameof(this.ActivityTriggerAsJObject), false)) { 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 client = await host.StartOrchestratorAsync(nameof(TestOrchestrations.CallActivity), startArgs, this.output); var status = await client.WaitForCompletionAsync(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(OrchestrationRuntimeStatus.Completed, status?.RuntimeStatus); Assert.True((bool)status?.Output); await host.StopAsync(); } }
public async Task ActivityTriggerAsNumber() { using (JobHost host = TestHelpers.GetJobHost(loggerFactory, nameof(ActivityTriggerAsNumber))) { 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.BindToDouble); startArgs.Input = 3.14; 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 input value Assert.Equal(OrchestrationRuntimeStatus.Completed, status?.RuntimeStatus); Assert.Equal((double)startArgs.Input, status?.Output); await host.StopAsync(); } }
public async Task Orchestration_OnUnregisteredActivity() { string[] orchestratorFunctionNames = { nameof(TestOrchestrations.CallActivity) }; const string activityFunctionName = "UnregisteredActivity"; string errorMessage = $"The function '{activityFunctionName}' doesn't exist, is disabled, or is not an activity function"; using (JobHost host = TestHelpers.GetJobHost(this.loggerFactory, nameof(Orchestration_OnUnregisteredActivity))) { await host.StartAsync(); var startArgs = new StartOrchestrationArgs { FunctionName = activityFunctionName, Input = new { Foo = "Bar" } }; var client = await host.StartOrchestratorAsync(orchestratorFunctionNames[0], startArgs, this.output); var status = await client.WaitForCompletionAsync(TimeSpan.FromSeconds(30), 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(errorMessage, (string)status?.Output); await host.StopAsync(); } if (this.useTestLogger) { TestHelpers.AssertLogMessageSequence(loggerProvider, "Orchestration_OnUnregisteredActivity", orchestratorFunctionNames); } }
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 ActivityTriggerAsPOCO(string storageProviderType) { using (ITestHost host = TestHelpers.GetJobHost(this.loggerProvider, nameof(this.ActivityTriggerAsPOCO), false, storageProviderType)) { 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 client = await host.StartOrchestratorAsync(nameof(TestOrchestrations.CallActivity), startArgs, this.output); var status = await client.WaitForCompletionAsync(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(); } }
public async Task RaiseEventToSubOrchestration() { string taskHub = nameof(RaiseEventToSubOrchestration); using (JobHost host = TestHelpers.GetJobHost(this.loggerFactory, taskHub)) { await host.StartAsync(); var orchestrator = nameof(TestOrchestrations.CallOrchestrator); var timeout = Debugger.IsAttached ? TimeSpan.FromMinutes(5) : TimeSpan.FromSeconds(30); var input = new StartOrchestrationArgs { FunctionName = nameof(TestOrchestrations.Approval), InstanceId = "SubOrchestration-" + Guid.NewGuid().ToString("N"), Input = TimeSpan.FromMinutes(5) }; var client = await host.StartOrchestratorAsync(orchestrator, input, this.output); var status = await client.WaitForStartupAsync(timeout, this.output); Assert.Equal(OrchestrationRuntimeStatus.Running, status?.RuntimeStatus); // Wait long enough for the sub-orchestration to be started and waiting for input. await Task.Delay(TimeSpan.FromSeconds(2)); await client.InnerClient.RaiseEventAsync(input.InstanceId, "approval", true); status = await client.WaitForCompletionAsync(timeout, this.output); Assert.Equal("Approved", status?.Output); await host.StopAsync(); } }