diff --git a/src/Abstractions/TaskOptions.cs b/src/Abstractions/TaskOptions.cs
index 7c0d54ee..49e98edf 100644
--- a/src/Abstractions/TaskOptions.cs
+++ b/src/Abstractions/TaskOptions.cs
@@ -176,6 +176,7 @@ public StartOrchestrationOptions(StartOrchestrationOptions options)
this.Tags = options.Tags;
this.Version = options.Version;
this.DedupeStatuses = options.DedupeStatuses;
+ this.IdReusePolicy = options.IdReusePolicy;
}
///
@@ -204,6 +205,16 @@ public StartOrchestrationOptions(StartOrchestrationOptions options)
///
///
/// For type-safe usage, use the WithDedupeStatuses extension method.
+ /// This property is mutually exclusive with IdReusePolicy. If both are set, IdReusePolicy takes precedence.
///
public IReadOnlyList? DedupeStatuses { get; init; }
+
+ ///
+ /// Gets the orchestration ID reuse policy.
+ ///
+ ///
+ /// This is an internal property. For type-safe usage, use the WithIdReusePolicy extension method.
+ /// This property takes precedence over DedupeStatuses if both are set.
+ ///
+ public object? IdReusePolicy { get; init; }
}
diff --git a/src/Client/Core/CreateOrchestrationAction.cs b/src/Client/Core/CreateOrchestrationAction.cs
new file mode 100644
index 00000000..327815c9
--- /dev/null
+++ b/src/Client/Core/CreateOrchestrationAction.cs
@@ -0,0 +1,28 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+namespace Microsoft.DurableTask.Client;
+
+///
+/// Defines actions for handling orchestration instance ID conflicts.
+///
+public enum CreateOrchestrationAction
+{
+ ///
+ /// Throws an exception if an orchestration instance with the specified ID already exists in one of the operation statuses.
+ /// This is the default behavior.
+ ///
+ Error = 0,
+
+ ///
+ /// Ignores the request to create a new orchestration instance if one already exists in one of the operation statuses.
+ /// No exception is thrown and no new instance is created.
+ ///
+ Ignore = 1,
+
+ ///
+ /// Terminates any existing orchestration instance with the same ID that is in one of the operation statuses,
+ /// and then creates a new instance as an atomic operation. This is similar to an on-demand ContinueAsNew.
+ ///
+ Terminate = 2,
+}
diff --git a/src/Client/Core/OrchestrationIdReusePolicy.cs b/src/Client/Core/OrchestrationIdReusePolicy.cs
new file mode 100644
index 00000000..be03b2a4
--- /dev/null
+++ b/src/Client/Core/OrchestrationIdReusePolicy.cs
@@ -0,0 +1,69 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+namespace Microsoft.DurableTask.Client;
+
+///
+/// Defines a policy for reusing orchestration instance IDs.
+///
+///
+/// This policy determines what happens when a client attempts to create a new orchestration instance
+/// with an ID that already exists. The policy consists of an action (Error, Ignore, or Terminate)
+/// and a set of orchestration runtime statuses to which the action applies.
+///
+public sealed class OrchestrationIdReusePolicy
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The orchestration runtime statuses to which the action applies.
+ /// The action to take when an orchestration instance with a matching status exists.
+ public OrchestrationIdReusePolicy(
+ IEnumerable operationStatuses,
+ CreateOrchestrationAction action)
+ {
+ Check.NotNull(operationStatuses);
+ this.OperationStatuses = operationStatuses.ToArray();
+ this.Action = action;
+ }
+
+ ///
+ /// Gets the orchestration runtime statuses to which the action applies.
+ ///
+ ///
+ /// When an orchestration instance exists with one of these statuses, the specified action will be taken.
+ /// For example, if the action is and the operation statuses
+ /// include , then any running instance with the same ID
+ /// will be terminated before creating a new instance.
+ ///
+ public IReadOnlyList OperationStatuses { get; }
+
+ ///
+ /// Gets the action to take when an orchestration instance with a matching status exists.
+ ///
+ public CreateOrchestrationAction Action { get; }
+
+ ///
+ /// Creates a policy that throws an error if an orchestration instance with the specified statuses already exists.
+ ///
+ /// The orchestration runtime statuses that should cause an error.
+ /// A new with the Error action.
+ public static OrchestrationIdReusePolicy Error(params OrchestrationRuntimeStatus[] statuses)
+ => new(statuses, CreateOrchestrationAction.Error);
+
+ ///
+ /// Creates a policy that ignores the request if an orchestration instance with the specified statuses already exists.
+ ///
+ /// The orchestration runtime statuses that should cause the request to be ignored.
+ /// A new with the Ignore action.
+ public static OrchestrationIdReusePolicy Ignore(params OrchestrationRuntimeStatus[] statuses)
+ => new(statuses, CreateOrchestrationAction.Ignore);
+
+ ///
+ /// Creates a policy that terminates any existing orchestration instance with the specified statuses and creates a new one.
+ ///
+ /// The orchestration runtime statuses that should be terminated before creating a new instance.
+ /// A new with the Terminate action.
+ public static OrchestrationIdReusePolicy Terminate(params OrchestrationRuntimeStatus[] statuses)
+ => new(statuses, CreateOrchestrationAction.Terminate);
+}
diff --git a/src/Client/Core/StartOrchestrationOptionsExtensions.cs b/src/Client/Core/StartOrchestrationOptionsExtensions.cs
index 4bfac52f..660f8302 100644
--- a/src/Client/Core/StartOrchestrationOptionsExtensions.cs
+++ b/src/Client/Core/StartOrchestrationOptionsExtensions.cs
@@ -10,6 +10,9 @@ namespace Microsoft.DurableTask.Client;
///
public static class StartOrchestrationOptionsExtensions
{
+ ///
+ /// Gets the valid terminal orchestration statuses that can be used for deduplication and ID reuse policies.
+ ///
public static readonly OrchestrationRuntimeStatus[] ValidDedupeStatuses = new[]
{
OrchestrationRuntimeStatus.Completed,
@@ -33,4 +36,21 @@ public static StartOrchestrationOptions WithDedupeStatuses(
DedupeStatuses = dedupeStatuses.Select(s => s.ToString()).ToList(),
};
}
+
+ ///
+ /// Creates a new with the specified orchestration ID reuse policy.
+ ///
+ /// The base options to extend.
+ /// The orchestration ID reuse policy.
+ /// A new instance with the ID reuse policy set.
+ public static StartOrchestrationOptions WithIdReusePolicy(
+ this StartOrchestrationOptions options,
+ OrchestrationIdReusePolicy policy)
+ {
+ Check.NotNull(policy);
+ return options with
+ {
+ IdReusePolicy = policy,
+ };
+ }
}
diff --git a/src/Client/Grpc/GrpcDurableTaskClient.cs b/src/Client/Grpc/GrpcDurableTaskClient.cs
index b6a6c72d..b5532cdd 100644
--- a/src/Client/Grpc/GrpcDurableTaskClient.cs
+++ b/src/Client/Grpc/GrpcDurableTaskClient.cs
@@ -123,11 +123,20 @@ public override async Task ScheduleNewOrchestrationInstanceAsync(
request.ScheduledStartTimestamp = Timestamp.FromDateTimeOffset(startAt.Value.ToUniversalTime());
}
- // Set orchestration ID reuse policy for deduplication support
- // Note: This requires the protobuf to support OrchestrationIdReusePolicy field
- // If the protobuf doesn't support it yet, this will need to be updated when the protobuf is updated
- if (options?.DedupeStatuses != null && options.DedupeStatuses.Count > 0)
+ // Set orchestration ID reuse policy
+ // Priority: IdReusePolicy > DedupeStatuses
+ if (options?.IdReusePolicy is OrchestrationIdReusePolicy idReusePolicy)
{
+ // Use the new explicit ID reuse policy
+ P.OrchestrationIdReusePolicy? policy = ProtoUtils.ConvertToProtoReusePolicy(idReusePolicy);
+ if (policy != null)
+ {
+ request.OrchestrationIdReusePolicy = policy;
+ }
+ }
+ else if (options?.DedupeStatuses != null && options.DedupeStatuses.Count > 0)
+ {
+ // Fall back to legacy dedupe statuses for backward compatibility
// Parse and validate all status strings to enum first
ImmutableHashSet dedupeStatuses = options.DedupeStatuses
.Select(s =>
diff --git a/src/Client/Grpc/ProtoUtils.cs b/src/Client/Grpc/ProtoUtils.cs
index f307f43f..65dd6af3 100644
--- a/src/Client/Grpc/ProtoUtils.cs
+++ b/src/Client/Grpc/ProtoUtils.cs
@@ -12,6 +12,42 @@ namespace Microsoft.DurableTask.Client.Grpc;
///
public static class ProtoUtils
{
+ ///
+ /// Converts a public CreateOrchestrationAction to a protobuf CreateOrchestrationAction.
+ ///
+ /// The public action.
+ /// A protobuf CreateOrchestrationAction.
+ internal static P.CreateOrchestrationAction ConvertToProtoAction(
+ Microsoft.DurableTask.Client.CreateOrchestrationAction action)
+ => action switch
+ {
+ Microsoft.DurableTask.Client.CreateOrchestrationAction.Error => P.CreateOrchestrationAction.Error,
+ Microsoft.DurableTask.Client.CreateOrchestrationAction.Ignore => P.CreateOrchestrationAction.Ignore,
+ Microsoft.DurableTask.Client.CreateOrchestrationAction.Terminate => P.CreateOrchestrationAction.Terminate,
+ _ => throw new ArgumentOutOfRangeException(nameof(action), "Unexpected value"),
+ };
+
+#pragma warning disable 0618 // Referencing Obsolete member. This is intention as we are only converting it.
+ ///
+ /// Converts to .
+ ///
+ /// The orchestration status.
+ /// A .
+ internal static P.OrchestrationStatus ToGrpcStatus(this OrchestrationRuntimeStatus status)
+ => status switch
+ {
+ OrchestrationRuntimeStatus.Canceled => P.OrchestrationStatus.Canceled,
+ OrchestrationRuntimeStatus.Completed => P.OrchestrationStatus.Completed,
+ OrchestrationRuntimeStatus.ContinuedAsNew => P.OrchestrationStatus.ContinuedAsNew,
+ OrchestrationRuntimeStatus.Failed => P.OrchestrationStatus.Failed,
+ OrchestrationRuntimeStatus.Pending => P.OrchestrationStatus.Pending,
+ OrchestrationRuntimeStatus.Running => P.OrchestrationStatus.Running,
+ OrchestrationRuntimeStatus.Terminated => P.OrchestrationStatus.Terminated,
+ OrchestrationRuntimeStatus.Suspended => P.OrchestrationStatus.Suspended,
+ _ => throw new ArgumentOutOfRangeException(nameof(status), "Unexpected value"),
+ };
+#pragma warning restore 0618 // Referencing Obsolete member.
+
///
/// Gets the terminal orchestration statuses that are commonly used for deduplication.
/// These are the statuses that can be used in OrchestrationIdReusePolicy.
@@ -30,14 +66,15 @@ public static class ProtoUtils
///
/// Converts dedupe statuses (statuses that should NOT be replaced) to an OrchestrationIdReusePolicy
- /// with replaceable statuses (statuses that CAN be replaced).
+ /// with TERMINATE action for terminal statuses that CAN be replaced.
///
/// The orchestration statuses that should NOT be replaced. These are statuses for which an exception should be thrown if an orchestration already exists.
- /// An OrchestrationIdReusePolicy with replaceable statuses set, or null if all terminal statuses are dedupe statuses.
+ /// An OrchestrationIdReusePolicy with TERMINATE action and operation statuses set, or null if all terminal statuses are dedupe statuses.
///
- /// The policy uses "replaceableStatus" - these are statuses that CAN be replaced.
- /// dedupeStatuses are statuses that should NOT be replaced.
- /// So replaceableStatus = all terminal statuses MINUS dedupeStatuses.
+ /// This method maintains backward compatibility by converting dedupe statuses to the new policy format.
+ /// The policy will have action = TERMINATE and operationStatus = terminal statuses that can be replaced.
+ /// dedupeStatuses are statuses that should NOT be replaced (ERROR action).
+ /// So operationStatus = all terminal statuses MINUS dedupeStatuses.
///
public static P.OrchestrationIdReusePolicy? ConvertDedupeStatusesToReusePolicy(
IEnumerable? dedupeStatuses)
@@ -45,67 +82,75 @@ public static class ProtoUtils
ImmutableArray terminalStatuses = GetTerminalStatuses();
ImmutableHashSet dedupeStatusSet = dedupeStatuses?.ToImmutableHashSet() ?? ImmutableHashSet.Empty;
- P.OrchestrationIdReusePolicy policy = new();
+ P.OrchestrationIdReusePolicy policy = new()
+ {
+ Action = P.CreateOrchestrationAction.Terminate,
+ };
- // Add terminal statuses that are NOT in dedupeStatuses as replaceable
+ // Add terminal statuses that are NOT in dedupeStatuses to operation status (these can be terminated and replaced)
foreach (P.OrchestrationStatus terminalStatus in terminalStatuses.Where(status => !dedupeStatusSet.Contains(status)))
{
- policy.ReplaceableStatus.Add(terminalStatus);
+ policy.OperationStatus.Add(terminalStatus);
+ }
+
+ // Only return policy if we have operation statuses
+ return policy.OperationStatus.Count > 0 ? policy : null;
+ }
+
+ ///
+ /// Converts a public OrchestrationIdReusePolicy to a protobuf OrchestrationIdReusePolicy.
+ ///
+ /// The public orchestration ID reuse policy.
+ /// A protobuf OrchestrationIdReusePolicy.
+ public static P.OrchestrationIdReusePolicy? ConvertToProtoReusePolicy(
+ Microsoft.DurableTask.Client.OrchestrationIdReusePolicy? policy)
+ {
+ if (policy == null)
+ {
+ return null;
+ }
+
+ P.OrchestrationIdReusePolicy protoPolicy = new()
+ {
+ Action = ConvertToProtoAction(policy.Action),
+ };
+
+ foreach (OrchestrationRuntimeStatus status in policy.OperationStatuses)
+ {
+ protoPolicy.OperationStatus.Add(status.ToGrpcStatus());
}
- // Only return policy if we have replaceable statuses
- return policy.ReplaceableStatus.Count > 0 ? policy : null;
+ return protoPolicy;
}
///
- /// Converts an OrchestrationIdReusePolicy with replaceable statuses to dedupe statuses
- /// (statuses that should NOT be replaced).
+ /// Converts an OrchestrationIdReusePolicy to dedupe statuses (statuses that should NOT be replaced).
///
- /// The OrchestrationIdReusePolicy containing replaceable statuses.
- /// An array of orchestration statuses that should NOT be replaced, or null if all terminal statuses are replaceable.
+ /// The OrchestrationIdReusePolicy containing action and operation statuses.
+ /// An array of orchestration statuses that should NOT be replaced, or null if all terminal statuses can be replaced.
///
- /// The policy uses "replaceableStatus" - these are statuses that CAN be replaced.
- /// dedupeStatuses are statuses that should NOT be replaced (should throw exception).
- /// So dedupeStatuses = all terminal statuses MINUS replaceableStatus.
+ /// This method maintains backward compatibility by converting the new policy format to dedupe statuses.
+ /// For TERMINATE action: dedupeStatuses = all terminal statuses MINUS operationStatus.
+ /// For ERROR or IGNORE action: the behavior depends on the action semantics.
///
public static P.OrchestrationStatus[]? ConvertReusePolicyToDedupeStatuses(
P.OrchestrationIdReusePolicy? policy)
{
- if (policy == null || policy.ReplaceableStatus.Count == 0)
+ if (policy == null || policy.OperationStatus.Count == 0)
{
return null;
}
ImmutableArray terminalStatuses = GetTerminalStatuses();
- ImmutableHashSet replaceableStatusSet = policy.ReplaceableStatus.ToImmutableHashSet();
+ ImmutableHashSet operationStatusSet = policy.OperationStatus.ToImmutableHashSet();
- // Calculate dedupe statuses = terminal statuses - replaceable statuses
+ // For TERMINATE action: dedupe statuses = terminal statuses - operation status
+ // For other actions, the conversion may not be straightforward
P.OrchestrationStatus[] dedupeStatuses = terminalStatuses
- .Where(terminalStatus => !replaceableStatusSet.Contains(terminalStatus))
+ .Where(terminalStatus => !operationStatusSet.Contains(terminalStatus))
.ToArray();
// Only return if there are dedupe statuses
return dedupeStatuses.Length > 0 ? dedupeStatuses : null;
}
-
-#pragma warning disable 0618 // Referencing Obsolete member. This is intention as we are only converting it.
- ///
- /// Converts to .
- ///
- /// The orchestration status.
- /// A .
- internal static P.OrchestrationStatus ToGrpcStatus(this OrchestrationRuntimeStatus status)
- => status switch
- {
- OrchestrationRuntimeStatus.Canceled => P.OrchestrationStatus.Canceled,
- OrchestrationRuntimeStatus.Completed => P.OrchestrationStatus.Completed,
- OrchestrationRuntimeStatus.ContinuedAsNew => P.OrchestrationStatus.ContinuedAsNew,
- OrchestrationRuntimeStatus.Failed => P.OrchestrationStatus.Failed,
- OrchestrationRuntimeStatus.Pending => P.OrchestrationStatus.Pending,
- OrchestrationRuntimeStatus.Running => P.OrchestrationStatus.Running,
- OrchestrationRuntimeStatus.Terminated => P.OrchestrationStatus.Terminated,
- OrchestrationRuntimeStatus.Suspended => P.OrchestrationStatus.Suspended,
- _ => throw new ArgumentOutOfRangeException(nameof(status), "Unexpected value"),
- };
-#pragma warning restore 0618 // Referencing Obsolete member.
}
diff --git a/src/Grpc/orchestrator_service.proto b/src/Grpc/orchestrator_service.proto
index 196c88da..ef3b6d2b 100644
--- a/src/Grpc/orchestrator_service.proto
+++ b/src/Grpc/orchestrator_service.proto
@@ -55,6 +55,12 @@ enum OrchestrationStatus {
ORCHESTRATION_STATUS_SUSPENDED = 7;
}
+enum CreateOrchestrationAction {
+ CREATE_ORCHESTRATION_ACTION_ERROR = 0;
+ CREATE_ORCHESTRATION_ACTION_IGNORE = 1;
+ CREATE_ORCHESTRATION_ACTION_TERMINATE = 2;
+}
+
message ParentInstanceInfo {
int32 taskScheduledId = 1;
google.protobuf.StringValue name = 2;
@@ -380,8 +386,8 @@ message CreateInstanceRequest {
}
message OrchestrationIdReusePolicy {
- repeated OrchestrationStatus replaceableStatus = 1;
- reserved 2;
+ repeated OrchestrationStatus operationStatus = 1;
+ CreateOrchestrationAction action = 2;
}
message CreateInstanceResponse {
diff --git a/test/Client/Grpc.Tests/OrchestrationIdReusePolicyTests.cs b/test/Client/Grpc.Tests/OrchestrationIdReusePolicyTests.cs
new file mode 100644
index 00000000..cea195a7
--- /dev/null
+++ b/test/Client/Grpc.Tests/OrchestrationIdReusePolicyTests.cs
@@ -0,0 +1,151 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using Microsoft.DurableTask.Client;
+using P = Microsoft.DurableTask.Protobuf;
+
+namespace Microsoft.DurableTask.Client.Grpc.Tests;
+
+public class OrchestrationIdReusePolicyTests
+{
+ [Fact]
+ public void OrchestrationIdReusePolicy_Error_CreatesCorrectPolicy()
+ {
+ // Arrange & Act
+ OrchestrationIdReusePolicy policy = OrchestrationIdReusePolicy.Error(
+ OrchestrationRuntimeStatus.Running,
+ OrchestrationRuntimeStatus.Pending);
+
+ // Assert
+ policy.Action.Should().Be(CreateOrchestrationAction.Error);
+ policy.OperationStatuses.Should().HaveCount(2);
+ policy.OperationStatuses.Should().Contain(OrchestrationRuntimeStatus.Running);
+ policy.OperationStatuses.Should().Contain(OrchestrationRuntimeStatus.Pending);
+ }
+
+ [Fact]
+ public void OrchestrationIdReusePolicy_Ignore_CreatesCorrectPolicy()
+ {
+ // Arrange & Act
+ OrchestrationIdReusePolicy policy = OrchestrationIdReusePolicy.Ignore(
+ OrchestrationRuntimeStatus.Completed,
+ OrchestrationRuntimeStatus.Failed);
+
+ // Assert
+ policy.Action.Should().Be(CreateOrchestrationAction.Ignore);
+ policy.OperationStatuses.Should().HaveCount(2);
+ policy.OperationStatuses.Should().Contain(OrchestrationRuntimeStatus.Completed);
+ policy.OperationStatuses.Should().Contain(OrchestrationRuntimeStatus.Failed);
+ }
+
+ [Fact]
+ public void OrchestrationIdReusePolicy_Terminate_CreatesCorrectPolicy()
+ {
+ // Arrange & Act
+ OrchestrationIdReusePolicy policy = OrchestrationIdReusePolicy.Terminate(
+ OrchestrationRuntimeStatus.Running);
+
+ // Assert
+ policy.Action.Should().Be(CreateOrchestrationAction.Terminate);
+ policy.OperationStatuses.Should().HaveCount(1);
+ policy.OperationStatuses.Should().Contain(OrchestrationRuntimeStatus.Running);
+ }
+
+ [Fact]
+ public void ConvertToProtoReusePolicy_NullPolicy_ReturnsNull()
+ {
+ // Arrange
+ OrchestrationIdReusePolicy? policy = null;
+
+ // Act
+ P.OrchestrationIdReusePolicy? result = ProtoUtils.ConvertToProtoReusePolicy(policy);
+
+ // Assert
+ result.Should().BeNull();
+ }
+
+ [Fact]
+ public void ConvertToProtoReusePolicy_ErrorAction_ConvertsCorrectly()
+ {
+ // Arrange
+ OrchestrationIdReusePolicy policy = OrchestrationIdReusePolicy.Error(
+ OrchestrationRuntimeStatus.Running,
+ OrchestrationRuntimeStatus.Pending);
+
+ // Act
+ P.OrchestrationIdReusePolicy? result = ProtoUtils.ConvertToProtoReusePolicy(policy);
+
+ // Assert
+ result.Should().NotBeNull();
+ result!.Action.Should().Be(P.CreateOrchestrationAction.Error);
+ result.OperationStatus.Should().HaveCount(2);
+ result.OperationStatus.Should().Contain(P.OrchestrationStatus.Running);
+ result.OperationStatus.Should().Contain(P.OrchestrationStatus.Pending);
+ }
+
+ [Fact]
+ public void ConvertToProtoReusePolicy_IgnoreAction_ConvertsCorrectly()
+ {
+ // Arrange
+ OrchestrationIdReusePolicy policy = OrchestrationIdReusePolicy.Ignore(
+ OrchestrationRuntimeStatus.Completed);
+
+ // Act
+ P.OrchestrationIdReusePolicy? result = ProtoUtils.ConvertToProtoReusePolicy(policy);
+
+ // Assert
+ result.Should().NotBeNull();
+ result!.Action.Should().Be(P.CreateOrchestrationAction.Ignore);
+ result.OperationStatus.Should().HaveCount(1);
+ result.OperationStatus.Should().Contain(P.OrchestrationStatus.Completed);
+ }
+
+ [Fact]
+ public void ConvertToProtoReusePolicy_TerminateAction_ConvertsCorrectly()
+ {
+ // Arrange
+ OrchestrationIdReusePolicy policy = OrchestrationIdReusePolicy.Terminate(
+ OrchestrationRuntimeStatus.Failed,
+ OrchestrationRuntimeStatus.Terminated);
+
+ // Act
+ P.OrchestrationIdReusePolicy? result = ProtoUtils.ConvertToProtoReusePolicy(policy);
+
+ // Assert
+ result.Should().NotBeNull();
+ result!.Action.Should().Be(P.CreateOrchestrationAction.Terminate);
+ result.OperationStatus.Should().HaveCount(2);
+ result.OperationStatus.Should().Contain(P.OrchestrationStatus.Failed);
+ result.OperationStatus.Should().Contain(P.OrchestrationStatus.Terminated);
+ }
+
+ [Fact]
+ public void ConvertToProtoAction_AllActions_ConvertCorrectly()
+ {
+ // Assert - Error
+ ProtoUtils.ConvertToProtoAction(CreateOrchestrationAction.Error)
+ .Should().Be(P.CreateOrchestrationAction.Error);
+
+ // Assert - Ignore
+ ProtoUtils.ConvertToProtoAction(CreateOrchestrationAction.Ignore)
+ .Should().Be(P.CreateOrchestrationAction.Ignore);
+
+ // Assert - Terminate
+ ProtoUtils.ConvertToProtoAction(CreateOrchestrationAction.Terminate)
+ .Should().Be(P.CreateOrchestrationAction.Terminate);
+ }
+
+ [Fact]
+ public void WithIdReusePolicy_SetsPolicy()
+ {
+ // Arrange
+ var options = new StartOrchestrationOptions();
+ var policy = OrchestrationIdReusePolicy.Terminate(OrchestrationRuntimeStatus.Running);
+
+ // Act
+ StartOrchestrationOptions result = options.WithIdReusePolicy(policy);
+
+ // Assert
+ result.IdReusePolicy.Should().Be(policy);
+ }
+}
diff --git a/test/Client/Grpc.Tests/ProtoUtilsTests.cs b/test/Client/Grpc.Tests/ProtoUtilsTests.cs
index 4db7a884..50292d94 100644
--- a/test/Client/Grpc.Tests/ProtoUtilsTests.cs
+++ b/test/Client/Grpc.Tests/ProtoUtilsTests.cs
@@ -47,7 +47,7 @@ public void ConvertDedupeStatusesToReusePolicy_EmptyArray_ReturnsPolicyWithAllTe
// Assert
// Empty array means no dedupe statuses, so all terminal statuses are replaceable
result.Should().NotBeNull();
- result!.ReplaceableStatus.Should().HaveCount(4);
+ result!.OperationStatus.Should().HaveCount(4);
}
[Fact]
@@ -76,12 +76,12 @@ public void ConvertDedupeStatusesToReusePolicy_NoDedupeStatuses_ReturnsPolicyWit
// Assert
// When no dedupe statuses, all terminal statuses should be replaceable
result.Should().NotBeNull();
- result!.ReplaceableStatus.Should().HaveCount(4);
- result.ReplaceableStatus.Should().Contain(P.OrchestrationStatus.Completed);
- result.ReplaceableStatus.Should().Contain(P.OrchestrationStatus.Failed);
- result.ReplaceableStatus.Should().Contain(P.OrchestrationStatus.Terminated);
+ result!.OperationStatus.Should().HaveCount(4);
+ result.OperationStatus.Should().Contain(P.OrchestrationStatus.Completed);
+ result.OperationStatus.Should().Contain(P.OrchestrationStatus.Failed);
+ result.OperationStatus.Should().Contain(P.OrchestrationStatus.Terminated);
#pragma warning disable CS0618 // Type or member is obsolete
- result.ReplaceableStatus.Should().Contain(P.OrchestrationStatus.Canceled);
+ result.OperationStatus.Should().Contain(P.OrchestrationStatus.Canceled);
#pragma warning restore CS0618
}
@@ -96,13 +96,13 @@ public void ConvertDedupeStatusesToReusePolicy_SingleDedupeStatus_ReturnsPolicyW
// Assert
result.Should().NotBeNull();
- result!.ReplaceableStatus.Should().HaveCount(3);
- result.ReplaceableStatus.Should().Contain(P.OrchestrationStatus.Failed);
- result.ReplaceableStatus.Should().Contain(P.OrchestrationStatus.Terminated);
+ result!.OperationStatus.Should().HaveCount(3);
+ result.OperationStatus.Should().Contain(P.OrchestrationStatus.Failed);
+ result.OperationStatus.Should().Contain(P.OrchestrationStatus.Terminated);
#pragma warning disable CS0618 // Type or member is obsolete
- result.ReplaceableStatus.Should().Contain(P.OrchestrationStatus.Canceled);
+ result.OperationStatus.Should().Contain(P.OrchestrationStatus.Canceled);
#pragma warning restore CS0618
- result.ReplaceableStatus.Should().NotContain(P.OrchestrationStatus.Completed);
+ result.OperationStatus.Should().NotContain(P.OrchestrationStatus.Completed);
}
[Fact]
@@ -120,13 +120,13 @@ public void ConvertDedupeStatusesToReusePolicy_MultipleDedupeStatuses_ReturnsPol
// Assert
result.Should().NotBeNull();
- result!.ReplaceableStatus.Should().HaveCount(2);
- result.ReplaceableStatus.Should().Contain(P.OrchestrationStatus.Terminated);
+ result!.OperationStatus.Should().HaveCount(2);
+ result.OperationStatus.Should().Contain(P.OrchestrationStatus.Terminated);
#pragma warning disable CS0618 // Type or member is obsolete
- result.ReplaceableStatus.Should().Contain(P.OrchestrationStatus.Canceled);
+ result.OperationStatus.Should().Contain(P.OrchestrationStatus.Canceled);
#pragma warning restore CS0618
- result.ReplaceableStatus.Should().NotContain(P.OrchestrationStatus.Completed);
- result.ReplaceableStatus.Should().NotContain(P.OrchestrationStatus.Failed);
+ result.OperationStatus.Should().NotContain(P.OrchestrationStatus.Completed);
+ result.OperationStatus.Should().NotContain(P.OrchestrationStatus.Failed);
}
[Fact]
@@ -145,10 +145,10 @@ public void ConvertDedupeStatusesToReusePolicy_DuplicateDedupeStatuses_HandlesDu
// Assert
result.Should().NotBeNull();
- result!.ReplaceableStatus.Should().HaveCount(2);
- result.ReplaceableStatus.Should().Contain(P.OrchestrationStatus.Terminated);
+ result!.OperationStatus.Should().HaveCount(2);
+ result.OperationStatus.Should().Contain(P.OrchestrationStatus.Terminated);
#pragma warning disable CS0618 // Type or member is obsolete
- result.ReplaceableStatus.Should().Contain(P.OrchestrationStatus.Canceled);
+ result.OperationStatus.Should().Contain(P.OrchestrationStatus.Canceled);
#pragma warning restore CS0618
}
@@ -168,13 +168,13 @@ public void ConvertDedupeStatusesToReusePolicy_NonTerminalStatus_IgnoresNonTermi
// Assert
result.Should().NotBeNull();
- result!.ReplaceableStatus.Should().HaveCount(3);
- result.ReplaceableStatus.Should().Contain(P.OrchestrationStatus.Failed);
- result.ReplaceableStatus.Should().Contain(P.OrchestrationStatus.Terminated);
+ result!.OperationStatus.Should().HaveCount(3);
+ result.OperationStatus.Should().Contain(P.OrchestrationStatus.Failed);
+ result.OperationStatus.Should().Contain(P.OrchestrationStatus.Terminated);
#pragma warning disable CS0618 // Type or member is obsolete
- result.ReplaceableStatus.Should().Contain(P.OrchestrationStatus.Canceled);
+ result.OperationStatus.Should().Contain(P.OrchestrationStatus.Canceled);
#pragma warning restore CS0618
- result.ReplaceableStatus.Should().NotContain(P.OrchestrationStatus.Completed);
+ result.OperationStatus.Should().NotContain(P.OrchestrationStatus.Completed);
}
[Fact]
@@ -211,7 +211,7 @@ public void ConvertReusePolicyToDedupeStatuses_AllTerminalStatusesReplaceable_Re
ImmutableArray terminalStatuses = ProtoUtils.GetTerminalStatuses();
foreach (var status in terminalStatuses)
{
- policy.ReplaceableStatus.Add(status);
+ policy.OperationStatus.Add(status);
}
// Act
@@ -222,11 +222,11 @@ public void ConvertReusePolicyToDedupeStatuses_AllTerminalStatusesReplaceable_Re
}
[Fact]
- public void ConvertReusePolicyToDedupeStatuses_SingleReplaceableStatus_ReturnsRemainingStatuses()
+ public void ConvertReusePolicyToDedupeStatuses_SingleOperationStatus_ReturnsRemainingStatuses()
{
// Arrange
var policy = new P.OrchestrationIdReusePolicy();
- policy.ReplaceableStatus.Add(P.OrchestrationStatus.Completed);
+ policy.OperationStatus.Add(P.OrchestrationStatus.Completed);
// Act
P.OrchestrationStatus[]? result = ProtoUtils.ConvertReusePolicyToDedupeStatuses(policy);
@@ -243,12 +243,12 @@ public void ConvertReusePolicyToDedupeStatuses_SingleReplaceableStatus_ReturnsRe
}
[Fact]
- public void ConvertReusePolicyToDedupeStatuses_MultipleReplaceableStatuses_ReturnsRemainingStatuses()
+ public void ConvertReusePolicyToDedupeStatuses_MultipleOperationStatuses_ReturnsRemainingStatuses()
{
// Arrange
var policy = new P.OrchestrationIdReusePolicy();
- policy.ReplaceableStatus.Add(P.OrchestrationStatus.Completed);
- policy.ReplaceableStatus.Add(P.OrchestrationStatus.Failed);
+ policy.OperationStatus.Add(P.OrchestrationStatus.Completed);
+ policy.OperationStatus.Add(P.OrchestrationStatus.Failed);
// Act
P.OrchestrationStatus[]? result = ProtoUtils.ConvertReusePolicyToDedupeStatuses(policy);
@@ -269,9 +269,9 @@ public void ConvertReusePolicyToDedupeStatuses_NonTerminalStatusInPolicy_Ignores
{
// Arrange
var policy = new P.OrchestrationIdReusePolicy();
- policy.ReplaceableStatus.Add(P.OrchestrationStatus.Completed);
- policy.ReplaceableStatus.Add(P.OrchestrationStatus.Running); // Non-terminal status
- policy.ReplaceableStatus.Add(P.OrchestrationStatus.Pending); // Non-terminal status
+ policy.OperationStatus.Add(P.OrchestrationStatus.Completed);
+ policy.OperationStatus.Add(P.OrchestrationStatus.Running); // Non-terminal status
+ policy.OperationStatus.Add(P.OrchestrationStatus.Pending); // Non-terminal status
// Act
P.OrchestrationStatus[]? result = ProtoUtils.ConvertReusePolicyToDedupeStatuses(policy);
@@ -288,13 +288,13 @@ public void ConvertReusePolicyToDedupeStatuses_NonTerminalStatusInPolicy_Ignores
}
[Fact]
- public void ConvertReusePolicyToDedupeStatuses_DuplicateReplaceableStatuses_HandlesDuplicates()
+ public void ConvertReusePolicyToDedupeStatuses_DuplicateOperationStatuses_HandlesDuplicates()
{
// Arrange
var policy = new P.OrchestrationIdReusePolicy();
- policy.ReplaceableStatus.Add(P.OrchestrationStatus.Completed);
- policy.ReplaceableStatus.Add(P.OrchestrationStatus.Completed); // Duplicate
- policy.ReplaceableStatus.Add(P.OrchestrationStatus.Failed);
+ policy.OperationStatus.Add(P.OrchestrationStatus.Completed);
+ policy.OperationStatus.Add(P.OrchestrationStatus.Completed); // Duplicate
+ policy.OperationStatus.Add(P.OrchestrationStatus.Failed);
// Act
P.OrchestrationStatus[]? result = ProtoUtils.ConvertReusePolicyToDedupeStatuses(policy);
@@ -332,8 +332,8 @@ public void ConvertReusePolicyToDedupeStatuses_ThenConvertBack_ReturnsOriginalPo
{
// Arrange
var policy = new P.OrchestrationIdReusePolicy();
- policy.ReplaceableStatus.Add(P.OrchestrationStatus.Completed);
- policy.ReplaceableStatus.Add(P.OrchestrationStatus.Failed);
+ policy.OperationStatus.Add(P.OrchestrationStatus.Completed);
+ policy.OperationStatus.Add(P.OrchestrationStatus.Failed);
// Act
P.OrchestrationStatus[]? dedupeStatuses = ProtoUtils.ConvertReusePolicyToDedupeStatuses(policy);
@@ -341,7 +341,7 @@ public void ConvertReusePolicyToDedupeStatuses_ThenConvertBack_ReturnsOriginalPo
// Assert
convertedBack.Should().NotBeNull();
- convertedBack!.ReplaceableStatus.Should().BeEquivalentTo(policy.ReplaceableStatus);
+ convertedBack!.OperationStatus.Should().BeEquivalentTo(policy.OperationStatus);
}
[Fact]
@@ -368,7 +368,7 @@ public void ConvertReusePolicyToDedupeStatuses_AllStatuses_ThenConvertBack_Retur
ImmutableArray terminalStatuses = ProtoUtils.GetTerminalStatuses();
foreach (var status in terminalStatuses)
{
- policy.ReplaceableStatus.Add(status);
+ policy.OperationStatus.Add(status);
}
// Act
@@ -380,8 +380,8 @@ public void ConvertReusePolicyToDedupeStatuses_AllStatuses_ThenConvertBack_Retur
// null dedupe statuses -> all are replaceable -> policy with all statuses
dedupeStatuses.Should().BeNull();
convertedBack.Should().NotBeNull();
- convertedBack!.ReplaceableStatus.Should().HaveCount(4);
- convertedBack.ReplaceableStatus.Should().BeEquivalentTo(policy.ReplaceableStatus);
+ convertedBack!.OperationStatus.Should().HaveCount(4);
+ convertedBack.OperationStatus.Should().BeEquivalentTo(policy.OperationStatus);
}
[Fact]
@@ -416,7 +416,7 @@ public void ConvertReusePolicyToDedupeStatuses_EmptyPolicy_ThenConvertBack_Retur
// null dedupe statuses -> all terminal statuses are replaceable -> policy with all statuses
dedupeStatuses.Should().BeNull();
convertedBack.Should().NotBeNull();
- convertedBack!.ReplaceableStatus.Should().HaveCount(4);
+ convertedBack!.OperationStatus.Should().HaveCount(4);
}
[Theory]