Exemple #1
0
        public async Task TestLocalRcpEndpointRuntimeVersion(string runtimeVersion, bool enabledExpected)
        {
            INameResolver nameResolver = new SimpleNameResolver(
                new Dictionary <string, string>
            {
                { "FUNCTIONS_WORKER_RUNTIME", runtimeVersion },
            });

            using (ITestHost host = TestHelpers.GetJobHost(
                       this.loggerProvider,
                       nameof(this.TestLocalRcpEndpointRuntimeVersion),
                       enableExtendedSessions: false,
                       localRpcEndpointEnabled: null /* use FUNCTIONS_WORKER_RUNTIME to decide */,
                       nameResolver: nameResolver))
            {
                await host.StartAsync();

                // Check to see whether the local RPC endpoint has been opened
                IPGlobalProperties ipGlobalProperties = IPGlobalProperties.GetIPGlobalProperties();
                IPEndPoint[]       endpoints          = ipGlobalProperties.GetActiveTcpListeners();

                const string LocalRcpAddress = "127.0.0.1:17071";
                if (enabledExpected)
                {
                    Assert.Contains(LocalRcpAddress, endpoints.Select(ep => ep.ToString()));
                }
                else
                {
                    Assert.DoesNotContain(LocalRcpAddress, endpoints.Select(ep => ep.ToString()));
                }

                await host.StopAsync();
            }
        }
        public async Task TestLocalRcpEndpointRuntimeVersion(string runtimeVersion, bool enabledExpected)
        {
            INameResolver nameResolver = new SimpleNameResolver(
                new Dictionary <string, string>
            {
                { "FUNCTIONS_WORKER_RUNTIME", runtimeVersion },
            });

            using (ITestHost host = TestHelpers.GetJobHost(
                       this.loggerProvider,
                       nameof(this.TestLocalRcpEndpointRuntimeVersion),
                       enableExtendedSessions: false,
                       localRpcEndpointEnabled: null /* use FUNCTIONS_WORKER_RUNTIME to decide */,
                       nameResolver: nameResolver))
            {
                await host.StartAsync();

                // Validate if we opened local RPC endpoint by looking at log statements.
                var  logger             = this.loggerProvider.CreatedLoggers.Single(l => l.Category == TestHelpers.LogCategory);
                var  logMessages        = logger.LogMessages.ToList();
                bool enabledRpcEndpoint = logMessages.Any(msg => msg.Level == Microsoft.Extensions.Logging.LogLevel.Information && msg.FormattedMessage.StartsWith("Opened local RPC endpoint:"));

                Assert.Equal(enabledExpected, enabledRpcEndpoint);

                await host.StopAsync();
            }
        }
