Skip to content

Commit d505adc

Browse files
authored
Detached cancellation test (#352)
1 parent 5141c2c commit d505adc

File tree

2 files changed

+84
-1
lines changed

2 files changed

+84
-1
lines changed

.github/workflows/ci.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,4 +134,3 @@ jobs:
134134
dotnet-repo-path: ${{github.event.pull_request.head.repo.full_name}}
135135
version: ${{github.event.pull_request.head.ref}}
136136
version-is-repo-ref: true
137-
features-repo-ref: "dotnet-after-1.3.0"

tests/Temporalio.Tests/Worker/WorkflowWorkerTests.cs

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5792,6 +5792,90 @@ await ExecuteWorkerAsync<NullWithCodecWorkflow>(
57925792
client);
57935793
}
57945794

5795+
[Workflow]
5796+
public class DetachedCancellationWorkflow
5797+
{
5798+
public class Activities
5799+
{
5800+
public TaskCompletionSource WaitingForCancel { get; } = new();
5801+
5802+
public bool CleanupCalled { get; set; }
5803+
5804+
[Activity]
5805+
public async Task WaitForCancel()
5806+
{
5807+
WaitingForCancel.SetResult();
5808+
await Task.Delay(
5809+
Timeout.Infinite,
5810+
ActivityExecutionContext.Current.CancellationToken);
5811+
}
5812+
5813+
[Activity]
5814+
public void Cleanup() => CleanupCalled = true;
5815+
}
5816+
5817+
[WorkflowRun]
5818+
public async Task RunAsync()
5819+
{
5820+
// Wait forever for cancellation, then cleanup
5821+
try
5822+
{
5823+
await Workflow.ExecuteActivityAsync(
5824+
(Activities acts) => acts.WaitForCancel(),
5825+
new() { StartToCloseTimeout = TimeSpan.FromMinutes(10) });
5826+
}
5827+
catch (Exception e) when (TemporalException.IsCanceledException(e))
5828+
{
5829+
// Run cleanup with another token
5830+
using var detachedCancelSource = new CancellationTokenSource();
5831+
await Workflow.ExecuteActivityAsync(
5832+
(Activities acts) => acts.Cleanup(),
5833+
new()
5834+
{
5835+
StartToCloseTimeout = TimeSpan.FromMinutes(10),
5836+
CancellationToken = detachedCancelSource.Token,
5837+
});
5838+
// Rethrow
5839+
throw;
5840+
}
5841+
}
5842+
}
5843+
5844+
[Fact]
5845+
public async Task ExecuteWorkflowAsync_DetachedCancellation_WorksProperly()
5846+
{
5847+
var activities = new DetachedCancellationWorkflow.Activities();
5848+
await ExecuteWorkerAsync<DetachedCancellationWorkflow>(
5849+
async worker =>
5850+
{
5851+
// Start workflow
5852+
var handle = await Client.StartWorkflowAsync(
5853+
(DetachedCancellationWorkflow wf) => wf.RunAsync(),
5854+
new(id: $"workflow-{Guid.NewGuid()}", taskQueue: worker.Options.TaskQueue!));
5855+
5856+
// Wait until waiting for cancel
5857+
await activities.WaitingForCancel.Task;
5858+
5859+
// Send workflow cancel
5860+
await handle.CancelAsync();
5861+
5862+
// Confirm canceled
5863+
var exc = await Assert.ThrowsAsync<WorkflowFailedException>(
5864+
() => handle.GetResultAsync());
5865+
Assert.IsType<CanceledFailureException>(exc.InnerException);
5866+
5867+
// Confirm cleanup called
5868+
Assert.True(activities.CleanupCalled);
5869+
5870+
// Run through replayer to confirm deterministic on replay
5871+
var history = await handle.FetchHistoryAsync();
5872+
var replayer = new WorkflowReplayer(
5873+
new WorkflowReplayerOptions().AddWorkflow<DetachedCancellationWorkflow>());
5874+
await replayer.ReplayWorkflowAsync(history);
5875+
},
5876+
new TemporalWorkerOptions().AddAllActivities(activities));
5877+
}
5878+
57955879
internal static Task AssertTaskFailureContainsEventuallyAsync(
57965880
WorkflowHandle handle, string messageContains)
57975881
{

0 commit comments

Comments
 (0)