feat(deployment-manager): resolve DeploymentManager-006 — query site deployment state before redeploy and reconcile

Adds DeploymentStateQuery request/response contracts (Commons), a site-side
handler (SiteRuntime), a CommunicationService query method (Communication), and
reconciliation in DeploymentService: when a prior record is InProgress or
Failed-on-timeout, query the site; if it already holds the target revision hash
mark the record Success without re-sending; on query failure fall through to a
normal deploy (site-side stale-rejection is the safety net).
This commit is contained in:
Joseph Doherty
2026-05-16 20:12:24 -04:00
parent cac8aebe9f
commit bc548e1447
13 changed files with 662 additions and 19 deletions

View File

@@ -326,4 +326,48 @@ public class CompatibilityTests
Assert.NotNull(msg);
Assert.Equal((DeploymentStatus)99, msg!.Status);
}
// ── DeploymentManager-006: query-the-site-before-redeploy contracts ──
[Fact]
public void RoundTrip_DeploymentStateQueryRequest_Succeeds()
{
var msg = new DeploymentStateQueryRequest("corr-1", "inst-1", DateTimeOffset.UtcNow);
var json = JsonSerializer.Serialize(msg);
var deserialized = JsonSerializer.Deserialize<DeploymentStateQueryRequest>(json, Options);
Assert.NotNull(deserialized);
Assert.Equal("corr-1", deserialized!.CorrelationId);
Assert.Equal("inst-1", deserialized.InstanceUniqueName);
}
[Fact]
public void RoundTrip_DeploymentStateQueryResponse_Deployed_Succeeds()
{
var msg = new DeploymentStateQueryResponse(
"corr-1", "inst-1", true, "dep-9", "sha256:abc", DateTimeOffset.UtcNow);
var json = JsonSerializer.Serialize(msg);
var deserialized = JsonSerializer.Deserialize<DeploymentStateQueryResponse>(json, Options);
Assert.NotNull(deserialized);
Assert.True(deserialized!.IsDeployed);
Assert.Equal("dep-9", deserialized.AppliedDeploymentId);
Assert.Equal("sha256:abc", deserialized.AppliedRevisionHash);
}
[Fact]
public void RoundTrip_DeploymentStateQueryResponse_NotDeployed_NullApplied()
{
// When the instance is not deployed at the site, the applied identity
// fields are null — verified to survive a JSON round-trip.
var msg = new DeploymentStateQueryResponse(
"corr-1", "inst-1", false, null, null, DateTimeOffset.UtcNow);
var json = JsonSerializer.Serialize(msg);
var deserialized = JsonSerializer.Deserialize<DeploymentStateQueryResponse>(json, Options);
Assert.NotNull(deserialized);
Assert.False(deserialized!.IsDeployed);
Assert.Null(deserialized.AppliedDeploymentId);
Assert.Null(deserialized.AppliedRevisionHash);
}
}