Exemple #3
0
        public async Task BindToBlobViaPOCO(string storageProviderType)
        {
            using (ITestHost host = TestHelpers.GetJobHost(this.loggerProvider, nameof(this.BindToBlobViaPOCO), false, storageProviderType))
            {
                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 client = await host.StartOrchestratorAsync(nameof(TestOrchestrations.CallActivity), startArgs, this.output);

                var status = await client.WaitForCompletionAsync(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();
            }
        }
Exemple #4
0
        public async Task TimerLengthLessThanMaxTime(bool extendedSessions)
        {
            using (ITestHost host = TestHelpers.GetJobHost(
                       this.loggerProvider,
                       nameof(this.TimerLengthLessThanMaxTime),
                       extendedSessions,
                       storageProviderType: "azure_storage",
                       durabilityProviderFactoryType: typeof(AzureStorageShortenedTimerDurabilityProviderFactory)))
            {
                await host.StartAsync();

                var fireAt = DateTime.UtcNow.AddSeconds(30);
                var client = await host.StartOrchestratorAsync(nameof(TestOrchestrations.Timer), fireAt, this.output);

                var status = await client.WaitForCompletionAsync(this.output, timeout : TimeSpan.FromMinutes(2));

                Assert.Equal(OrchestrationRuntimeStatus.Completed, status.RuntimeStatus);
                await host.StopAsync();
            }
        }
Exemple #5
0
        public async Task ActivityTriggerAsNumber(string storageProviderType)
        {
            using (ITestHost host = TestHelpers.GetJobHost(this.loggerProvider, nameof(this.ActivityTriggerAsNumber), 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.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 #6
0
        public async Task WaitForExternalEventAboveMaximumTimerLength()
        {
            using (ITestHost host = TestHelpers.GetJobHost(
                       this.loggerProvider,
                       nameof(this.WaitForExternalEventAboveMaximumTimerLength),
                       enableExtendedSessions: false,
                       storageProviderType: "azure_storage",
                       durabilityProviderFactoryType: typeof(AzureStorageShortenedTimerDurabilityProviderFactory)))
            {
                await host.StartAsync();

                var fireAt = TimeSpan.FromSeconds(90);
                var client = await host.StartOrchestratorAsync(nameof(TestOrchestrations.ApprovalWithTimeout), (fireAt, "throw"), this.output);

                var status = await client.WaitForCompletionAsync(this.output, timeout : TimeSpan.FromMinutes(2));

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

                await host.StopAsync();
            }
        }
Exemple #7
0
        public async Task InvokeLocalRpcEndpoint()
        {
            using (ITestHost host = TestHelpers.GetJobHost(
                       this.loggerProvider,
                       nameof(this.InvokeLocalRpcEndpoint),
                       enableExtendedSessions: false,
                       localRpcEndpointEnabled: true,
                       notificationUrl: null))
            {
                await host.StartAsync();

                using (var client = new WebClient())
                {
                    string jsonString = client.DownloadString("http://localhost:17071/durabletask/instances");

                    // The result is expected to be an empty array
                    JArray array = JArray.Parse(jsonString);
                }

                await host.StopAsync();
            }
        }
Exemple #8
0
        private async Task ActivityTriggerAsJObject(string storageProviderType)
        {
            using (ITestHost host = TestHelpers.GetJobHost(this.loggerProvider, nameof(this.ActivityTriggerAsJObject), 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.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 #9
0
        public async Task BindToDurableClientAsString(bool localRcpEnabled)
        {
            Uri testNotificationUrl = new Uri("https://durable.edu/runtime/webhooks/durabletask?code=abcdefg");

            using (ITestHost host = TestHelpers.GetJobHost(
                       this.loggerProvider,
                       nameof(this.BindToDurableClientAsString) + localRcpEnabled.ToString(),
                       enableExtendedSessions: false,
                       localRpcEndpointEnabled: localRcpEnabled,
                       notificationUrl: testNotificationUrl))
            {
                await host.StartAsync();

                // Fetch the JSON that was passed to the DurableClient binding
                string[] jsonRef = new string[1];
                await host.CallAsync(
                    nameof(ClientFunctions.GetDurableClientConfigJson),
                    new Dictionary <string, object>
                {
                    { "jsonRef", jsonRef },
                });

                string jsonString = jsonRef[0];
                Assert.NotNull(jsonString);
                JObject outerJson = JObject.Parse(jsonString);

                // Expected format:
                // {
                //   "taskHubName": "BindToDurableClientAsStringV2",
                //   "creationUrls": {
                //     "createNewInstancePostUri": "https://durable.edu/runtime/webhooks/durabletask/orchestrators/{functionName}[/{instanceId}]",
                //     "createAndWaitOnNewInstancePostUri": "https://durable.edu/runtime/webhooks/durabletask/orchestrators/{functionName}[/{instanceId}]?timeout={timeoutInSeconds}&pollingInterval={intervalInSeconds}"
                //   },
                //   "managementUrls": {
                //     "id": "INSTANCEID",
                //     "statusQueryGetUri": "https://durable.edu/runtime/webhooks/durabletask/instances/INSTANCEID?taskHub=BindToDurableClientAsStringV2&connection=Storage",
                //     "sendEventPostUri": "https://durable.edu/runtime/webhooks/durabletask/instances/INSTANCEID/raiseEvent/{eventName}?taskHub=BindToDurableClientAsStringV2&connection=Storage",
                //     "terminatePostUri": "https://durable.edu/runtime/webhooks/durabletask/instances/INSTANCEID/terminate?reason={text}&taskHub=BindToDurableClientAsStringV2&connection=Storage",
                //     "rewindPostUri": "https://durable.edu/runtime/webhooks/durabletask/instances/INSTANCEID/rewind?reason={text}&taskHub=BindToDurableClientAsStringV2&connection=Storage",
                //     "purgeHistoryDeleteUri": "https://durable.edu/runtime/webhooks/durabletask/instances/INSTANCEID?taskHub=BindToDurableClientAsStringV2&connection=Storage"
                //   },
                //   "baseUrl": "https://durable.edu/runtime/webhooks/durabletask/",
                //   "requiredQueryStringParameters": "code=abcdefg",
                //   "rpcBaseUrl": "http://127.0.0.1:17071/durabletask/" (or null)
                // }

                Assert.True(outerJson.TryGetValue("taskHubName", out JToken taskHubName));
                Assert.StartsWith(nameof(this.BindToDurableClientAsString), (string)taskHubName);

                // Local function that validates presence and format of the URL payloads.
                void CommonUriValidation(JToken json, string fieldName, string[] requiredSegments)
                {
                    Assert.Equal(JTokenType.Object, json.Type);
                    JObject jObj = (JObject)json;

                    Assert.True(jObj.TryGetValue(fieldName, out JToken fieldValue));
                    Assert.True(Uri.TryCreate((string)fieldValue, UriKind.Absolute, out Uri uri));
                    Assert.StartsWith(testNotificationUrl.GetLeftPart(UriPartial.Path), uri.GetLeftPart(UriPartial.Path));

                    if (fieldName != "baseUrl")
                    {
                        Assert.Contains(testNotificationUrl.Query.TrimStart('?'), uri.Query);
                    }

                    foreach (string segment in requiredSegments)
                    {
                        Assert.Contains(segment, uri.OriginalString);
                    }
                }

                string[] creationUrlParams = new[] { "{functionName}", "{instanceId}" };

                // Legacy payload validation
                Assert.True(outerJson.TryGetValue("creationUrls", out JToken creationUrls));
                CommonUriValidation(creationUrls, "createNewInstancePostUri", creationUrlParams);
                CommonUriValidation(creationUrls, "createAndWaitOnNewInstancePostUri", creationUrlParams);

                Assert.True(outerJson.TryGetValue("managementUrls", out JToken managementUrls));
                Assert.Equal(JTokenType.Object, managementUrls.Type);
                Assert.True(((JObject)managementUrls).TryGetValue("id", out JToken idValue));
                Assert.Equal(JTokenType.String, idValue.Type);

                string   idPlaceholder       = (string)idValue;
                string[] managementUrlParams = new[] { idPlaceholder };
                CommonUriValidation(managementUrls, "statusQueryGetUri", managementUrlParams);
                CommonUriValidation(managementUrls, "sendEventPostUri", managementUrlParams);
                CommonUriValidation(managementUrls, "terminatePostUri", managementUrlParams);
                CommonUriValidation(managementUrls, "rewindPostUri", managementUrlParams);
                CommonUriValidation(managementUrls, "purgeHistoryDeleteUri", managementUrlParams);

                CommonUriValidation(outerJson, "baseUrl", new string[0]);

                Assert.True(outerJson.TryGetValue("requiredQueryStringParameters", out JToken requiredQueryStringParameters));
                Assert.Equal(testNotificationUrl.Query.Trim('?'), (string)requiredQueryStringParameters);

                Assert.True(outerJson.TryGetValue("rpcBaseUrl", out JToken rpcBaseUrl));

                if (localRcpEnabled)
                {
                    Assert.True(Uri.TryCreate((string)rpcBaseUrl, UriKind.Absolute, out Uri rpcBaseUri));
                    Assert.True(rpcBaseUri.IsLoopback);
                    Assert.Equal("http", rpcBaseUri.Scheme);
                    Assert.Equal("/durabletask/", rpcBaseUri.AbsolutePath);
                }
                else
                {
                    Assert.Equal(JTokenType.Null, rpcBaseUrl.Type);
                }
            }
        }