Exemple #1
0
        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();
            }
        }
Exemple #2
0
        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();
            }
        }
Exemple #6
0
        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);
                }
            }
        }
Exemple #7
0
        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);
            }
        }
Exemple #8
0
        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();
            }
        }
Exemple #9
0
        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();
            }
        }
Exemple #10
0
        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);
            }
        }
Exemple #12
0
        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();
            }
        }
Exemple #13
0
        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();
            }
        }