Skip to content

Commit 29321fb

Browse files
authored
Improve Exception Handling and Testing for .NET Isolated Orchestrations (#1251)
* update * fix comment
1 parent 47e168f commit 29321fb

File tree

5 files changed

+108
-4
lines changed

5 files changed

+108
-4
lines changed

src/DurableTask.ApplicationInsights/DurableTask.ApplicationInsights.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
<PropertyGroup>
1313
<MajorVersion>0</MajorVersion>
1414
<MinorVersion>7</MinorVersion>
15-
<PatchVersion>0</PatchVersion>
15+
<PatchVersion>1</PatchVersion>
1616
<VersionPrefix>$(MajorVersion).$(MinorVersion).$(PatchVersion)</VersionPrefix>
1717
<FileVersion>$(VersionPrefix).0</FileVersion>
1818
<!-- FileVersionRevision is expected to be set by the CI. This is useful for distinguishing between multiple builds of the same version. -->

src/DurableTask.AzureStorage/DurableTask.AzureStorage.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
<PropertyGroup>
2323
<MajorVersion>2</MajorVersion>
2424
<MinorVersion>6</MinorVersion>
25-
<PatchVersion>0</PatchVersion>
25+
<PatchVersion>1</PatchVersion>
2626
<VersionPrefix>$(MajorVersion).$(MinorVersion).$(PatchVersion)</VersionPrefix>
2727
<FileVersion>$(VersionPrefix).0</FileVersion>
2828
<!-- FileVersionRevision is expected to be set by the CI. This is useful for distinguishing between multiple builds of the same version. -->

src/DurableTask.Core/DurableTask.Core.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
<PropertyGroup>
1919
<MajorVersion>3</MajorVersion>
2020
<MinorVersion>5</MinorVersion>
21-
<PatchVersion>0</PatchVersion>
21+
<PatchVersion>1</PatchVersion>
2222
<VersionPrefix>$(MajorVersion).$(MinorVersion).$(PatchVersion)</VersionPrefix>
2323
<FileVersion>$(VersionPrefix).0</FileVersion>
2424
<!-- FileVersionRevision is expected to be set by the CI. This is useful for distinguishing between multiple builds of the same version. -->

src/DurableTask.Core/TaskOrchestrationContext.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -686,7 +686,7 @@ public void FailOrchestration(Exception failure, OrchestrationRuntimeState runti
686686
{
687687
if (this.ErrorPropagationMode == ErrorPropagationMode.UseFailureDetails)
688688
{
689-
failureDetails = new FailureDetails(failure);
689+
failureDetails = new FailureDetails(failure, this.ExceptionPropertiesProvider.ExtractProperties(failure));
690690
}
691691
else
692692
{

test/DurableTask.Core.Tests/ExceptionHandlingIntegrationTests.cs

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,71 @@ await this.worker
335335
}
336336
}
337337

338+
[TestMethod]
339+
// Test that when a provider is set, properties of exception thrown by orchestration directly will be included
340+
// if excception type is matched.
341+
public async Task ExceptionPropertiesProvider_SimpleThrowExceptionOrchestration()
342+
{
343+
this.worker.ExceptionPropertiesProvider = new TestExceptionPropertiesProvider();
344+
this.worker.ErrorPropagationMode = ErrorPropagationMode.UseFailureDetails;
345+
346+
try
347+
{
348+
await this.worker
349+
.AddTaskOrchestrations(typeof(SimpleThrowExceptionOrchestration))
350+
.StartAsync();
351+
352+
var instance = await this.client.CreateOrchestrationInstanceAsync(typeof(SimpleThrowExceptionOrchestration), "test-input");
353+
var result = await this.client.WaitForOrchestrationAsync(instance, DefaultTimeout);
354+
355+
// Check that custom properties were extracted
356+
Assert.AreEqual(OrchestrationStatus.Failed, result.OrchestrationStatus);
357+
Assert.IsNotNull(result.FailureDetails);
358+
Assert.IsNotNull(result.FailureDetails.Properties);
359+
360+
// Check the properties match the ArgumentOutOfRangeException.
361+
Assert.AreEqual("count", result.FailureDetails.Properties["Name"]);
362+
Assert.AreEqual("100", result.FailureDetails.Properties["Value"]);
363+
}
364+
finally
365+
{
366+
await this.worker.StopAsync();
367+
}
368+
}
369+
370+
[TestMethod]
371+
// Test that when a provider is set, exception properties are included in failure details with propogation.
372+
public async Task ExceptionPropertiesProvider_SubOrchestrationThrowExceptionOrchestration()
373+
{
374+
this.worker.ExceptionPropertiesProvider = new TestExceptionPropertiesProvider();
375+
this.worker.ErrorPropagationMode = ErrorPropagationMode.UseFailureDetails;
376+
377+
try
378+
{
379+
await this.worker
380+
.AddTaskOrchestrations(typeof(SubOrchestrationThrowExceptionOrchestration))
381+
.AddTaskOrchestrations(typeof(ThrowArgumentOutofRangeExceptionASubOrchestration))
382+
.AddTaskActivities(typeof(ThrowArgumentOutofRangeExceptionActivity))
383+
.StartAsync();
384+
385+
var instance = await this.client.CreateOrchestrationInstanceAsync(typeof(SubOrchestrationThrowExceptionOrchestration), "test-input");
386+
var result = await this.client.WaitForOrchestrationAsync(instance, DefaultTimeout);
387+
388+
// Check that custom properties were extracted
389+
Assert.AreEqual(OrchestrationStatus.Failed, result.OrchestrationStatus);
390+
Assert.IsNotNull(result.FailureDetails);
391+
Assert.IsNotNull(result.FailureDetails.Properties);
392+
393+
// Check the properties match the ArgumentOutOfRangeException.
394+
Assert.AreEqual("count", result.FailureDetails.Properties["Name"]);
395+
Assert.AreEqual("100", result.FailureDetails.Properties["Value"]);
396+
}
397+
finally
398+
{
399+
await this.worker.StopAsync();
400+
}
401+
}
402+
338403
class ThrowCustomExceptionOrchestration : TaskOrchestration<string, string>
339404
{
340405
public override async Task<string> RunTask(OrchestrationContext context, string input)
@@ -352,6 +417,40 @@ protected override string Execute(TaskContext context, string input)
352417
}
353418
}
354419

420+
class SimpleThrowExceptionOrchestration : TaskOrchestration<string, string>
421+
{
422+
public override Task<string> RunTask(OrchestrationContext context, string input)
423+
{
424+
throw new ArgumentOutOfRangeException("count", 100, "Count is not valid.");
425+
}
426+
}
427+
428+
class SubOrchestrationThrowExceptionOrchestration : TaskOrchestration<string, string>
429+
{
430+
public override async Task<string> RunTask(OrchestrationContext context, string input)
431+
{
432+
await context.CreateSubOrchestrationInstance<string>(typeof(ThrowArgumentOutofRangeExceptionASubOrchestration), input);
433+
return "This should never be reached";
434+
}
435+
}
436+
437+
class ThrowArgumentOutofRangeExceptionASubOrchestration : TaskOrchestration<string, string>
438+
{
439+
public override async Task<string> RunTask(OrchestrationContext context, string input)
440+
{
441+
await context.ScheduleTask<string>(typeof(ThrowArgumentOutofRangeExceptionActivity), input);
442+
return "This should never be reached";
443+
}
444+
}
445+
446+
class ThrowArgumentOutofRangeExceptionActivity : TaskActivity<string, string>
447+
{
448+
protected override string Execute(TaskContext context, string input)
449+
{
450+
throw new ArgumentOutOfRangeException("count", 100, "Count is not valid.");
451+
}
452+
}
453+
355454
class ThrowInvalidOperationExceptionOrchestration : TaskOrchestration<string, string>
356455
{
357456
public override async Task<string> RunTask(OrchestrationContext context, string input)
@@ -409,6 +508,11 @@ class TestExceptionPropertiesProvider : IExceptionPropertiesProvider
409508
{
410509
return exception switch
411510
{
511+
ArgumentOutOfRangeException e => new Dictionary<string, object?>
512+
{
513+
["Name"] = e.ParamName ?? string.Empty,
514+
["Value"] = e.ActualValue?.ToString() ?? string.Empty,
515+
},
412516
CustomBusinessException businessEx => new Dictionary<string, object?>
413517
{
414518
["ExceptionTypeName"] = nameof(CustomBusinessException),

0 commit comments

Comments
 (0)