fix(external-system-gateway): resolve ExternalSystemGateway-015..017 — treat MaxRetries=0 as unset, scope HTTP connection cap to gateway clients, no bare trailing '?'
This commit is contained in:
@@ -396,9 +396,14 @@ public class ExternalSystemClientTests
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CachedCall_TransientFailure_ZeroMaxRetriesIsHonouredNotTreatedAsUnset()
|
||||
public async Task CachedCall_TransientFailure_ZeroMaxRetriesIsTreatedAsUnsetNotRetryForever()
|
||||
{
|
||||
// MaxRetries == 0 must mean "never retry", not "fall back to the S&F default".
|
||||
// ExternalSystemGateway-015: the Store-and-Forward engine interprets a stored
|
||||
// MaxRetries of 0 as "no limit" (retry forever) — see StoreAndForwardMessage.cs
|
||||
// and the retry-sweep guard `MaxRetries > 0 && ...`. The entity's non-nullable
|
||||
// int default is also 0, so passing 0 verbatim would buffer every cached call
|
||||
// as an unbounded retry loop. The ESG must therefore treat the entity's
|
||||
// MaxRetries == 0 as "unset" and pass null, so the bounded S&F default applies.
|
||||
var system = new ExternalSystemDefinition("TestAPI", "https://api.example.com", "none")
|
||||
{
|
||||
Id = 1,
|
||||
@@ -432,7 +437,9 @@ public class ExternalSystemClientTests
|
||||
await client.CachedCallAsync("TestAPI", "postData");
|
||||
|
||||
var (maxRetries, _) = ReadBufferedRetrySettings(connStr);
|
||||
Assert.Equal(0, maxRetries); // honoured — not the default of 99
|
||||
// Must be the bounded S&F default, never 0 — a stored 0 would mean retry-forever.
|
||||
Assert.Equal(99, maxRetries);
|
||||
Assert.NotEqual(0, maxRetries);
|
||||
}
|
||||
|
||||
// ── ExternalSystemGateway-005: HttpRequestMessage / HttpResponseMessage disposal ──
|
||||
@@ -615,6 +622,33 @@ public class ExternalSystemClientTests
|
||||
Assert.Contains("page=2", uri);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Call_GetWithAllNullParameters_DoesNotAppendTrailingQuestionMark()
|
||||
{
|
||||
// ExternalSystemGateway-017: a GET method invoked with a non-empty parameter
|
||||
// dictionary whose values are all null has an effectively empty query string.
|
||||
// The URL must be identical to the no-parameters case — no bare trailing '?'.
|
||||
var system = new ExternalSystemDefinition("TestAPI", "https://api.example.com", "none") { Id = 1 };
|
||||
var method = new ExternalSystemMethod("search", "GET", "/search") { Id = 1, ExternalSystemDefinitionId = 1 };
|
||||
StubResolution(system, method);
|
||||
|
||||
var handler = new RequestCapturingHandler(HttpStatusCode.OK, "{}");
|
||||
_httpClientFactory.CreateClient(Arg.Any<string>()).Returns(new HttpClient(handler));
|
||||
|
||||
var client = new ExternalSystemClient(
|
||||
_httpClientFactory, _repository, NullLogger<ExternalSystemClient>.Instance);
|
||||
|
||||
await client.CallAsync("TestAPI", "search", new Dictionary<string, object?>
|
||||
{
|
||||
["q"] = null,
|
||||
["page"] = null,
|
||||
});
|
||||
|
||||
var uri = handler.LastUri!.AbsoluteUri;
|
||||
Assert.Equal("https://api.example.com/search", uri);
|
||||
Assert.DoesNotContain("?", uri);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Call_PostWithParameters_SendsJsonBody()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user