fix(transport): carry ExternalSystem TimeoutSeconds through bundles + diff (arch-review 2026-08-19 P7-1, row-27 shape)
TimeoutSeconds is the per-system HTTP timeout ExternalSystemClient reads at call time and is deployed to sites (proto field 6), but Transport bundles silently dropped it: the DTO never carried it, export never wrote it, the importer's Add path reset it to 0, the Overwrite path left the target's stale value untouched, and ArtifactDiff never compared it (so a bundle whose only change was a tuned timeout previewed as Identical). Adds TimeoutSeconds as an additive-optional trailing field on ExternalSystemDto (schemaVersion stays 1.1, bundleFormatVersion unchanged) and threads it through EntitySerializer (export + import), BundleImporter (Add and Overwrite), and ArtifactDiff.CompareExternalSystem. Old-bundle semantics pinned: an absent field (null) falls back to the entity default on Add and leaves the target's existing value untouched on Overwrite/diff. Beefed up the RoundTripEquivalenceTests external-system fixture with a non-default TimeoutSeconds so the reflection round-trip guard actually exercises the field instead of comparing default against default. Swept the rest of the ExternalSystemDto shape against ExternalSystemDefinition field-by-field; no other operative field is missing.
This commit is contained in:
@@ -156,6 +156,70 @@ public sealed class ArtifactDiffTests
|
||||
Assert.Equal(ConflictKind.Identical, item.Kind);
|
||||
}
|
||||
|
||||
// ============ arch-review 2026-08-19 P7-1: TimeoutSeconds in the diff (row-27 shape) ============
|
||||
|
||||
/// <summary>
|
||||
/// The bundle has always CARRIED TimeoutSeconds and BundleImporter has always APPLIED it
|
||||
/// (the per-system HTTP timeout used by ExternalSystemClient), but CompareExternalSystem
|
||||
/// never compared it — so an import whose ONLY change was a tuned timeout previewed as
|
||||
/// "Identical" and the operator had no diff evidence the value was about to change.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void ExternalSystem_ChangedTimeoutSeconds_IsReported()
|
||||
{
|
||||
var existing = new ExternalSystemDefinition("Mes", "https://mes", "ApiKey")
|
||||
{
|
||||
TimeoutSeconds = 30,
|
||||
};
|
||||
var incoming = new ExternalSystemDto("Mes", "https://mes", "ApiKey",
|
||||
MaxRetries: 0, RetryDelay: TimeSpan.Zero, Methods: [], Secrets: null, TimeoutSeconds: 120);
|
||||
|
||||
var item = _diff.CompareExternalSystem(incoming, existing, []);
|
||||
|
||||
Assert.Equal(ConflictKind.Modified, item.Kind);
|
||||
|
||||
var timeout = ChangeFor(item, "TimeoutSeconds");
|
||||
Assert.Equal("30", timeout.GetProperty("oldValue").GetString());
|
||||
Assert.Equal("120", timeout.GetProperty("newValue").GetString());
|
||||
}
|
||||
|
||||
/// <summary>An unchanged timeout must not manufacture a spurious diff row.</summary>
|
||||
[Fact]
|
||||
public void ExternalSystem_UnchangedTimeoutSeconds_IsIdentical()
|
||||
{
|
||||
var existing = new ExternalSystemDefinition("Mes", "https://mes", "ApiKey")
|
||||
{
|
||||
TimeoutSeconds = 30,
|
||||
};
|
||||
var incoming = new ExternalSystemDto("Mes", "https://mes", "ApiKey",
|
||||
MaxRetries: 0, RetryDelay: TimeSpan.Zero, Methods: [], Secrets: null, TimeoutSeconds: 30);
|
||||
|
||||
var item = _diff.CompareExternalSystem(incoming, existing, []);
|
||||
|
||||
Assert.Equal(ConflictKind.Identical, item.Kind);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Old-bundle semantics: a bundle written before this field existed carries
|
||||
/// <c>TimeoutSeconds: null</c>. The diff must treat that as "not specified by this
|
||||
/// bundle" — same as BundleImporter's Overwrite fallback — rather than reading it as
|
||||
/// "reset to 0/unset" and manufacturing a spurious change.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void ExternalSystem_MissingTimeoutSeconds_ComparesAsUnchanged()
|
||||
{
|
||||
var existing = new ExternalSystemDefinition("Mes", "https://mes", "ApiKey")
|
||||
{
|
||||
TimeoutSeconds = 30,
|
||||
};
|
||||
var incoming = new ExternalSystemDto("Mes", "https://mes", "ApiKey",
|
||||
MaxRetries: 0, RetryDelay: TimeSpan.Zero, Methods: [], Secrets: null, TimeoutSeconds: null);
|
||||
|
||||
var item = _diff.CompareExternalSystem(incoming, existing, []);
|
||||
|
||||
Assert.Equal(ConflictKind.Identical, item.Kind);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ApiMethod_ModifiedScript_EmitsStructuredLineDiff()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user