merge: external-system retry policy end-to-end (register row 27) — MaxRetries/RetryDelay now travel artifact→gRPC→site schema→S&F enqueue, CLI --max-retries/--retry-delay-seconds, ArtifactDiff compares them

This commit is contained in:
Joseph Doherty
2026-08-15 17:56:21 -04:00
28 changed files with 1036 additions and 453 deletions
@@ -997,6 +997,35 @@ public class DeploymentManagerActorTests : TestKit, IDisposable
SmtpConfigurations: null,
Timestamp: DateTimeOffset.UtcNow);
/// <summary>
/// Deferred-work row 27: the per-system Store-and-Forward retry policy must survive the
/// site APPLY path, not just the storage round-trip. Before the fix
/// <c>ExternalSystemArtifact</c> carried no retry fields at all, so a centrally-configured
/// policy was dropped on the floor here and every cached call fell back to the engine's
/// 50 x 30 s default.
/// </summary>
[Fact]
public async Task ApplyingArtifacts_PersistsExternalSystemRetryPolicy()
{
var manager = CreateDeploymentManager();
manager.Tell(ArtifactsCommand("dep-es-retry", externalSystems:
[
new ExternalSystemArtifact("MES", "https://mes", "ApiKey", null, null, 0,
MaxRetries: 9, RetryDelay: TimeSpan.FromSeconds(45))
]));
Assert.True(ExpectMsg<ArtifactDeploymentResponse>(TimeSpan.FromSeconds(10)).Success);
await using var connection = _storage.CreateConnection();
await using var command = connection.CreateCommand();
command.CommandText = "SELECT max_retries, retry_delay_ms FROM external_systems WHERE name = 'MES'";
await using var reader = await command.ExecuteReaderAsync();
Assert.True(await reader.ReadAsync());
Assert.Equal(9, reader.GetInt32(0));
Assert.Equal(45_000, reader.GetInt64(1));
}
[Fact]
public async Task ApplyingArtifacts_DeletesExternalSystemsAbsentFromTheSet()
{