Skip to content

Commit

Permalink
Add a test for PurgeAllInstancesAsync (#3021)
Browse files Browse the repository at this point in the history
- Add a test for PurgeOrchestrationHistory
- Fix bug with PurgeOrchestrationHistory relating to null start date
  • Loading branch information
andystaples authored Feb 5, 2025
1 parent 168d7d0 commit c838aa0
Show file tree
Hide file tree
Showing 4 changed files with 156 additions and 1 deletion.
1 change: 1 addition & 0 deletions release_notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
- Fix custom connection name not working when using IDurableClientFactory.CreateClient() - contributed by [@hctan](https://github.com/hctan)
- Made durable extension for isolated worker configuration idempotent, allowing multiple calls safely. (#2950)
- Fixes a bug with Out of Memory exception handling in Isolated, improving reliability of retries for this case. (part of #3020)
- Fixed issue with passing null CreatedFrom date in PurgeInstancesFilter to client.PurgeAllInstancesAsync (#3021)

### Breaking Changes

Expand Down
4 changes: 3 additions & 1 deletion src/WebJobs.Extensions.DurableTask/ProtobufUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -438,8 +438,10 @@ internal static PurgeInstanceFilter ToPurgeInstanceFilter(P.PurgeInstancesReques
statusFilter = request.PurgeInstanceFilter.RuntimeStatus?.Select(status => (OrchestrationStatus)status).ToList();
}

// This ternary condition is necessary because the protobuf spec __insists__ that CreatedTimeFrom may never be null,
// but nonetheless if you pass null in function code, the value will be null here
return new PurgeInstanceFilter(
request.PurgeInstanceFilter.CreatedTimeFrom.ToDateTime(),
request.PurgeInstanceFilter.CreatedTimeFrom == null ? DateTime.MinValue : request.PurgeInstanceFilter.CreatedTimeFrom.ToDateTime(),
request.PurgeInstanceFilter.CreatedTimeTo?.ToDateTime(),
statusFilter);
}
Expand Down
51 changes: 51 additions & 0 deletions test/e2e/Apps/BasicDotNetIsolated/PurgeOrchestrationHistory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.

using System.Net;
using Grpc.Core;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Http;
using Microsoft.DurableTask.Client;
using Microsoft.Extensions.Logging;

namespace Microsoft.Azure.Durable.Tests.E2E
{
public static class PurgeOrchestrationHistory
{
[Function(nameof(PurgeOrchestrationHistory))]
public static async Task<HttpResponseData> PurgeHistory(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")] HttpRequestData req,
[DurableClient] DurableTaskClient client,
FunctionContext executionContext,
DateTime? purgeStartTime=null,
DateTime? purgeEndTime=null)
{
ILogger logger = executionContext.GetLogger("HelloCities_HttpStart");

logger.LogInformation("Starting purge all instance history");
try
{
var requestPurgeResult = await client.PurgeAllInstancesAsync(new PurgeInstancesFilter(purgeStartTime, purgeEndTime, new List<OrchestrationRuntimeStatus>{
OrchestrationRuntimeStatus.Completed,
OrchestrationRuntimeStatus.Failed,
OrchestrationRuntimeStatus.Terminated
}));

logger.LogInformation("Finished purge all instance history");

var response = req.CreateResponse(HttpStatusCode.OK);
response.Headers.Add("Content-Type", "text/plain");
await response.WriteStringAsync($"Purged {requestPurgeResult.PurgedInstanceCount} records");
return response;
}
catch (RpcException ex)
{
logger.LogError(ex, "Failed to purge all instance history");
var response = req.CreateResponse(HttpStatusCode.InternalServerError);
response.Headers.Add("Content-Type", "text/plain");
await response.WriteStringAsync($"Failed to purge all instance history: {ex.Message}");
return response;
}
}
}
}
101 changes: 101 additions & 0 deletions test/e2e/Tests/Tests/PurgeInstancesTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Xunit;
using Xunit.Abstractions;

namespace Microsoft.Azure.Durable.Tests.DotnetIsolatedE2E;

[Collection(Constants.FunctionAppCollectionName)]
public class PurgeInstancesTests
{
private readonly FunctionAppFixture _fixture;
private readonly ITestOutputHelper _output;

public PurgeInstancesTests(FunctionAppFixture fixture, ITestOutputHelper testOutputHelper)
{
_fixture = fixture;
_fixture.TestLogs.UseTestLogger(testOutputHelper);
_output = testOutputHelper;
}

[Fact]
public async Task PurgeOrchestrationHistory_StartAndEnd_Succeeds()
{
DateTime purgeStartTime = DateTime.MinValue;
DateTime purgeEndTime = DateTime.UtcNow;
string queryParams = $"?purgeStartTime={purgeStartTime:o}&purgeEndTime={purgeEndTime:o}";
using HttpResponseMessage response = await HttpHelpers.InvokeHttpTrigger("PurgeOrchestrationHistory", queryParams);
string actualMessage = await response.Content.ReadAsStringAsync();
Assert.Matches(@"^Purged [0-9]* records$", actualMessage);
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
}

[Fact]
public async Task PurgeOrchestrationHistory_Start_Succeeds()
{
DateTime purgeStartTime = DateTime.MinValue;
DateTime purgeEndTime = DateTime.UtcNow;
string queryParams = $"?purgeStartTime={purgeStartTime:o}";
using HttpResponseMessage response = await HttpHelpers.InvokeHttpTrigger("PurgeOrchestrationHistory", queryParams);
string actualMessage = await response.Content.ReadAsStringAsync();
Assert.Matches(@"^Purged [0-9]* records$", actualMessage);
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
}

[Fact]
public async Task PurgeOrchestrationHistory_End_Succeeds()
{
DateTime purgeStartTime = DateTime.MinValue;
DateTime purgeEndTime = DateTime.UtcNow;
string queryParams = $"?purgeEndTime={purgeEndTime:o}";
using HttpResponseMessage response = await HttpHelpers.InvokeHttpTrigger("PurgeOrchestrationHistory", queryParams);
string actualMessage = await response.Content.ReadAsStringAsync();
Assert.Matches(@"^Purged [0-9]* records$", actualMessage);
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
}

[Fact]
public async Task PurgeOrchestrationHistory_NoBoundaries_Succeeds()
{
DateTime purgeStartTime = DateTime.MinValue;
DateTime purgeEndTime = DateTime.UtcNow;
string queryParams = $"";
using HttpResponseMessage response = await HttpHelpers.InvokeHttpTrigger("PurgeOrchestrationHistory", queryParams);
string actualMessage = await response.Content.ReadAsStringAsync();
Assert.Matches(@"^Purged [0-9]* records$", actualMessage);
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
}

[Fact]
public async Task PurgeOrchestrationHistoryAfterInvocation_Succeeds()
{
using HttpResponseMessage response = await HttpHelpers.InvokeHttpTrigger("HelloCities_HttpStart", "");
Assert.Equal(HttpStatusCode.Accepted, response.StatusCode);
Thread.Sleep(1000);

DateTime purgeEndTime = DateTime.UtcNow + TimeSpan.FromMinutes(1);
using HttpResponseMessage purgeResponse = await HttpHelpers.InvokeHttpTrigger("PurgeOrchestrationHistory", $"?purgeEndTime={purgeEndTime:o}");
string purgeMessage = await purgeResponse.Content.ReadAsStringAsync();
Assert.Matches(@"^Purged [0-9]* records$", purgeMessage);
Assert.DoesNotMatch(@"^Purged 0 records$", purgeMessage);
Assert.Equal(HttpStatusCode.OK, purgeResponse.StatusCode);
}

[Fact]
public async Task PurgeAfterPurge_ZeroRows()
{
DateTime purgeEndTime = DateTime.UtcNow + TimeSpan.FromMinutes(1);
using HttpResponseMessage purgeResponse = await HttpHelpers.InvokeHttpTrigger("PurgeOrchestrationHistory", $"?purgeEndTime={purgeEndTime:o}");
string purgeMessage = await purgeResponse.Content.ReadAsStringAsync();
Assert.Matches(@"^Purged [0-9]* records$", purgeMessage);
using HttpResponseMessage purgeAgainResponse = await HttpHelpers.InvokeHttpTrigger("PurgeOrchestrationHistory", $"?purgeEndTime={purgeEndTime:o}");
string purgeAgainMessage = await purgeAgainResponse.Content.ReadAsStringAsync();
Assert.Matches(@"^Purged 0 records$", purgeAgainMessage);
Assert.Equal(HttpStatusCode.OK, purgeAgainResponse.StatusCode);
}
}

0 comments on commit c838aa0

Please sign in to comment.