From dd7a431972d79b3e2fc8eac5a21e4b5eafa146a0 Mon Sep 17 00:00:00 2001 From: David Justo Date: Tue, 30 Apr 2024 16:47:55 -0700 Subject: [PATCH 1/6] naive first implementation --- .../LocalGrpcListener.cs | 26 ++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/src/WebJobs.Extensions.DurableTask/LocalGrpcListener.cs b/src/WebJobs.Extensions.DurableTask/LocalGrpcListener.cs index fea9a0ffd..ed1daefaf 100644 --- a/src/WebJobs.Extensions.DurableTask/LocalGrpcListener.cs +++ b/src/WebJobs.Extensions.DurableTask/LocalGrpcListener.cs @@ -152,13 +152,33 @@ public override Task Hello(Empty request, ServerCallContext context) public async override Task StartInstance(P.CreateInstanceRequest request, ServerCallContext context) { + var instance = new OrchestrationInstance + { + InstanceId = request.InstanceId ?? Guid.NewGuid().ToString("N"), + ExecutionId = Guid.NewGuid().ToString(), + }; + + try { - string instanceId = await this.GetClient(context).StartNewAsync( - request.Name, request.InstanceId, Raw(request.Input)); + // we don't use a DurableOrchestrationClient here because it doesn't expose a "ScheduledStartTime" property + await this.GetDurabilityProvider(context).CreateTaskOrchestrationAsync( + new TaskMessage + { + Event = new ExecutionStartedEvent(-1, Raw(request.Input)) + { + Name = request.Name, + Version = request.Version, + OrchestrationInstance = instance, + ScheduledStartTime = request.ScheduledStartTimestamp?.ToDateTime(), + }, + OrchestrationInstance = instance, + }, + this.GetStatusesNotToOverride()); + return new P.CreateInstanceResponse { - InstanceId = instanceId, + InstanceId = instance.InstanceId, }; } catch (OrchestrationAlreadyExistsException) From 6a5632ea808c9d6e1c2392c93dd71d7c2dee3198 Mon Sep 17 00:00:00 2001 From: David Justo Date: Wed, 1 May 2024 10:10:28 -0700 Subject: [PATCH 2/6] use TaskHubClient instead --- .../LocalGrpcListener.cs | 52 ++++++++----------- 1 file changed, 23 insertions(+), 29 deletions(-) diff --git a/src/WebJobs.Extensions.DurableTask/LocalGrpcListener.cs b/src/WebJobs.Extensions.DurableTask/LocalGrpcListener.cs index ed1daefaf..296ad3864 100644 --- a/src/WebJobs.Extensions.DurableTask/LocalGrpcListener.cs +++ b/src/WebJobs.Extensions.DurableTask/LocalGrpcListener.cs @@ -152,33 +152,27 @@ public override Task Hello(Empty request, ServerCallContext context) public async override Task StartInstance(P.CreateInstanceRequest request, ServerCallContext context) { - var instance = new OrchestrationInstance - { - InstanceId = request.InstanceId ?? Guid.NewGuid().ToString("N"), - ExecutionId = Guid.NewGuid().ToString(), - }; - - try { - // we don't use a DurableOrchestrationClient here because it doesn't expose a "ScheduledStartTime" property - await this.GetDurabilityProvider(context).CreateTaskOrchestrationAsync( - new TaskMessage - { - Event = new ExecutionStartedEvent(-1, Raw(request.Input)) - { - Name = request.Name, - Version = request.Version, - OrchestrationInstance = instance, - ScheduledStartTime = request.ScheduledStartTimestamp?.ToDateTime(), - }, - OrchestrationInstance = instance, - }, - this.GetStatusesNotToOverride()); + string instanceId = request.InstanceId ?? Guid.NewGuid().ToString("N"); + TaskHubClient taskhubClient = new TaskHubClient(this.GetDurabilityProvider(context)); + + // TODO: Ideally, we'd have a single method in the taskhubClient that can handle both scheduled and non-scheduled starts. + // TODO: the type of `ScheduledStartTimestamp` is not nullable. Can we change it to `DateTime?` in the proto file? + if (request.ScheduledStartTimestamp != null) + { + var ins = await taskhubClient.CreateScheduledOrchestrationInstanceAsync( + name: request.Name, version: request.Version, instanceId: instanceId, input: Raw(request.Input), startAt: request.ScheduledStartTimestamp.ToDateTime()); + } + else + { + await taskhubClient.CreateOrchestrationInstanceAsync(request.Name, request.Version, instanceId, Raw(request.Input)); + } + // TODO: should this not inclide the ExecutionId and other elements of the taskhubClient response? return new P.CreateInstanceResponse { - InstanceId = instance.InstanceId, + InstanceId = instanceId, }; } catch (OrchestrationAlreadyExistsException) @@ -251,13 +245,13 @@ await this.GetDurabilityProvider(context).CreateTaskOrchestrationAsync( EntityBackendQueries.EntityQueryResult result = await entityOrchestrationService.EntityBackendQueries!.QueryEntitiesAsync( new EntityBackendQueries.EntityQuery() { - InstanceIdStartsWith = query.InstanceIdStartsWith, - LastModifiedFrom = query.LastModifiedFrom?.ToDateTime(), - LastModifiedTo = query.LastModifiedTo?.ToDateTime(), - IncludeTransient = query.IncludeTransient, - IncludeState = query.IncludeState, - ContinuationToken = query.ContinuationToken, - PageSize = query.PageSize, + InstanceIdStartsWith = query.InstanceIdStartsWith, + LastModifiedFrom = query.LastModifiedFrom?.ToDateTime(), + LastModifiedTo = query.LastModifiedTo?.ToDateTime(), + IncludeTransient = query.IncludeTransient, + IncludeState = query.IncludeState, + ContinuationToken = query.ContinuationToken, + PageSize = query.PageSize, }, context.CancellationToken); From 1a17e1a185d27fcd5653b5cd66f6aec9fe5e0e11 Mon Sep 17 00:00:00 2001 From: David Justo Date: Tue, 28 May 2024 13:42:20 -0700 Subject: [PATCH 3/6] minor edit --- src/WebJobs.Extensions.DurableTask/LocalGrpcListener.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/WebJobs.Extensions.DurableTask/LocalGrpcListener.cs b/src/WebJobs.Extensions.DurableTask/LocalGrpcListener.cs index 296ad3864..605acc041 100644 --- a/src/WebJobs.Extensions.DurableTask/LocalGrpcListener.cs +++ b/src/WebJobs.Extensions.DurableTask/LocalGrpcListener.cs @@ -156,23 +156,24 @@ public override Task Hello(Empty request, ServerCallContext context) { string instanceId = request.InstanceId ?? Guid.NewGuid().ToString("N"); TaskHubClient taskhubClient = new TaskHubClient(this.GetDurabilityProvider(context)); + OrchestrationInstance instance; // TODO: Ideally, we'd have a single method in the taskhubClient that can handle both scheduled and non-scheduled starts. // TODO: the type of `ScheduledStartTimestamp` is not nullable. Can we change it to `DateTime?` in the proto file? if (request.ScheduledStartTimestamp != null) { - var ins = await taskhubClient.CreateScheduledOrchestrationInstanceAsync( + instance = await taskhubClient.CreateScheduledOrchestrationInstanceAsync( name: request.Name, version: request.Version, instanceId: instanceId, input: Raw(request.Input), startAt: request.ScheduledStartTimestamp.ToDateTime()); } else { - await taskhubClient.CreateOrchestrationInstanceAsync(request.Name, request.Version, instanceId, Raw(request.Input)); + instance = await taskhubClient.CreateOrchestrationInstanceAsync(request.Name, request.Version, instanceId, Raw(request.Input)); } - // TODO: should this not inclide the ExecutionId and other elements of the taskhubClient response? + // TODO: should this not include the ExecutionId and other elements of the taskhubClient response? return new P.CreateInstanceResponse { - InstanceId = instanceId, + InstanceId = instance.InstanceId, }; } catch (OrchestrationAlreadyExistsException) From cabe839dde4b3e0ed17d9ba70ab49c567008d6c4 Mon Sep 17 00:00:00 2001 From: Andy Staples Date: Wed, 15 Jan 2025 11:42:46 -0700 Subject: [PATCH 4/6] Add scheduled start time E2E Test --- .../Apps/BasicDotNetIsolated/HelloCities.cs | 22 +++++++++++ test/e2e/Tests/Helpers/DurableHelpers.cs | 39 +++++++++++++++++++ test/e2e/Tests/Tests/HelloCitiesTest.cs | 33 ++++++++++++---- 3 files changed, 86 insertions(+), 8 deletions(-) create mode 100644 test/e2e/Tests/Helpers/DurableHelpers.cs diff --git a/test/e2e/Apps/BasicDotNetIsolated/HelloCities.cs b/test/e2e/Apps/BasicDotNetIsolated/HelloCities.cs index dc7633947..749e02557 100644 --- a/test/e2e/Apps/BasicDotNetIsolated/HelloCities.cs +++ b/test/e2e/Apps/BasicDotNetIsolated/HelloCities.cs @@ -54,5 +54,27 @@ public static async Task HttpStart( // See https://learn.microsoft.com/azure/azure-functions/durable/durable-functions-http-api#start-orchestration return await client.CreateCheckStatusResponseAsync(req, instanceId); } + + [Function("HelloCities_HttpStart_Scheduled")] + public static async Task HttpStartScheduled( + [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")] HttpRequestData req, + [DurableClient] DurableTaskClient client, + FunctionContext executionContext, + DateTime ScheduledStartTime) + { + ILogger logger = executionContext.GetLogger("HelloCities_HttpStart"); + + var startOptions = new StartOrchestrationOptions(StartAt: ScheduledStartTime); + + // Function input comes from the request content. + string instanceId = await client.ScheduleNewOrchestrationInstanceAsync( + nameof(HelloCities), startOptions); + + logger.LogInformation("Started orchestration with ID = '{instanceId}'.", instanceId); + + // Returns an HTTP 202 response with an instance management payload. + // See https://learn.microsoft.com/azure/azure-functions/durable/durable-functions-http-api#start-orchestration + return await client.CreateCheckStatusResponseAsync(req, instanceId); + } } } diff --git a/test/e2e/Tests/Helpers/DurableHelpers.cs b/test/e2e/Tests/Helpers/DurableHelpers.cs new file mode 100644 index 000000000..475cfe9d7 --- /dev/null +++ b/test/e2e/Tests/Helpers/DurableHelpers.cs @@ -0,0 +1,39 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System.Text.Json.Nodes; + +namespace Microsoft.Azure.Durable.Tests.DotnetIsolatedE2E; + +internal class DurableHelpers +{ + internal static string ParseStatusQueryGetUri(HttpResponseMessage invocationStartResponse) + { + string? responseString = invocationStartResponse.Content?.ReadAsStringAsync().Result; + if (string.IsNullOrEmpty(responseString)) + { + return string.Empty; + } + JsonNode? responseJsonNode = JsonNode.Parse(responseString); + if (responseJsonNode == null) + { + return string.Empty; + } + + string? statusQueryGetUri = responseJsonNode["StatusQueryGetUri"]?.GetValue(); + return statusQueryGetUri ?? string.Empty; + } + internal static string GetRuntimeStatus(string statusQueryGetUri) + { + HttpClient client = new HttpClient(); + var statusQueryResponse = client.GetAsync(statusQueryGetUri); + + string? statusQueryResponseString = statusQueryResponse.Result.Content.ReadAsStringAsync().Result; + JsonNode? statusQueryJsonNode = JsonNode.Parse(statusQueryResponseString); + if (statusQueryJsonNode == null) + { + return string.Empty; + } + return statusQueryJsonNode["runtimeStatus"]?.GetValue() ?? string.Empty; + } +} diff --git a/test/e2e/Tests/Tests/HelloCitiesTest.cs b/test/e2e/Tests/Tests/HelloCitiesTest.cs index d21fb375b..03c53cf57 100644 --- a/test/e2e/Tests/Tests/HelloCitiesTest.cs +++ b/test/e2e/Tests/Tests/HelloCitiesTest.cs @@ -7,7 +7,7 @@ namespace Microsoft.Azure.Durable.Tests.DotnetIsolatedE2E; -[Collection(Constants.FunctionAppCollectionName )] +[Collection(Constants.FunctionAppCollectionName)] public class HttpEndToEndTests { private readonly FunctionAppFixture _fixture; @@ -19,17 +19,34 @@ public HttpEndToEndTests(FunctionAppFixture fixture, ITestOutputHelper testOutpu } [Theory] - [InlineData("HelloCities_HttpStart", "", HttpStatusCode.Accepted, "")] - public async Task HttpTriggerTests(string functionName, string queryString, HttpStatusCode expectedStatusCode, string expectedMessage) + [InlineData("HelloCities_HttpStart", HttpStatusCode.Accepted)] + public async Task HttpTriggerTests(string functionName, HttpStatusCode expectedStatusCode) { - using HttpResponseMessage response = await HttpHelpers.InvokeHttpTrigger(functionName, queryString); + using HttpResponseMessage response = await HttpHelpers.InvokeHttpTrigger(functionName, ""); string actualMessage = await response.Content.ReadAsStringAsync(); Assert.Equal(expectedStatusCode, response.StatusCode); + Assert.False(string.IsNullOrEmpty(actualMessage)); + } + + [Theory] + [InlineData("HelloCities_HttpStart_Scheduled", HttpStatusCode.Accepted)] + public async Task ScheduledStartTests(string functionName, HttpStatusCode expectedStatusCode) + { + var scheduledStartDate = DateTime.Now + TimeSpan.FromSeconds(10); + + using HttpResponseMessage response = await HttpHelpers.InvokeHttpTrigger(functionName, $"?ScheduledStartTime={scheduledStartDate.ToString("o")}"); + string actualMessage = await response.Content.ReadAsStringAsync(); + + string statusQueryGetUri = DurableHelpers.ParseStatusQueryGetUri(response); + + Assert.Equal(expectedStatusCode, response.StatusCode); + + string startRuntimeStatus = DurableHelpers.GetRuntimeStatus(statusQueryGetUri); + Assert.Equal("Pending", startRuntimeStatus); + Thread.Sleep(11000); - if (!string.IsNullOrEmpty(expectedMessage)) - { - Assert.False(string.IsNullOrEmpty(actualMessage)); - } + string endRuntimeStatus = DurableHelpers.GetRuntimeStatus(statusQueryGetUri); + Assert.Equal("Completed", endRuntimeStatus); } } From 18e491df196bb754c11cca6b770cebc2340e7a50 Mon Sep 17 00:00:00 2001 From: Andy Staples Date: Wed, 15 Jan 2025 13:11:08 -0700 Subject: [PATCH 5/6] Test improvements - Add a run for an orchestration scheduled in past - Basic test case should check output - Provide more info from StatusQueryGetUri response --- .../Apps/BasicDotNetIsolated/HelloCities.cs | 4 +- test/e2e/Tests/E2ETests.csproj | 2 +- test/e2e/Tests/Helpers/DurableHelpers.cs | 38 ++++++++++++---- test/e2e/Tests/Tests/HelloCitiesTest.cs | 44 ++++++++++++++----- 4 files changed, 64 insertions(+), 24 deletions(-) diff --git a/test/e2e/Apps/BasicDotNetIsolated/HelloCities.cs b/test/e2e/Apps/BasicDotNetIsolated/HelloCities.cs index 749e02557..1db525394 100644 --- a/test/e2e/Apps/BasicDotNetIsolated/HelloCities.cs +++ b/test/e2e/Apps/BasicDotNetIsolated/HelloCities.cs @@ -60,11 +60,11 @@ public static async Task HttpStartScheduled( [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")] HttpRequestData req, [DurableClient] DurableTaskClient client, FunctionContext executionContext, - DateTime ScheduledStartTime) + DateTime scheduledStartTime) { ILogger logger = executionContext.GetLogger("HelloCities_HttpStart"); - var startOptions = new StartOrchestrationOptions(StartAt: ScheduledStartTime); + var startOptions = new StartOrchestrationOptions(StartAt: scheduledStartTime); // Function input comes from the request content. string instanceId = await client.ScheduleNewOrchestrationInstanceAsync( diff --git a/test/e2e/Tests/E2ETests.csproj b/test/e2e/Tests/E2ETests.csproj index 5ba0ccf7a..e9587197a 100644 --- a/test/e2e/Tests/E2ETests.csproj +++ b/test/e2e/Tests/E2ETests.csproj @@ -1,4 +1,4 @@ - + net8.0 diff --git a/test/e2e/Tests/Helpers/DurableHelpers.cs b/test/e2e/Tests/Helpers/DurableHelpers.cs index 475cfe9d7..b5d9a535b 100644 --- a/test/e2e/Tests/Helpers/DurableHelpers.cs +++ b/test/e2e/Tests/Helpers/DurableHelpers.cs @@ -7,9 +7,34 @@ namespace Microsoft.Azure.Durable.Tests.DotnetIsolatedE2E; internal class DurableHelpers { + static readonly HttpClient _httpClient = new HttpClient(); + + internal class OrchestrationStatusDetails + { + public string RuntimeStatus { get; set; } = string.Empty; + public string Input { get; set; } = string.Empty; + public string Output { get; set; } = string.Empty; + public DateTime CreatedTime { get; set; } + public DateTime LastUpdatedTime { get; set; } + public OrchestrationStatusDetails(string statusQueryResponse) + { + JsonNode? statusQueryJsonNode = JsonNode.Parse(statusQueryResponse); + if (statusQueryJsonNode == null) + { + return; + } + this.RuntimeStatus = statusQueryJsonNode["runtimeStatus"]?.GetValue() ?? string.Empty; + this.Input = statusQueryJsonNode["input"]?.ToString() ?? string.Empty; + this.Output = statusQueryJsonNode["output"]?.ToString() ?? string.Empty; + this.CreatedTime = DateTime.Parse(statusQueryJsonNode["createdTime"]?.GetValue() ?? string.Empty); + this.LastUpdatedTime = DateTime.Parse(statusQueryJsonNode["lastUpdatedTime"]?.GetValue() ?? string.Empty); + } + } + internal static string ParseStatusQueryGetUri(HttpResponseMessage invocationStartResponse) { string? responseString = invocationStartResponse.Content?.ReadAsStringAsync().Result; + if (string.IsNullOrEmpty(responseString)) { return string.Empty; @@ -23,17 +48,12 @@ internal static string ParseStatusQueryGetUri(HttpResponseMessage invocationStar string? statusQueryGetUri = responseJsonNode["StatusQueryGetUri"]?.GetValue(); return statusQueryGetUri ?? string.Empty; } - internal static string GetRuntimeStatus(string statusQueryGetUri) + internal static OrchestrationStatusDetails GetRunningOrchestrationDetails(string statusQueryGetUri) { - HttpClient client = new HttpClient(); - var statusQueryResponse = client.GetAsync(statusQueryGetUri); + var statusQueryResponse = _httpClient.GetAsync(statusQueryGetUri); string? statusQueryResponseString = statusQueryResponse.Result.Content.ReadAsStringAsync().Result; - JsonNode? statusQueryJsonNode = JsonNode.Parse(statusQueryResponseString); - if (statusQueryJsonNode == null) - { - return string.Empty; - } - return statusQueryJsonNode["runtimeStatus"]?.GetValue() ?? string.Empty; + + return new OrchestrationStatusDetails(statusQueryResponseString); } } diff --git a/test/e2e/Tests/Tests/HelloCitiesTest.cs b/test/e2e/Tests/Tests/HelloCitiesTest.cs index 03c53cf57..570553a76 100644 --- a/test/e2e/Tests/Tests/HelloCitiesTest.cs +++ b/test/e2e/Tests/Tests/HelloCitiesTest.cs @@ -11,42 +11,62 @@ namespace Microsoft.Azure.Durable.Tests.DotnetIsolatedE2E; public class HttpEndToEndTests { private readonly FunctionAppFixture _fixture; + private readonly ITestOutputHelper _output; public HttpEndToEndTests(FunctionAppFixture fixture, ITestOutputHelper testOutputHelper) { _fixture = fixture; _fixture.TestLogs.UseTestLogger(testOutputHelper); + _output = testOutputHelper; } [Theory] - [InlineData("HelloCities_HttpStart", HttpStatusCode.Accepted)] - public async Task HttpTriggerTests(string functionName, HttpStatusCode expectedStatusCode) + [InlineData("HelloCities_HttpStart", HttpStatusCode.Accepted, "Hello Tokyo!")] + public async Task HttpTriggerTests(string functionName, HttpStatusCode expectedStatusCode, string partialExpectedOutput) { using HttpResponseMessage response = await HttpHelpers.InvokeHttpTrigger(functionName, ""); string actualMessage = await response.Content.ReadAsStringAsync(); Assert.Equal(expectedStatusCode, response.StatusCode); - Assert.False(string.IsNullOrEmpty(actualMessage)); + string statusQueryGetUri = DurableHelpers.ParseStatusQueryGetUri(response); + Thread.Sleep(1000); + var orchestrationDetails = DurableHelpers.GetRunningOrchestrationDetails(statusQueryGetUri); + Assert.Equal("Completed", orchestrationDetails.RuntimeStatus); + Assert.Contains(partialExpectedOutput, orchestrationDetails.Output); } [Theory] - [InlineData("HelloCities_HttpStart_Scheduled", HttpStatusCode.Accepted)] - public async Task ScheduledStartTests(string functionName, HttpStatusCode expectedStatusCode) + [InlineData("HelloCities_HttpStart_Scheduled", 5, HttpStatusCode.Accepted)] + [InlineData("HelloCities_HttpStart_Scheduled", -5, HttpStatusCode.Accepted)] + public async Task ScheduledStartTests(string functionName, int startDelaySeconds, HttpStatusCode expectedStatusCode) { - var scheduledStartDate = DateTime.Now + TimeSpan.FromSeconds(10); + var testStartTime = DateTime.Now; + var scheduledStartTime = testStartTime + TimeSpan.FromSeconds(startDelaySeconds); + string urlQueryString = $"?ScheduledStartTime={scheduledStartTime.ToString("o")}"; - using HttpResponseMessage response = await HttpHelpers.InvokeHttpTrigger(functionName, $"?ScheduledStartTime={scheduledStartDate.ToString("o")}"); + using HttpResponseMessage response = await HttpHelpers.InvokeHttpTrigger(functionName, urlQueryString); string actualMessage = await response.Content.ReadAsStringAsync(); string statusQueryGetUri = DurableHelpers.ParseStatusQueryGetUri(response); Assert.Equal(expectedStatusCode, response.StatusCode); - string startRuntimeStatus = DurableHelpers.GetRuntimeStatus(statusQueryGetUri); - Assert.Equal("Pending", startRuntimeStatus); - Thread.Sleep(11000); + var orchestrationDetails = DurableHelpers.GetRunningOrchestrationDetails(statusQueryGetUri); + while (DateTime.Now < scheduledStartTime) + { + _output.WriteLine($"Test scheduled for {scheduledStartTime}, current time {DateTime.Now}"); + orchestrationDetails = DurableHelpers.GetRunningOrchestrationDetails(statusQueryGetUri); + Assert.Equal("Pending", orchestrationDetails.RuntimeStatus); + Thread.Sleep(3000); + } + + // Give a small amount of time for the orchestration to complete, even if scheduled to run immediately + Thread.Sleep(1000); + _output.WriteLine($"Test scheduled for {scheduledStartTime}, current time {DateTime.Now}, looking for completed"); + + var finalOrchestrationDetails = DurableHelpers.GetRunningOrchestrationDetails(statusQueryGetUri); + Assert.Equal("Completed", finalOrchestrationDetails.RuntimeStatus); - string endRuntimeStatus = DurableHelpers.GetRuntimeStatus(statusQueryGetUri); - Assert.Equal("Completed", endRuntimeStatus); + Assert.True(finalOrchestrationDetails.LastUpdatedTime > scheduledStartTime); } } From a163eccdcd06fe04c4748351cd2a82a666da9008 Mon Sep 17 00:00:00 2001 From: Andy Staples Date: Wed, 15 Jan 2025 13:27:37 -0700 Subject: [PATCH 6/6] Use UTC universally --- test/e2e/Tests/Helpers/DurableHelpers.cs | 4 ++-- test/e2e/Tests/Tests/HelloCitiesTest.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/test/e2e/Tests/Helpers/DurableHelpers.cs b/test/e2e/Tests/Helpers/DurableHelpers.cs index b5d9a535b..dd8ef2fe2 100644 --- a/test/e2e/Tests/Helpers/DurableHelpers.cs +++ b/test/e2e/Tests/Helpers/DurableHelpers.cs @@ -26,8 +26,8 @@ public OrchestrationStatusDetails(string statusQueryResponse) this.RuntimeStatus = statusQueryJsonNode["runtimeStatus"]?.GetValue() ?? string.Empty; this.Input = statusQueryJsonNode["input"]?.ToString() ?? string.Empty; this.Output = statusQueryJsonNode["output"]?.ToString() ?? string.Empty; - this.CreatedTime = DateTime.Parse(statusQueryJsonNode["createdTime"]?.GetValue() ?? string.Empty); - this.LastUpdatedTime = DateTime.Parse(statusQueryJsonNode["lastUpdatedTime"]?.GetValue() ?? string.Empty); + this.CreatedTime = DateTime.Parse(statusQueryJsonNode["createdTime"]?.GetValue() ?? string.Empty).ToUniversalTime(); + this.LastUpdatedTime = DateTime.Parse(statusQueryJsonNode["lastUpdatedTime"]?.GetValue() ?? string.Empty).ToUniversalTime(); } } diff --git a/test/e2e/Tests/Tests/HelloCitiesTest.cs b/test/e2e/Tests/Tests/HelloCitiesTest.cs index 570553a76..f618ac9e2 100644 --- a/test/e2e/Tests/Tests/HelloCitiesTest.cs +++ b/test/e2e/Tests/Tests/HelloCitiesTest.cs @@ -40,7 +40,7 @@ public async Task HttpTriggerTests(string functionName, HttpStatusCode expectedS [InlineData("HelloCities_HttpStart_Scheduled", -5, HttpStatusCode.Accepted)] public async Task ScheduledStartTests(string functionName, int startDelaySeconds, HttpStatusCode expectedStatusCode) { - var testStartTime = DateTime.Now; + var testStartTime = DateTime.UtcNow; var scheduledStartTime = testStartTime + TimeSpan.FromSeconds(startDelaySeconds); string urlQueryString = $"?ScheduledStartTime={scheduledStartTime.ToString("o")}"; @@ -52,7 +52,7 @@ public async Task ScheduledStartTests(string functionName, int startDelaySeconds Assert.Equal(expectedStatusCode, response.StatusCode); var orchestrationDetails = DurableHelpers.GetRunningOrchestrationDetails(statusQueryGetUri); - while (DateTime.Now < scheduledStartTime) + while (DateTime.UtcNow < scheduledStartTime) { _output.WriteLine($"Test scheduled for {scheduledStartTime}, current time {DateTime.Now}"); orchestrationDetails = DurableHelpers.GetRunningOrchestrationDetails(statusQueryGetUri);