fix(management): ParseDeploymentStatus rejects ordinals — Enum.TryParse accepts "2" as InProgress despite the row-88 names-only contract; same LooksNumeric guard as the audit strict parser (found by the Wave 6 rows-98/99 agent survey)
This commit is contained in:
@@ -2745,7 +2745,13 @@ public class ManagementActor : ReceiveActor
|
|||||||
private static DeploymentStatus? ParseDeploymentStatus(string? status)
|
private static DeploymentStatus? ParseDeploymentStatus(string? status)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(status)) return null;
|
if (string.IsNullOrWhiteSpace(status)) return null;
|
||||||
if (Enum.TryParse<DeploymentStatus>(status, ignoreCase: true, out var parsed)
|
var trimmed = status.Trim();
|
||||||
|
// Reject ordinals before TryParse — "2" parses to InProgress, which the
|
||||||
|
// documented names-only contract does not admit. No enum member name can
|
||||||
|
// start with a digit or a sign (same guard as AuditQueryParamParsers).
|
||||||
|
var looksNumeric = char.IsAsciiDigit(trimmed[0]) || trimmed[0] == '+' || trimmed[0] == '-';
|
||||||
|
if (!looksNumeric
|
||||||
|
&& Enum.TryParse<DeploymentStatus>(trimmed, ignoreCase: true, out var parsed)
|
||||||
&& Enum.IsDefined(parsed))
|
&& Enum.IsDefined(parsed))
|
||||||
{
|
{
|
||||||
return parsed;
|
return parsed;
|
||||||
|
|||||||
@@ -1678,6 +1678,27 @@ public class ManagementActorTests : TestKit, IDisposable
|
|||||||
deployRepo.DidNotReceiveWithAnyArgs().QueryDeploymentListPageAsync(default!, default, default);
|
deployRepo.DidNotReceiveWithAnyArgs().QueryDeploymentListPageAsync(default!, default, default);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("2")]
|
||||||
|
[InlineData("+1")]
|
||||||
|
[InlineData("-1")]
|
||||||
|
public void QueryDeployments_OrdinalStatus_IsRejectedLikeAnyUnknownValue(string status)
|
||||||
|
{
|
||||||
|
// Register row 88 postscript (Wave 6): Enum.TryParse accepts ordinals, so
|
||||||
|
// `--status 2` silently resolved to InProgress despite the documented
|
||||||
|
// names-only contract. Same guard as AuditQueryParamParsers.LooksNumeric.
|
||||||
|
var deployRepo = Substitute.For<IDeploymentManagerRepository>();
|
||||||
|
StubDeploymentPageQuery(deployRepo, (1, 1));
|
||||||
|
_services.AddScoped(_ => deployRepo);
|
||||||
|
|
||||||
|
var actor = CreateActor();
|
||||||
|
actor.Tell(Envelope(new QueryDeploymentsCommand(Status: status), "Deployer"));
|
||||||
|
|
||||||
|
var response = ExpectMsg<ManagementError>(TimeSpan.FromSeconds(5));
|
||||||
|
Assert.Contains("Success", response.Error);
|
||||||
|
deployRepo.DidNotReceiveWithAnyArgs().QueryDeploymentListPageAsync(default!, default, default);
|
||||||
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[InlineData("success")]
|
[InlineData("success")]
|
||||||
[InlineData("SUCCESS")]
|
[InlineData("SUCCESS")]
|
||||||
|
|||||||
Reference in New Issue
Block a user