feat(esg): TimeoutSeconds in ExternalSystem UI form + Component-ExternalSystemGateway doc sync — closes the spec-drift gap

Claude-Session: https://claude.ai/code/session_01MtdgwpEeCUn6cUA5f1LMPj
This commit is contained in:
Joseph Doherty
2026-07-10 04:06:53 -04:00
parent e3ef320359
commit f0dd016207
3 changed files with 74 additions and 3 deletions
@@ -1,3 +1,4 @@
using System.Linq;
using System.Security.Claims;
using Bunit;
using Microsoft.AspNetCore.Components;
@@ -67,4 +68,65 @@ public class ExternalSystemFormAuditDrillinTests : BunitContext
Assert.Empty(cut.FindAll("a[data-test=\"audit-link\"]"));
});
}
[Fact]
public void Form_RendersTimeoutSecondsInput_WithGatewayDefaultHelperText()
{
// PLAN-06 Task 12: the per-system TimeoutSeconds column (Task 8) needs a
// Central UI form field so Design users can actually set it.
var cut = Render<ExternalSystemForm>();
cut.WaitForAssertion(() =>
{
Assert.Contains("Timeout (seconds)", cut.Markup);
Assert.Contains("0 = use gateway default", cut.Markup);
});
}
[Fact]
public void SavingNewSystem_WithTimeoutSeconds_PassesTimeoutSecondsToRepository()
{
var cut = Render<ExternalSystemForm>();
// Re-query between each Change(): two-way binding re-renders the form and
// invalidates previously found element references.
cut.FindAll("input[type=text]")[0].Change("ERP-Beta"); // Name
cut.FindAll("input[type=text]")[1].Change("https://erp-beta.example.test"); // Endpoint URL
cut.FindAll("input[type=number]")[2].Change("20"); // Timeout (seconds)
cut.FindAll("button").First(b => b.TextContent.Contains("Save")).Click();
cut.WaitForAssertion(() =>
{
_repo.Received().AddExternalSystemAsync(
Arg.Is<ExternalSystemDefinition>(e => e.TimeoutSeconds == 20));
_repo.Received().SaveChangesAsync();
});
}
[Fact]
public void SavingEditedSystem_UpdatesTimeoutSecondsOnExisting()
{
var existing = new ExternalSystemDefinition("ERP-Delta", "https://erp-delta.example.test", "ApiKey")
{
Id = 11,
TimeoutSeconds = 10,
};
_repo.GetExternalSystemByIdAsync(11, Arg.Any<CancellationToken>()).Returns(existing);
var cut = Render<ExternalSystemForm>(p => p.Add(c => c.Id, 11));
cut.WaitForState(() => cut.FindAll("input[type=number]").Count >= 3);
// The Timeout field must be pre-filled from the existing entity on load.
Assert.Contains(cut.FindAll("input[type=number]"), i => i.GetAttribute("value") == "10");
cut.FindAll("input[type=number]")[2].Change("60"); // Timeout (seconds)
cut.FindAll("button").First(b => b.TextContent.Contains("Save")).Click();
cut.WaitForAssertion(() =>
{
Assert.Equal(60, existing.TimeoutSeconds);
_repo.Received().UpdateExternalSystemAsync(existing);
});
}
}