fix(deployment): audit-write fault can no longer flip a committed Success deployment to Failed
Route all post-terminal-status lifecycle audit writes (Deploy/Disable/Enable/Delete/DeleteOrphaned) through a swallow-and-warn TryLogAuditAsync helper so a failed best-effort audit write can neither reach the outer catch (flipping Success to Failed) nor propagate to the caller. Claude-Session: https://claude.ai/code/session_01MtdgwpEeCUn6cUA5f1LMPj
This commit is contained in:
@@ -878,6 +878,36 @@ public class DeploymentServiceTests : TestKit
|
||||
Arg.Any<object>(), Arg.Any<CancellationToken>());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DeployInstanceAsync_DeployAuditWriteThrows_KeepsCommittedSuccess()
|
||||
{
|
||||
// #05-T17: the post-success "Deploy" audit write is best-effort. If it
|
||||
// throws, the already-committed Success record must NOT be flipped to
|
||||
// Failed by the outer catch — "audit-write failure NEVER aborts the
|
||||
// user-facing action".
|
||||
var instance = new Instance("AuditFaultInst") { Id = 52, SiteId = 1, State = InstanceState.NotDeployed };
|
||||
_repo.GetInstanceByIdAsync(52, Arg.Any<CancellationToken>()).Returns(instance);
|
||||
SetupValidPipeline(52, "AuditFaultInst", "sha256:target");
|
||||
_repo.GetCurrentDeploymentStatusAsync(52, Arg.Any<CancellationToken>())
|
||||
.Returns((DeploymentRecord?)null);
|
||||
|
||||
// The Deploy audit LogAsync faults.
|
||||
_audit.When(a => a.LogAsync(
|
||||
Arg.Any<string>(), "Deploy", Arg.Any<string>(), Arg.Any<string>(),
|
||||
Arg.Any<string>(), Arg.Any<object>(), Arg.Any<CancellationToken>()))
|
||||
.Do(_ => throw new InvalidOperationException("audit pipeline down"));
|
||||
|
||||
var counters = new ReconcileProbeCounters();
|
||||
var commActor = Sys.ActorOf(Props.Create(() =>
|
||||
new ReconcileProbeActor(counters, siteHash: "sha256:target", failQuery: false)));
|
||||
var service = CreateServiceWithCommActor(commActor);
|
||||
|
||||
var result = await service.DeployInstanceAsync(52, "admin");
|
||||
|
||||
Assert.True(result.IsSuccess);
|
||||
Assert.Equal(DeploymentStatus.Success, result.Value.Status);
|
||||
}
|
||||
|
||||
// ── Telemetry follow-on: scadabridge.deployments.applied on deploy success ──
|
||||
|
||||
[Fact]
|
||||
|
||||
Reference in New Issue
Block a user