diff --git a/src/Dapr.Workflow.Versioning.Runtime/VersionStrategies/DateSuffixVersionStrategy.cs b/src/Dapr.Workflow.Versioning.Runtime/VersionStrategies/DateSuffixVersionStrategy.cs
deleted file mode 100644
index 5dc6d9eb3..000000000
--- a/src/Dapr.Workflow.Versioning.Runtime/VersionStrategies/DateSuffixVersionStrategy.cs
+++ /dev/null
@@ -1,112 +0,0 @@
-// ------------------------------------------------------------------------
-// Copyright 2026 The Dapr Authors
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-// http://www.apache.org/licenses/LICENSE-2.0
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ------------------------------------------------------------------------
-
-using System.Globalization;
-using Microsoft.Extensions.Options;
-
-namespace Dapr.Workflow.Versioning;
-
-///
-/// Strategy that derives a date-based version from a trailing suffix
-/// (for example, MyWorkflow20260212 with format yyyyMMdd).
-///
-public sealed class DateSuffixVersionStrategy(IOptionsMonitor? optionsMonitor = null)
- : IWorkflowVersionStrategy, IWorkflowVersionStrategyContextConsumer
-{
- private DateSuffixVersionStrategyOptions _options = new();
-
- ///
- public void Configure(WorkflowVersionStrategyContext context)
- {
- var optionsName = string.IsNullOrWhiteSpace(context.OptionsName)
- ? Options.DefaultName
- : context.OptionsName;
-
- if (optionsMonitor is not null)
- {
- _options = optionsMonitor.Get(optionsName);
- }
- }
-
- ///
- public bool TryParse(string typeName, out string canonicalName, out string version)
- {
- canonicalName = string.Empty;
- version = string.Empty;
-
- if (string.IsNullOrWhiteSpace(typeName))
- return false;
-
- var format = string.IsNullOrWhiteSpace(_options.DateFormat) ? "yyyyMMdd" : _options.DateFormat;
- if (typeName.Length <= format.Length)
- return ApplyNoSuffix(typeName, out canonicalName, out version);
-
- var suffix = typeName.Substring(typeName.Length - format.Length);
- if (!TryParseDate(suffix, format, out _))
- return ApplyNoSuffix(typeName, out canonicalName, out version);
-
- canonicalName = typeName.Substring(0, typeName.Length - format.Length);
- if (string.IsNullOrEmpty(canonicalName))
- return false;
-
- version = suffix;
- return true;
- }
-
- ///
- public int Compare(string? v1, string? v2)
- {
- if (ReferenceEquals(v1, v2)) return 0;
- if (v1 is null) return -1;
- if (v2 is null) return 1;
-
- var format = string.IsNullOrWhiteSpace(_options.DateFormat) ? "yyyyMMdd" : _options.DateFormat;
- var ok1 = TryParseDate(v1.Trim(), format, out var d1);
- var ok2 = TryParseDate(v2.Trim(), format, out var d2);
-
- switch (ok1)
- {
- case true when ok2:
- return d1.CompareTo(d2);
- case true:
- return 1;
- }
-
- if (ok2) return -1;
-
- return StringComparer.Ordinal.Compare(v1, v2);
- }
-
- private bool ApplyNoSuffix(string typeName, out string canonicalName, out string version)
- {
- canonicalName = string.Empty;
- version = string.Empty;
-
- if (!_options.AllowNoSuffix)
- return false;
-
- canonicalName = typeName;
- version = string.IsNullOrWhiteSpace(_options.DefaultVersion) ? "0" : _options.DefaultVersion;
- return true;
- }
-
- private static bool TryParseDate(string value, string format, out DateTime date)
- {
- return DateTime.TryParseExact(
- value,
- format,
- CultureInfo.InvariantCulture,
- DateTimeStyles.None,
- out date);
- }
-}
diff --git a/src/Dapr.Workflow.Versioning.Runtime/VersionStrategies/DateVersionStrategy.cs b/src/Dapr.Workflow.Versioning.Runtime/VersionStrategies/DateVersionStrategy.cs
new file mode 100644
index 000000000..ca655830f
--- /dev/null
+++ b/src/Dapr.Workflow.Versioning.Runtime/VersionStrategies/DateVersionStrategy.cs
@@ -0,0 +1,177 @@
+// ------------------------------------------------------------------------
+// Copyright 2026 The Dapr Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ------------------------------------------------------------------------
+
+using System.Globalization;
+using Microsoft.Extensions.Options;
+
+namespace Dapr.Workflow.Versioning;
+
+///
+/// Strategy that derives a date-based version from a trailing suffix
+/// (for example, MyWorkflow20260212 with format yyyyMMdd).
+///
+public sealed class DateVersionStrategy(IOptionsMonitor? optionsMonitor = null)
+ : IWorkflowVersionStrategy, IWorkflowVersionStrategyContextConsumer
+{
+ private DateVersionStrategyOptions _options = new();
+
+ ///
+ public void Configure(WorkflowVersionStrategyContext context)
+ {
+ var optionsName = string.IsNullOrWhiteSpace(context.OptionsName)
+ ? Options.DefaultName
+ : context.OptionsName;
+
+ if (optionsMonitor is not null)
+ {
+ _options = optionsMonitor.Get(optionsName);
+ }
+ }
+
+ ///
+ public bool TryParse(string typeName, out string canonicalName, out string version)
+ {
+ canonicalName = string.Empty;
+ version = string.Empty;
+
+ if (string.IsNullOrWhiteSpace(typeName))
+ return false;
+
+ var format = string.IsNullOrWhiteSpace(_options.DateFormat) ? "yyyyMMdd" : _options.DateFormat;
+ var suffixLength = GetFormattedLength(format);
+ var prefix = _options.Prefix ?? string.Empty;
+ var comparison = _options.IgnorePrefixCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal;
+ var totalSuffixLength = suffixLength + prefix.Length;
+ if (typeName.Length <= totalSuffixLength)
+ return ApplyNoSuffix(typeName, prefix, comparison, out canonicalName, out version);
+
+ var suffixStart = typeName.Length - suffixLength;
+ var dateSuffix = typeName.Substring(suffixStart, suffixLength);
+ if (!TryParseDate(dateSuffix, format, _options.IgnorePrefixCase, out _))
+ return ApplyNoSuffix(typeName, prefix, comparison, out canonicalName, out version);
+
+ if (!string.IsNullOrEmpty(prefix))
+ {
+ var prefixStart = suffixStart - prefix.Length;
+ if (prefixStart < 1)
+ return false;
+
+ var candidatePrefix = typeName.Substring(prefixStart, prefix.Length);
+ if (!string.Equals(candidatePrefix, prefix, comparison))
+ return false;
+
+ canonicalName = typeName.Substring(0, prefixStart);
+ }
+ else
+ {
+ canonicalName = typeName.Substring(0, suffixStart);
+ }
+
+ if (string.IsNullOrEmpty(canonicalName))
+ return false;
+
+ version = dateSuffix;
+ return true;
+ }
+
+ ///
+ public int Compare(string? v1, string? v2)
+ {
+ if (ReferenceEquals(v1, v2)) return 0;
+ if (v1 is null) return -1;
+ if (v2 is null) return 1;
+
+ var format = string.IsNullOrWhiteSpace(_options.DateFormat) ? "yyyyMMdd" : _options.DateFormat;
+ var ok1 = TryParseDate(v1.Trim(), format, _options.IgnorePrefixCase, out var d1);
+ var ok2 = TryParseDate(v2.Trim(), format, _options.IgnorePrefixCase, out var d2);
+
+ switch (ok1)
+ {
+ case true when ok2:
+ return d1.CompareTo(d2);
+ case true:
+ return 1;
+ }
+
+ if (ok2) return -1;
+
+ return StringComparer.Ordinal.Compare(v1, v2);
+ }
+
+ private bool ApplyNoSuffix(
+ string typeName,
+ string prefix,
+ StringComparison comparison,
+ out string canonicalName,
+ out string version)
+ {
+ canonicalName = string.Empty;
+ version = string.Empty;
+
+ if (!_options.AllowNoSuffix)
+ return false;
+
+ if (!string.IsNullOrEmpty(prefix) && typeName.EndsWith(prefix, comparison))
+ return false;
+
+ canonicalName = typeName;
+ version = string.IsNullOrWhiteSpace(_options.DefaultVersion) ? "0" : _options.DefaultVersion;
+ return true;
+ }
+
+ private static bool TryParseDate(string value, string format, bool ignorePrefixCase, out DateTime date)
+ {
+ if (DateTime.TryParseExact(
+ value,
+ format,
+ CultureInfo.InvariantCulture,
+ DateTimeStyles.None,
+ out date))
+ {
+ return true;
+ }
+
+ if (!ignorePrefixCase)
+ return false;
+
+ var upper = value.ToUpperInvariant();
+ if (!string.Equals(upper, value, StringComparison.Ordinal) &&
+ DateTime.TryParseExact(
+ upper,
+ format,
+ CultureInfo.InvariantCulture,
+ DateTimeStyles.None,
+ out date))
+ {
+ return true;
+ }
+
+ var lower = value.ToLowerInvariant();
+ if (!string.Equals(lower, value, StringComparison.Ordinal))
+ {
+ return DateTime.TryParseExact(
+ lower,
+ format,
+ CultureInfo.InvariantCulture,
+ DateTimeStyles.None,
+ out date);
+ }
+
+ return false;
+ }
+
+ private static int GetFormattedLength(string format)
+ {
+ return DateTime.UnixEpoch.ToString(format, CultureInfo.InvariantCulture).Length;
+ }
+}
diff --git a/src/Dapr.Workflow.Versioning.Runtime/VersionStrategies/DateSuffixVersionStrategyOptions.cs b/src/Dapr.Workflow.Versioning.Runtime/VersionStrategies/DateVersionStrategyOptions.cs
similarity index 72%
rename from src/Dapr.Workflow.Versioning.Runtime/VersionStrategies/DateSuffixVersionStrategyOptions.cs
rename to src/Dapr.Workflow.Versioning.Runtime/VersionStrategies/DateVersionStrategyOptions.cs
index 454206779..0c3e92a8f 100644
--- a/src/Dapr.Workflow.Versioning.Runtime/VersionStrategies/DateSuffixVersionStrategyOptions.cs
+++ b/src/Dapr.Workflow.Versioning.Runtime/VersionStrategies/DateVersionStrategyOptions.cs
@@ -14,9 +14,9 @@
namespace Dapr.Workflow.Versioning;
///
-/// Options for .
+/// Options for .
///
-public sealed class DateSuffixVersionStrategyOptions
+public sealed class DateVersionStrategyOptions
{
///
/// Gets or sets the date format expected at the end of the workflow type name.
@@ -24,6 +24,17 @@ public sealed class DateSuffixVersionStrategyOptions
///
public string DateFormat { get; set; } = "yyyyMMdd";
+ ///
+ /// Gets or sets the prefix expected before the date suffix (for example, "V" in MyWorkflowV20260212).
+ /// Set to an empty string to require no prefix.
+ ///
+ public string Prefix { get; set; } = string.Empty;
+
+ ///
+ /// Gets or sets a value indicating whether prefix matching ignores case.
+ ///
+ public bool IgnorePrefixCase { get; set; }
+
///
/// Gets or sets a value indicating whether names without a date suffix are allowed.
/// When enabled, the default version is applied.
diff --git a/src/Dapr.Workflow.Versioning.Runtime/VersionStrategies/DelimitedSuffixVersionStrategy.cs b/src/Dapr.Workflow.Versioning.Runtime/VersionStrategies/DelimitedSuffixVersionStrategy.cs
deleted file mode 100644
index 02dbd17a2..000000000
--- a/src/Dapr.Workflow.Versioning.Runtime/VersionStrategies/DelimitedSuffixVersionStrategy.cs
+++ /dev/null
@@ -1,104 +0,0 @@
-// ------------------------------------------------------------------------
-// Copyright 2026 The Dapr Authors
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-// http://www.apache.org/licenses/LICENSE-2.0
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ------------------------------------------------------------------------
-
-using System.Globalization;
-using Microsoft.Extensions.Options;
-
-namespace Dapr.Workflow.Versioning;
-
-///
-/// Strategy that derives the version from a delimiter-separated suffix (for example, MyWorkflow-1).
-///
-public sealed class DelimitedSuffixVersionStrategy(
- IOptionsMonitor? optionsMonitor = null)
- : IWorkflowVersionStrategy, IWorkflowVersionStrategyContextConsumer
-{
- private DelimitedSuffixVersionStrategyOptions _options = new();
-
- ///
- public void Configure(WorkflowVersionStrategyContext context)
- {
- var optionsName = string.IsNullOrWhiteSpace(context.OptionsName)
- ? Options.DefaultName
- : context.OptionsName;
-
- if (optionsMonitor is not null)
- {
- _options = optionsMonitor.Get(optionsName);
- }
- }
-
- ///
- public bool TryParse(string typeName, out string canonicalName, out string version)
- {
- canonicalName = string.Empty;
- version = string.Empty;
-
- if (string.IsNullOrWhiteSpace(typeName))
- return false;
-
- var delimiter = _options.Delimiter ?? string.Empty;
- if (delimiter.Length == 0)
- return false;
-
- var comparison = _options.IgnoreDelimiterCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal;
- var delimiterIndex = typeName.LastIndexOf(delimiter, comparison);
-
- if (delimiterIndex < 0)
- {
- if (_options.AllowNoSuffix)
- {
- canonicalName = typeName;
- version = string.IsNullOrWhiteSpace(_options.DefaultVersion) ? "0" : _options.DefaultVersion;
- return true;
- }
-
- return false;
- }
-
- var versionStart = delimiterIndex + delimiter.Length;
- if (delimiterIndex == 0 || versionStart >= typeName.Length)
- return false;
-
- canonicalName = typeName[..delimiterIndex];
- version = typeName[versionStart..];
-
- return !string.IsNullOrEmpty(canonicalName) && !string.IsNullOrEmpty(version);
- }
-
- ///
- public int Compare(string? v1, string? v2)
- {
- if (ReferenceEquals(v1, v2)) return 0;
- if (v1 is null) return -1;
- if (v2 is null) return 1;
-
- var s1 = v1.Trim();
- var s2 = v2.Trim();
-
- var ok1 = long.TryParse(s1, NumberStyles.None, CultureInfo.InvariantCulture, out var n1);
- var ok2 = long.TryParse(s2, NumberStyles.None, CultureInfo.InvariantCulture, out var n2);
-
- switch (ok1)
- {
- case true when ok2:
- return n1.CompareTo(n2);
- case true:
- return 1;
- }
-
- if (ok2) return -1;
-
- return StringComparer.Ordinal.Compare(s1, s2);
- }
-}
diff --git a/src/Dapr.Workflow.Versioning.Runtime/VersionStrategies/DelimitedSuffixVersionStrategyOptions.cs b/src/Dapr.Workflow.Versioning.Runtime/VersionStrategies/DelimitedSuffixVersionStrategyOptions.cs
deleted file mode 100644
index 713ac8778..000000000
--- a/src/Dapr.Workflow.Versioning.Runtime/VersionStrategies/DelimitedSuffixVersionStrategyOptions.cs
+++ /dev/null
@@ -1,41 +0,0 @@
-// ------------------------------------------------------------------------
-// Copyright 2026 The Dapr Authors
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-// http://www.apache.org/licenses/LICENSE-2.0
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ------------------------------------------------------------------------
-
-namespace Dapr.Workflow.Versioning;
-
-///
-/// Options for .
-///
-public sealed class DelimitedSuffixVersionStrategyOptions
-{
- ///
- /// Gets or sets the delimiter separating the canonical name from the version.
- ///
- public string Delimiter { get; set; } = "-";
-
- ///
- /// Gets or sets a value indicating whether delimiter matching ignores case.
- ///
- public bool IgnoreDelimiterCase { get; set; }
-
- ///
- /// Gets or sets a value indicating whether names without a delimiter are allowed.
- /// When enabled, the default version is applied.
- ///
- public bool AllowNoSuffix { get; set; }
-
- ///
- /// Gets or sets the default version used when no suffix is present.
- ///
- public string DefaultVersion { get; set; } = "0";
-}
diff --git a/src/Dapr.Workflow.Versioning.Runtime/VersionStrategies/ExplicitVersionStrategy.cs b/src/Dapr.Workflow.Versioning.Runtime/VersionStrategies/ExplicitVersionStrategy.cs
deleted file mode 100644
index d3a899db9..000000000
--- a/src/Dapr.Workflow.Versioning.Runtime/VersionStrategies/ExplicitVersionStrategy.cs
+++ /dev/null
@@ -1,63 +0,0 @@
-// ------------------------------------------------------------------------
-// Copyright 2026 The Dapr Authors
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-// http://www.apache.org/licenses/LICENSE-2.0
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ------------------------------------------------------------------------
-
-using Microsoft.Extensions.Options;
-
-namespace Dapr.Workflow.Versioning;
-
-///
-/// Strategy that requires versions to be supplied explicitly via .
-///
-public sealed class ExplicitVersionStrategy(IOptionsMonitor? optionsMonitor = null)
- : IWorkflowVersionStrategy, IWorkflowVersionStrategyContextConsumer
-{
- private ExplicitVersionStrategyOptions _options = new();
-
- ///
- public void Configure(WorkflowVersionStrategyContext context)
- {
- var optionsName = string.IsNullOrWhiteSpace(context.OptionsName)
- ? Options.DefaultName
- : context.OptionsName;
-
- if (optionsMonitor is not null)
- {
- _options = optionsMonitor.Get(optionsName);
- }
- }
-
- ///
- public bool TryParse(string typeName, out string canonicalName, out string version)
- {
- canonicalName = string.Empty;
- version = string.Empty;
-
- if (!_options.AllowMissingVersion || string.IsNullOrWhiteSpace(typeName))
- return false;
-
- canonicalName = typeName;
- version = string.IsNullOrWhiteSpace(_options.DefaultVersion) ? "0" : _options.DefaultVersion;
- return true;
- }
-
- ///
- public int Compare(string? v1, string? v2)
- {
- if (ReferenceEquals(v1, v2)) return 0;
- if (v1 is null) return -1;
- if (v2 is null) return 1;
-
- var comparer = _options.IgnoreCase ? StringComparer.OrdinalIgnoreCase : StringComparer.Ordinal;
- return comparer.Compare(v1, v2);
- }
-}
diff --git a/src/Dapr.Workflow.Versioning.Runtime/VersionStrategies/ExplicitVersionStrategyOptions.cs b/src/Dapr.Workflow.Versioning.Runtime/VersionStrategies/ExplicitVersionStrategyOptions.cs
deleted file mode 100644
index 57dd04f82..000000000
--- a/src/Dapr.Workflow.Versioning.Runtime/VersionStrategies/ExplicitVersionStrategyOptions.cs
+++ /dev/null
@@ -1,35 +0,0 @@
-// ------------------------------------------------------------------------
-// Copyright 2026 The Dapr Authors
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-// http://www.apache.org/licenses/LICENSE-2.0
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ------------------------------------------------------------------------
-
-namespace Dapr.Workflow.Versioning;
-
-///
-/// Options for .
-///
-public sealed class ExplicitVersionStrategyOptions
-{
- ///
- /// Gets or sets a value indicating whether parsing should be allowed when no explicit version is supplied.
- ///
- public bool AllowMissingVersion { get; set; }
-
- ///
- /// Gets or sets the default version used when no explicit version is supplied and parsing is allowed.
- ///
- public string DefaultVersion { get; set; } = "0";
-
- ///
- /// Gets or sets a value indicating whether version comparisons ignore case.
- ///
- public bool IgnoreCase { get; set; }
-}
diff --git a/src/Dapr.Workflow.Versioning.Runtime/VersionStrategies/NumericVersionStrategy.cs b/src/Dapr.Workflow.Versioning.Runtime/VersionStrategies/NumericVersionStrategy.cs
index d7e71e864..0af9ef3ed 100644
--- a/src/Dapr.Workflow.Versioning.Runtime/VersionStrategies/NumericVersionStrategy.cs
+++ b/src/Dapr.Workflow.Versioning.Runtime/VersionStrategies/NumericVersionStrategy.cs
@@ -18,7 +18,7 @@ namespace Dapr.Workflow.Versioning;
///
/// Strategy that derives a numeric version from a trailing suffix with an optional prefix
-/// (for example, MyWorkflowV1 with prefix V).
+/// (for example, MyWorkflowV1 with prefix V), with optional zero-padding.
///
public sealed class NumericVersionStrategy : IWorkflowVersionStrategy, IWorkflowVersionStrategyContextConsumer
{
@@ -72,6 +72,10 @@ public bool TryParse(string typeName, out string canonicalName, out string versi
return false;
}
+ var digitsLength = typeName.Length - digitsStart;
+ if (_options.ZeroPad && _options.Width > 0 && digitsLength != _options.Width)
+ return false;
+
if (!string.IsNullOrEmpty(prefix))
{
var prefixStart = digitsStart - prefix.Length;
diff --git a/src/Dapr.Workflow.Versioning.Runtime/VersionStrategies/NumericVersionStrategyOptions.cs b/src/Dapr.Workflow.Versioning.Runtime/VersionStrategies/NumericVersionStrategyOptions.cs
index ca5ed60c5..b589e0c05 100644
--- a/src/Dapr.Workflow.Versioning.Runtime/VersionStrategies/NumericVersionStrategyOptions.cs
+++ b/src/Dapr.Workflow.Versioning.Runtime/VersionStrategies/NumericVersionStrategyOptions.cs
@@ -35,6 +35,18 @@ public sealed class NumericVersionStrategyOptions
///
public bool AllowNoSuffix { get; set; } = true;
+ ///
+ /// Gets or sets a value indicating whether numeric suffixes must use zero-padding.
+ /// When enabled, the numeric suffix must match the configured .
+ ///
+ public bool ZeroPad { get; set; }
+
+ ///
+ /// Gets or sets the required width for the numeric suffix when zero-padding is enabled.
+ /// Set to 0 to allow any width.
+ ///
+ public int Width { get; set; } = 4;
+
///
/// Gets or sets the default version used when no suffix is present.
///
diff --git a/src/Dapr.Workflow.Versioning.Runtime/VersionStrategies/ZeroPaddedNumericVersionStrategy.cs b/src/Dapr.Workflow.Versioning.Runtime/VersionStrategies/ZeroPaddedNumericVersionStrategy.cs
deleted file mode 100644
index 363b84fd9..000000000
--- a/src/Dapr.Workflow.Versioning.Runtime/VersionStrategies/ZeroPaddedNumericVersionStrategy.cs
+++ /dev/null
@@ -1,133 +0,0 @@
-// ------------------------------------------------------------------------
-// Copyright 2026 The Dapr Authors
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-// http://www.apache.org/licenses/LICENSE-2.0
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ------------------------------------------------------------------------
-
-using System.Globalization;
-using Microsoft.Extensions.Options;
-
-namespace Dapr.Workflow.Versioning;
-
-///
-/// Strategy that derives a numeric version from a zero-padded trailing suffix (for example, MyWorkflow0001).
-///
-public sealed class ZeroPaddedNumericVersionStrategy(
- IOptionsMonitor? optionsMonitor = null)
- : IWorkflowVersionStrategy, IWorkflowVersionStrategyContextConsumer
-{
- private ZeroPaddedNumericVersionStrategyOptions _options = new();
-
- ///
- public void Configure(WorkflowVersionStrategyContext context)
- {
- var optionsName = string.IsNullOrWhiteSpace(context.OptionsName)
- ? Options.DefaultName
- : context.OptionsName;
-
- if (optionsMonitor is not null)
- {
- _options = optionsMonitor.Get(optionsName);
- }
- }
-
- ///
- public bool TryParse(string typeName, out string canonicalName, out string version)
- {
- canonicalName = string.Empty;
- version = string.Empty;
-
- if (string.IsNullOrWhiteSpace(typeName))
- return false;
-
- var prefix = _options.SuffixPrefix ?? string.Empty;
- var comparison = _options.IgnorePrefixCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal;
-
- var digitsStart = FindTrailingDigits(typeName);
- if (digitsStart < 0)
- {
- if (_options.AllowNoSuffix && !EndsWithPrefix(typeName, prefix, comparison))
- {
- canonicalName = typeName;
- version = string.IsNullOrWhiteSpace(_options.DefaultVersion) ? "0" : _options.DefaultVersion;
- return true;
- }
-
- return false;
- }
-
- var digitsLength = typeName.Length - digitsStart;
- if (_options.Width > 0 && digitsLength != _options.Width)
- return false;
-
- if (!string.IsNullOrEmpty(prefix))
- {
- var prefixStart = digitsStart - prefix.Length;
- if (prefixStart < 1)
- return false;
-
- var candidatePrefix = typeName.Substring(prefixStart, prefix.Length);
- if (!string.Equals(candidatePrefix, prefix, comparison))
- return false;
-
- canonicalName = typeName.Substring(0, prefixStart);
- }
- else
- {
- if (digitsStart < 1)
- return false;
-
- canonicalName = typeName.Substring(0, digitsStart);
- }
-
- version = typeName.Substring(digitsStart);
- return !string.IsNullOrEmpty(canonicalName) && !string.IsNullOrEmpty(version);
- }
-
- ///
- public int Compare(string? v1, string? v2)
- {
- if (ReferenceEquals(v1, v2)) return 0;
- if (v1 is null) return -1;
- if (v2 is null) return 1;
-
- var s1 = v1.Trim();
- var s2 = v2.Trim();
-
- var ok1 = long.TryParse(s1, NumberStyles.None, CultureInfo.InvariantCulture, out var n1);
- var ok2 = long.TryParse(s2, NumberStyles.None, CultureInfo.InvariantCulture, out var n2);
-
- switch (ok1)
- {
- case true when ok2:
- return n1.CompareTo(n2);
- case true:
- return 1;
- }
-
- if (ok2) return -1;
-
- return StringComparer.Ordinal.Compare(s1, s2);
- }
-
- private static int FindTrailingDigits(string value)
- {
- var i = value.Length - 1;
- while (i >= 0 && value[i] >= '0' && value[i] <= '9')
- {
- i--;
- }
-
- return i == value.Length - 1 ? -1 : i + 1;
- }
-
- private static bool EndsWithPrefix(string value, string prefix, StringComparison comparison) =>
- !string.IsNullOrEmpty(prefix) && value.EndsWith(prefix, comparison);
-}
diff --git a/src/Dapr.Workflow.Versioning.Runtime/VersionStrategies/ZeroPaddedNumericVersionStrategyOptions.cs b/src/Dapr.Workflow.Versioning.Runtime/VersionStrategies/ZeroPaddedNumericVersionStrategyOptions.cs
deleted file mode 100644
index c64ca9bdb..000000000
--- a/src/Dapr.Workflow.Versioning.Runtime/VersionStrategies/ZeroPaddedNumericVersionStrategyOptions.cs
+++ /dev/null
@@ -1,47 +0,0 @@
-// ------------------------------------------------------------------------
-// Copyright 2026 The Dapr Authors
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-// http://www.apache.org/licenses/LICENSE-2.0
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ------------------------------------------------------------------------
-
-namespace Dapr.Workflow.Versioning;
-
-///
-/// Options for .
-///
-public sealed class ZeroPaddedNumericVersionStrategyOptions
-{
- ///
- /// Gets or sets the prefix used before the numeric suffix (for example, "V" in MyWorkflowV0001).
- /// Set to an empty string to allow a raw numeric suffix.
- ///
- public string SuffixPrefix { get; set; } = string.Empty;
-
- ///
- /// Gets or sets a value indicating whether prefix matching ignores case.
- ///
- public bool IgnorePrefixCase { get; set; }
-
- ///
- /// Gets or sets the required width for the numeric suffix. Set to 0 to allow any width.
- ///
- public int Width { get; set; } = 4;
-
- ///
- /// Gets or sets a value indicating whether names without a numeric suffix are allowed.
- /// When enabled, the default version is applied.
- ///
- public bool AllowNoSuffix { get; set; }
-
- ///
- /// Gets or sets the default version used when no suffix is present.
- ///
- public string DefaultVersion { get; set; } = "0";
-}
diff --git a/test/Dapr.Workflow.Versioning.Runtime.Test/DateSuffixVersionStrategyOptionsTests.cs b/test/Dapr.Workflow.Versioning.Runtime.Test/DateVersionStrategyOptionsTests.cs
similarity index 83%
rename from test/Dapr.Workflow.Versioning.Runtime.Test/DateSuffixVersionStrategyOptionsTests.cs
rename to test/Dapr.Workflow.Versioning.Runtime.Test/DateVersionStrategyOptionsTests.cs
index e86bdb9de..1032683a2 100644
--- a/test/Dapr.Workflow.Versioning.Runtime.Test/DateSuffixVersionStrategyOptionsTests.cs
+++ b/test/Dapr.Workflow.Versioning.Runtime.Test/DateVersionStrategyOptionsTests.cs
@@ -13,14 +13,16 @@
namespace Dapr.Workflow.Versioning.Runtime.Test;
-public class DateSuffixVersionStrategyOptionsTests
+public class DateVersionStrategyOptionsTests
{
[Fact]
public void Defaults_ShouldMatchExpectedValues()
{
- var options = new DateSuffixVersionStrategyOptions();
+ var options = new DateVersionStrategyOptions();
Assert.Equal("yyyyMMdd", options.DateFormat);
+ Assert.Equal(string.Empty, options.Prefix);
+ Assert.False(options.IgnorePrefixCase);
Assert.False(options.AllowNoSuffix);
Assert.Equal("0", options.DefaultVersion);
}
diff --git a/test/Dapr.Workflow.Versioning.Runtime.Test/DateSuffixVersionStrategyTests.cs b/test/Dapr.Workflow.Versioning.Runtime.Test/DateVersionStrategyTests.cs
similarity index 58%
rename from test/Dapr.Workflow.Versioning.Runtime.Test/DateSuffixVersionStrategyTests.cs
rename to test/Dapr.Workflow.Versioning.Runtime.Test/DateVersionStrategyTests.cs
index 17f67a76f..d8f352b74 100644
--- a/test/Dapr.Workflow.Versioning.Runtime.Test/DateSuffixVersionStrategyTests.cs
+++ b/test/Dapr.Workflow.Versioning.Runtime.Test/DateVersionStrategyTests.cs
@@ -16,12 +16,12 @@
namespace Dapr.Workflow.Versioning.Runtime.Test;
-public class DateSuffixVersionStrategyTests
+public class DateVersionStrategyTests
{
[Fact]
public void TryParse_ShouldParseDefaultFormat()
{
- var strategy = new DateSuffixVersionStrategy();
+ var strategy = new DateVersionStrategy();
var parsed = strategy.TryParse("MyWorkflow20260212", out var canonical, out var version);
@@ -33,7 +33,7 @@ public void TryParse_ShouldParseDefaultFormat()
[Fact]
public void TryParse_ShouldRejectNoSuffix_ByDefault()
{
- var strategy = new DateSuffixVersionStrategy();
+ var strategy = new DateVersionStrategy();
Assert.False(strategy.TryParse("MyWorkflow", out _, out _));
}
@@ -42,13 +42,13 @@ public void TryParse_ShouldRejectNoSuffix_ByDefault()
public void TryParse_ShouldAllowNoSuffix_WhenEnabled()
{
var services = new ServiceCollection();
- services.AddOptions(Options.DefaultName)
+ services.AddOptions(Options.DefaultName)
.Configure(o => o.AllowNoSuffix = true);
using var provider = services.BuildServiceProvider();
var factory = new DefaultWorkflowVersionStrategyFactory();
- var strategy = (DateSuffixVersionStrategy)factory.Create(
- typeof(DateSuffixVersionStrategy),
+ var strategy = (DateVersionStrategy)factory.Create(
+ typeof(DateVersionStrategy),
canonicalName: "Orders",
optionsName: null,
services: provider);
@@ -62,13 +62,13 @@ public void TryParse_ShouldAllowNoSuffix_WhenEnabled()
public void TryParse_ShouldUseNamedFormatFromFactory()
{
var services = new ServiceCollection();
- services.AddOptions("custom")
+ services.AddOptions("custom")
.Configure(o => o.DateFormat = "yyyy-MM-dd");
using var provider = services.BuildServiceProvider();
var factory = new DefaultWorkflowVersionStrategyFactory();
- var strategy = (DateSuffixVersionStrategy)factory.Create(
- typeof(DateSuffixVersionStrategy),
+ var strategy = (DateVersionStrategy)factory.Create(
+ typeof(DateVersionStrategy),
canonicalName: "Orders",
optionsName: "custom",
services: provider);
@@ -78,18 +78,68 @@ public void TryParse_ShouldUseNamedFormatFromFactory()
Assert.Equal("2026-02-12", version);
}
+ [Fact]
+ public void TryParse_ShouldIgnorePrefixCase_WhenConfigured()
+ {
+ var services = new ServiceCollection();
+ services.AddOptions("custom")
+ .Configure(o =>
+ {
+ o.DateFormat = "yyyyMMdd";
+ o.Prefix = "v";
+ o.IgnorePrefixCase = true;
+ });
+
+ using var provider = services.BuildServiceProvider();
+ var factory = new DefaultWorkflowVersionStrategyFactory();
+ var strategy = (DateVersionStrategy)factory.Create(
+ typeof(DateVersionStrategy),
+ canonicalName: "Orders",
+ optionsName: "custom",
+ services: provider);
+
+ Assert.True(strategy.TryParse("OrdersV20260212", out var canonical, out var version));
+ Assert.Equal("Orders", canonical);
+ Assert.Equal("20260212", version);
+ }
+
+ [Fact]
+ public void TryParse_ShouldRequirePrefix_WhenConfigured()
+ {
+ var services = new ServiceCollection();
+ services.AddOptions("custom")
+ .Configure(o =>
+ {
+ o.DateFormat = "yyyyMMdd";
+ o.Prefix = "v";
+ });
+
+ using var provider = services.BuildServiceProvider();
+ var factory = new DefaultWorkflowVersionStrategyFactory();
+ var strategy = (DateVersionStrategy)factory.Create(
+ typeof(DateVersionStrategy),
+ canonicalName: "Orders",
+ optionsName: "custom",
+ services: provider);
+
+ Assert.False(strategy.TryParse("Orders20260212", out _, out _));
+ Assert.True(strategy.TryParse("Ordersv20260212", out var canonical, out var version));
+ Assert.Equal("Orders", canonical);
+ Assert.Equal("20260212", version);
+ }
+
[Fact]
public void TryParse_ShouldReadFromDate()
{
var services = new ServiceCollection();
const string optionsName = "workflow-defaults";
- services.AddOptions(optionsName)
+ services.AddOptions(optionsName)
.Configure(o => o.DateFormat = "yyyyMMddHHmmss");
using var provider = services.BuildServiceProvider();
var factory = new DefaultWorkflowVersionStrategyFactory();
- var strategy = (DateSuffixVersionStrategy)factory.Create(
- typeof(DateSuffixVersionStrategy),
+ var strategy = (DateVersionStrategy)factory.Create(
+ typeof(DateVersionStrategy),
canonicalName: "",
optionsName: optionsName,
services: provider);
@@ -102,7 +152,7 @@ public void TryParse_ShouldReadFromDate()
[Fact]
public void Compare_ShouldOrderByDate()
{
- var strategy = new DateSuffixVersionStrategy();
+ var strategy = new DateVersionStrategy();
Assert.True(strategy.Compare("20260101", "20261231") < 0);
Assert.True(strategy.Compare("20261231", "20260101") > 0);
@@ -111,7 +161,7 @@ public void Compare_ShouldOrderByDate()
[Fact]
public void Compare_ShouldPreferValidDateOverInvalid()
{
- var strategy = new DateSuffixVersionStrategy();
+ var strategy = new DateVersionStrategy();
Assert.True(strategy.Compare("20261231", "not-a-date") > 0);
Assert.True(strategy.Compare("bad", "20260101") < 0);
diff --git a/test/Dapr.Workflow.Versioning.Runtime.Test/DelimitedSuffixVersionStrategyOptionsTests.cs b/test/Dapr.Workflow.Versioning.Runtime.Test/DelimitedSuffixVersionStrategyOptionsTests.cs
deleted file mode 100644
index b922779b7..000000000
--- a/test/Dapr.Workflow.Versioning.Runtime.Test/DelimitedSuffixVersionStrategyOptionsTests.cs
+++ /dev/null
@@ -1,28 +0,0 @@
-// ------------------------------------------------------------------------
-// Copyright 2026 The Dapr Authors
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-// http://www.apache.org/licenses/LICENSE-2.0
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ------------------------------------------------------------------------
-
-namespace Dapr.Workflow.Versioning.Runtime.Test;
-
-public class DelimitedSuffixVersionStrategyOptionsTests
-{
- [Fact]
- public void Defaults_ShouldMatchExpectedValues()
- {
- var options = new DelimitedSuffixVersionStrategyOptions();
-
- Assert.Equal("-", options.Delimiter);
- Assert.False(options.IgnoreDelimiterCase);
- Assert.False(options.AllowNoSuffix);
- Assert.Equal("0", options.DefaultVersion);
- }
-}
diff --git a/test/Dapr.Workflow.Versioning.Runtime.Test/DelimitedSuffixVersionStrategyTests.cs b/test/Dapr.Workflow.Versioning.Runtime.Test/DelimitedSuffixVersionStrategyTests.cs
deleted file mode 100644
index c008a2b5f..000000000
--- a/test/Dapr.Workflow.Versioning.Runtime.Test/DelimitedSuffixVersionStrategyTests.cs
+++ /dev/null
@@ -1,60 +0,0 @@
-// ------------------------------------------------------------------------
-// Copyright 2026 The Dapr Authors
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-// http://www.apache.org/licenses/LICENSE-2.0
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ------------------------------------------------------------------------
-
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Options;
-
-namespace Dapr.Workflow.Versioning.Runtime.Test;
-
-public class DelimitedSuffixVersionStrategyTests
-{
- [Fact]
- public void TryParse_ShouldParseDelimitedSuffix()
- {
- var strategy = new DelimitedSuffixVersionStrategy();
-
- var parsed = strategy.TryParse("Orders-2", out var canonical, out var version);
-
- Assert.True(parsed);
- Assert.Equal("Orders", canonical);
- Assert.Equal("2", version);
- }
-
- [Fact]
- public void TryParse_ShouldRejectNoSuffix_ByDefault()
- {
- var strategy = new DelimitedSuffixVersionStrategy();
-
- Assert.False(strategy.TryParse("Orders", out _, out _));
- }
-
- [Fact]
- public void TryParse_ShouldAllowNoSuffix_WhenConfigured()
- {
- var services = new ServiceCollection();
- services.AddOptions(Options.DefaultName)
- .Configure(o => o.AllowNoSuffix = true);
-
- using var provider = services.BuildServiceProvider();
- var factory = new DefaultWorkflowVersionStrategyFactory();
- var strategy = (DelimitedSuffixVersionStrategy)factory.Create(
- typeof(DelimitedSuffixVersionStrategy),
- canonicalName: "Orders",
- optionsName: null,
- services: provider);
-
- Assert.True(strategy.TryParse("Orders", out var canonical, out var version));
- Assert.Equal("Orders", canonical);
- Assert.Equal("0", version);
- }
-}
diff --git a/test/Dapr.Workflow.Versioning.Runtime.Test/ExplicitVersionStrategyOptionsTests.cs b/test/Dapr.Workflow.Versioning.Runtime.Test/ExplicitVersionStrategyOptionsTests.cs
deleted file mode 100644
index e6677762e..000000000
--- a/test/Dapr.Workflow.Versioning.Runtime.Test/ExplicitVersionStrategyOptionsTests.cs
+++ /dev/null
@@ -1,27 +0,0 @@
-// ------------------------------------------------------------------------
-// Copyright 2026 The Dapr Authors
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-// http://www.apache.org/licenses/LICENSE-2.0
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ------------------------------------------------------------------------
-
-namespace Dapr.Workflow.Versioning.Runtime.Test;
-
-public class ExplicitVersionStrategyOptionsTests
-{
- [Fact]
- public void Defaults_ShouldMatchExpectedValues()
- {
- var options = new ExplicitVersionStrategyOptions();
-
- Assert.False(options.AllowMissingVersion);
- Assert.Equal("0", options.DefaultVersion);
- Assert.False(options.IgnoreCase);
- }
-}
diff --git a/test/Dapr.Workflow.Versioning.Runtime.Test/ExplicitVersionStrategyTests.cs b/test/Dapr.Workflow.Versioning.Runtime.Test/ExplicitVersionStrategyTests.cs
deleted file mode 100644
index 6a9662e97..000000000
--- a/test/Dapr.Workflow.Versioning.Runtime.Test/ExplicitVersionStrategyTests.cs
+++ /dev/null
@@ -1,48 +0,0 @@
-// ------------------------------------------------------------------------
-// Copyright 2026 The Dapr Authors
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-// http://www.apache.org/licenses/LICENSE-2.0
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ------------------------------------------------------------------------
-
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Options;
-
-namespace Dapr.Workflow.Versioning.Runtime.Test;
-
-public class ExplicitVersionStrategyTests
-{
- [Fact]
- public void TryParse_ShouldRejectMissingVersion_ByDefault()
- {
- var strategy = new ExplicitVersionStrategy();
-
- Assert.False(strategy.TryParse("Orders", out _, out _));
- }
-
- [Fact]
- public void TryParse_ShouldAllowMissingVersion_WhenConfigured()
- {
- var services = new ServiceCollection();
- services.AddOptions(Options.DefaultName)
- .Configure(o => o.AllowMissingVersion = true);
-
- using var provider = services.BuildServiceProvider();
- var factory = new DefaultWorkflowVersionStrategyFactory();
- var strategy = (ExplicitVersionStrategy)factory.Create(
- typeof(ExplicitVersionStrategy),
- canonicalName: "Orders",
- optionsName: null,
- services: provider);
-
- Assert.True(strategy.TryParse("Orders", out var canonical, out var version));
- Assert.Equal("Orders", canonical);
- Assert.Equal("0", version);
- }
-}
diff --git a/test/Dapr.Workflow.Versioning.Runtime.Test/NumericVersionStrategyOptionsTests.cs b/test/Dapr.Workflow.Versioning.Runtime.Test/NumericVersionStrategyOptionsTests.cs
index cb7cedd73..2a051d396 100644
--- a/test/Dapr.Workflow.Versioning.Runtime.Test/NumericVersionStrategyOptionsTests.cs
+++ b/test/Dapr.Workflow.Versioning.Runtime.Test/NumericVersionStrategyOptionsTests.cs
@@ -23,6 +23,8 @@ public void Defaults_ShouldMatchExpectedValues()
Assert.Equal("V", options.SuffixPrefix);
Assert.False(options.IgnorePrefixCase);
Assert.True(options.AllowNoSuffix);
+ Assert.False(options.ZeroPad);
+ Assert.Equal(4, options.Width);
Assert.Equal("0", options.DefaultVersion);
}
}
diff --git a/test/Dapr.Workflow.Versioning.Runtime.Test/NumericVersionStrategyTests.cs b/test/Dapr.Workflow.Versioning.Runtime.Test/NumericVersionStrategyTests.cs
index 67725c2e5..e486a77d7 100644
--- a/test/Dapr.Workflow.Versioning.Runtime.Test/NumericVersionStrategyTests.cs
+++ b/test/Dapr.Workflow.Versioning.Runtime.Test/NumericVersionStrategyTests.cs
@@ -12,6 +12,7 @@
// ------------------------------------------------------------------------
using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Options;
namespace Dapr.Workflow.Versioning.Runtime.Test;
@@ -51,6 +52,56 @@ public void TryParse_ShouldRejectMissingPrefix_WhenDigitsPresent()
Assert.False(parsed);
}
+ [Fact]
+ public void TryParse_ShouldParseWithWidth_WhenZeroPadEnabled()
+ {
+ var services = new ServiceCollection();
+ services.AddOptions(Options.DefaultName)
+ .Configure(o =>
+ {
+ o.SuffixPrefix = string.Empty;
+ o.ZeroPad = true;
+ o.Width = 4;
+ o.AllowNoSuffix = false;
+ });
+
+ using var provider = services.BuildServiceProvider();
+ var factory = new DefaultWorkflowVersionStrategyFactory();
+ var strategy = (NumericVersionStrategy)factory.Create(
+ typeof(NumericVersionStrategy),
+ canonicalName: "Orders",
+ optionsName: null,
+ services: provider);
+
+ Assert.True(strategy.TryParse("Orders0007", out var canonical, out var version));
+ Assert.Equal("Orders", canonical);
+ Assert.Equal("0007", version);
+ }
+
+ [Fact]
+ public void TryParse_ShouldRejectWrongWidth_WhenZeroPadEnabled()
+ {
+ var services = new ServiceCollection();
+ services.AddOptions(Options.DefaultName)
+ .Configure(o =>
+ {
+ o.SuffixPrefix = string.Empty;
+ o.ZeroPad = true;
+ o.Width = 4;
+ o.AllowNoSuffix = false;
+ });
+
+ using var provider = services.BuildServiceProvider();
+ var factory = new DefaultWorkflowVersionStrategyFactory();
+ var strategy = (NumericVersionStrategy)factory.Create(
+ typeof(NumericVersionStrategy),
+ canonicalName: "Orders",
+ optionsName: null,
+ services: provider);
+
+ Assert.False(strategy.TryParse("Orders007", out _, out _));
+ }
+
[Theory]
[InlineData("1", "2")]
[InlineData("9", "10")]
diff --git a/test/Dapr.Workflow.Versioning.Runtime.Test/WorkflowVersioningServiceCollectionExtensionsTests.cs b/test/Dapr.Workflow.Versioning.Runtime.Test/WorkflowVersioningServiceCollectionExtensionsTests.cs
index 4c3ffc0a2..51a3949dc 100644
--- a/test/Dapr.Workflow.Versioning.Runtime.Test/WorkflowVersioningServiceCollectionExtensionsTests.cs
+++ b/test/Dapr.Workflow.Versioning.Runtime.Test/WorkflowVersioningServiceCollectionExtensionsTests.cs
@@ -24,8 +24,8 @@ public void UseDefaultWorkflowStrategy_UsesNamedOptions()
services.AddDaprWorkflowVersioning();
const string optionsName = "workflow-defaults";
- services.UseDefaultWorkflowStrategy(optionsName);
- services.ConfigureStrategyOptions(optionsName, o =>
+ services.UseDefaultWorkflowStrategy(optionsName);
+ services.ConfigureStrategyOptions(optionsName, o =>
{
o.DateFormat = "yyyyMMddHHmmss";
});
diff --git a/test/Dapr.Workflow.Versioning.Runtime.Test/ZeroPaddedNumericVersionStrategyOptionsTests.cs b/test/Dapr.Workflow.Versioning.Runtime.Test/ZeroPaddedNumericVersionStrategyOptionsTests.cs
deleted file mode 100644
index 233514c07..000000000
--- a/test/Dapr.Workflow.Versioning.Runtime.Test/ZeroPaddedNumericVersionStrategyOptionsTests.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-// ------------------------------------------------------------------------
-// Copyright 2026 The Dapr Authors
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-// http://www.apache.org/licenses/LICENSE-2.0
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ------------------------------------------------------------------------
-
-namespace Dapr.Workflow.Versioning.Runtime.Test;
-
-public class ZeroPaddedNumericVersionStrategyOptionsTests
-{
- [Fact]
- public void Defaults_ShouldMatchExpectedValues()
- {
- var options = new ZeroPaddedNumericVersionStrategyOptions();
-
- Assert.Equal(string.Empty, options.SuffixPrefix);
- Assert.False(options.IgnorePrefixCase);
- Assert.Equal(4, options.Width);
- Assert.False(options.AllowNoSuffix);
- Assert.Equal("0", options.DefaultVersion);
- }
-}
diff --git a/test/Dapr.Workflow.Versioning.Runtime.Test/ZeroPaddedNumericVersionStrategyTests.cs b/test/Dapr.Workflow.Versioning.Runtime.Test/ZeroPaddedNumericVersionStrategyTests.cs
deleted file mode 100644
index 9f3af941e..000000000
--- a/test/Dapr.Workflow.Versioning.Runtime.Test/ZeroPaddedNumericVersionStrategyTests.cs
+++ /dev/null
@@ -1,58 +0,0 @@
-// ------------------------------------------------------------------------
-// Copyright 2026 The Dapr Authors
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-// http://www.apache.org/licenses/LICENSE-2.0
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ------------------------------------------------------------------------
-
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Options;
-
-namespace Dapr.Workflow.Versioning.Runtime.Test;
-
-public class ZeroPaddedNumericVersionStrategyTests
-{
- [Fact]
- public void TryParse_ShouldParseWithWidth()
- {
- var strategy = new ZeroPaddedNumericVersionStrategy();
-
- Assert.True(strategy.TryParse("Orders0007", out var canonical, out var version));
- Assert.Equal("Orders", canonical);
- Assert.Equal("0007", version);
- }
-
- [Fact]
- public void TryParse_ShouldRejectWrongWidth()
- {
- var strategy = new ZeroPaddedNumericVersionStrategy();
-
- Assert.False(strategy.TryParse("Orders007", out _, out _));
- }
-
- [Fact]
- public void TryParse_ShouldAllowNoSuffix_WhenConfigured()
- {
- var services = new ServiceCollection();
- services.AddOptions(Options.DefaultName)
- .Configure(o => o.AllowNoSuffix = true);
-
- using var provider = services.BuildServiceProvider();
- var factory = new DefaultWorkflowVersionStrategyFactory();
- var strategy = (ZeroPaddedNumericVersionStrategy)factory.Create(
- typeof(ZeroPaddedNumericVersionStrategy),
- canonicalName: "Orders",
- optionsName: null,
- services: provider);
-
- Assert.True(strategy.TryParse("Orders", out var canonical, out var version));
- Assert.Equal("Orders", canonical);
- Assert.Equal("0", version);
- }
-}