From 85fe3f4c6c371bf4ff24f4df612eba6a84aa7e60 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 9 Dec 2025 23:03:12 +0000 Subject: [PATCH 1/3] Initial plan From af63780666e48742dfe480de28f1e1f9753ac694 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 9 Dec 2025 23:08:24 +0000 Subject: [PATCH 2/3] Add missing integration tests for external events and exception handling Co-authored-by: YunchuWang <12449837+YunchuWang@users.noreply.github.com> --- .../OrchestrationPatterns.cs | 149 +++++++++++++++++- 1 file changed, 147 insertions(+), 2 deletions(-) diff --git a/test/Grpc.IntegrationTests/OrchestrationPatterns.cs b/test/Grpc.IntegrationTests/OrchestrationPatterns.cs index 666d7e25..42dc16a0 100644 --- a/test/Grpc.IntegrationTests/OrchestrationPatterns.cs +++ b/test/Grpc.IntegrationTests/OrchestrationPatterns.cs @@ -1195,6 +1195,151 @@ async Task OrchestratorFunc(TaskOrchestrationContext ctx, int counter) Assert.Equal(EventCount, metadata.ReadOutputAs()); } - // TODO: Test for multiple external events with the same name - // TODO: Test for catching activity exceptions of specific types + [Theory] + [InlineData(2)] + [InlineData(5)] + [InlineData(10)] + public async Task MultipleExternalEventsWithSameName(int eventCount) + { + TaskName orchestratorName = nameof(MultipleExternalEventsWithSameName); + await using HostTestLifetime server = await this.StartWorkerAsync(b => + { + b.AddTasks(tasks => tasks.AddOrchestratorFunc(orchestratorName, async ctx => + { + List receivedEvents = new(); + for (int i = 0; i < eventCount; i++) + { + string eventData = await ctx.WaitForExternalEvent("MyEvent"); + receivedEvents.Add(eventData); + } + + return receivedEvents; + })); + }); + + string instanceId = await server.Client.ScheduleNewOrchestrationInstanceAsync(orchestratorName); + + // To ensure consistency, wait for the instance to start before sending the events + OrchestrationMetadata metadata = await server.Client.WaitForInstanceStartAsync( + instanceId, + this.TimeoutToken); + + // Send multiple events with the same name but different payloads + List expectedEvents = new(); + for (int i = 0; i < eventCount; i++) + { + string eventData = $"Event_{i}"; + expectedEvents.Add(eventData); + await server.Client.RaiseEventAsync(metadata.InstanceId, "MyEvent", eventPayload: eventData); + } + + // Once the orchestration receives all the events it is expecting, it should complete. + metadata = await server.Client.WaitForInstanceCompletionAsync( + instanceId, getInputsAndOutputs: true, this.TimeoutToken); + Assert.NotNull(metadata); + Assert.Equal(OrchestrationRuntimeStatus.Completed, metadata.RuntimeStatus); + + List? results = metadata.ReadOutputAs>(); + Assert.NotNull(results); + Assert.Equal(eventCount, results!.Count); + Assert.Equal(expectedEvents, results); + } + + [Fact] + public async Task CatchingActivityExceptionsByType() + { + TaskName orchestratorName = nameof(CatchingActivityExceptionsByType); + TaskName throwInvalidOpActivityName = "ThrowInvalidOp"; + TaskName throwArgumentActivityName = "ThrowArgument"; + TaskName successActivityName = "Success"; + + await using HostTestLifetime server = await this.StartWorkerAsync(b => + { + b.AddTasks(tasks => tasks + .AddOrchestratorFunc(orchestratorName, async ctx => + { + List results = new(); + + // Test 1: Catch InvalidOperationException + try + { + await ctx.CallActivityAsync(throwInvalidOpActivityName); + results.Add("No exception thrown"); + } + catch (TaskFailedException ex) when (ex.FailureDetails?.IsCausedBy() == true) + { + results.Add("Caught InvalidOperationException"); + } + catch (TaskFailedException) + { + results.Add("Caught wrong exception type"); + } + + // Test 2: Catch ArgumentException + try + { + await ctx.CallActivityAsync(throwArgumentActivityName); + results.Add("No exception thrown"); + } + catch (TaskFailedException ex) when (ex.FailureDetails?.IsCausedBy() == true) + { + results.Add("Caught ArgumentException"); + } + catch (TaskFailedException) + { + results.Add("Caught wrong exception type"); + } + + // Test 3: Successful activity should not throw + try + { + string result = await ctx.CallActivityAsync(successActivityName); + results.Add(result); + } + catch (TaskFailedException) + { + results.Add("Unexpected exception"); + } + + // Test 4: Catch with base Exception type + try + { + await ctx.CallActivityAsync(throwInvalidOpActivityName); + results.Add("No exception thrown"); + } + catch (TaskFailedException ex) when (ex.FailureDetails?.IsCausedBy() == true) + { + results.Add("Caught base Exception"); + } + + return results; + }) + .AddActivityFunc(throwInvalidOpActivityName, (TaskActivityContext ctx) => + { + throw new InvalidOperationException("Invalid operation"); + }) + .AddActivityFunc(throwArgumentActivityName, (TaskActivityContext ctx) => + { + throw new ArgumentException("Invalid argument"); + }) + .AddActivityFunc(successActivityName, (TaskActivityContext ctx) => + { + return "Success"; + })); + }); + + string instanceId = await server.Client.ScheduleNewOrchestrationInstanceAsync(orchestratorName); + OrchestrationMetadata metadata = await server.Client.WaitForInstanceCompletionAsync( + instanceId, getInputsAndOutputs: true, this.TimeoutToken); + Assert.NotNull(metadata); + Assert.Equal(OrchestrationRuntimeStatus.Completed, metadata.RuntimeStatus); + + List? results = metadata.ReadOutputAs>(); + Assert.NotNull(results); + Assert.Equal(4, results!.Count); + Assert.Equal("Caught InvalidOperationException", results[0]); + Assert.Equal("Caught ArgumentException", results[1]); + Assert.Equal("Success", results[2]); + Assert.Equal("Caught base Exception", results[3]); + } } From 2d886096a9793d3ea2cff33e3b10f28654c8c2bc Mon Sep 17 00:00:00 2001 From: peterstone2017 <12449837+YunchuWang@users.noreply.github.com> Date: Wed, 10 Dec 2025 09:41:00 -0800 Subject: [PATCH 3/3] remove dup test --- .../OrchestrationPatterns.cs | 50 ------------------- 1 file changed, 50 deletions(-) diff --git a/test/Grpc.IntegrationTests/OrchestrationPatterns.cs b/test/Grpc.IntegrationTests/OrchestrationPatterns.cs index 42dc16a0..72c286f3 100644 --- a/test/Grpc.IntegrationTests/OrchestrationPatterns.cs +++ b/test/Grpc.IntegrationTests/OrchestrationPatterns.cs @@ -1195,56 +1195,6 @@ async Task OrchestratorFunc(TaskOrchestrationContext ctx, int counter) Assert.Equal(EventCount, metadata.ReadOutputAs()); } - [Theory] - [InlineData(2)] - [InlineData(5)] - [InlineData(10)] - public async Task MultipleExternalEventsWithSameName(int eventCount) - { - TaskName orchestratorName = nameof(MultipleExternalEventsWithSameName); - await using HostTestLifetime server = await this.StartWorkerAsync(b => - { - b.AddTasks(tasks => tasks.AddOrchestratorFunc(orchestratorName, async ctx => - { - List receivedEvents = new(); - for (int i = 0; i < eventCount; i++) - { - string eventData = await ctx.WaitForExternalEvent("MyEvent"); - receivedEvents.Add(eventData); - } - - return receivedEvents; - })); - }); - - string instanceId = await server.Client.ScheduleNewOrchestrationInstanceAsync(orchestratorName); - - // To ensure consistency, wait for the instance to start before sending the events - OrchestrationMetadata metadata = await server.Client.WaitForInstanceStartAsync( - instanceId, - this.TimeoutToken); - - // Send multiple events with the same name but different payloads - List expectedEvents = new(); - for (int i = 0; i < eventCount; i++) - { - string eventData = $"Event_{i}"; - expectedEvents.Add(eventData); - await server.Client.RaiseEventAsync(metadata.InstanceId, "MyEvent", eventPayload: eventData); - } - - // Once the orchestration receives all the events it is expecting, it should complete. - metadata = await server.Client.WaitForInstanceCompletionAsync( - instanceId, getInputsAndOutputs: true, this.TimeoutToken); - Assert.NotNull(metadata); - Assert.Equal(OrchestrationRuntimeStatus.Completed, metadata.RuntimeStatus); - - List? results = metadata.ReadOutputAs>(); - Assert.NotNull(results); - Assert.Equal(eventCount, results!.Count); - Assert.Equal(expectedEvents, results); - } - [Fact] public async Task CatchingActivityExceptionsByType() {