fix(launcher): Campaign LA close LA2 review findings

This commit is contained in:
Erik 2026-08-14 17:20:02 +02:00
parent 000ea979d5
commit 1c5e66c05b
8 changed files with 346 additions and 58 deletions

View file

@ -269,6 +269,105 @@ public sealed class HeadlessConfigurationLoaderTests
() => HeadlessConfigurationLoader.Load(file.Path));
}
/// <summary>
/// Campaign LA LA2 review fix: the pinned contract is presence-aware.
/// Normal play omits mode and supplies character/policy; probe supplies
/// mode and omits character/policy. JSON null is not another spelling of
/// omission for any of those conditional fields.
/// </summary>
[Theory]
[InlineData(false, "mode")]
[InlineData(false, "character")]
[InlineData(false, "policy")]
[InlineData(true, "character")]
[InlineData(true, "policy")]
public void ConditionalSessionFieldsRejectExplicitJsonNull(
bool probe,
string nullField)
{
string mode = probe
? "\"mode\":\"probe\","
: nullField == "mode"
? "\"mode\":null,"
: string.Empty;
string character = nullField == "character"
? "\"character\":null,"
: probe
? string.Empty
: "\"character\":{\"index\":0},";
string policy = nullField == "policy"
? "\"policy\":null,"
: probe
? string.Empty
: "\"policy\":{\"id\":\"idle\"},";
using TemporaryConfiguration file = TemporaryConfiguration.Create(
$$"""
{
"version": 1,
"sessions": [
{
"id": "explicit-null",
"endpoint": { "host": "127.0.0.1", "port": 9000 },
"account": "account",
{{mode}}
{{character}}
{{policy}}
"credential": { "provider": "environment", "reference": "PASSWORD" }
}
]
}
""");
HeadlessConfigurationException exception = Assert.Throws<
HeadlessConfigurationException>(
() => HeadlessConfigurationLoader.Load(file.Path));
Assert.Contains(
$"'{nullField}'",
exception.Message,
StringComparison.Ordinal);
Assert.Contains(
"cannot be null",
exception.Message,
StringComparison.Ordinal);
}
[Fact]
public void PresenceAwareValidationKeepsUnmappedMemberRejection()
{
using TemporaryConfiguration file = TemporaryConfiguration.Create(
ConfigurationWith(Session(
"bot",
"PASSWORD",
"\"notAContractField\":true")));
Assert.Throws<JsonException>(
() => HeadlessConfigurationLoader.Load(file.Path));
}
[Fact]
public void PresenceAwareValidationKeepsTypedValueRejection()
{
using TemporaryConfiguration file = TemporaryConfiguration.Create(
"""
{
"version": 1,
"sessions": [
{
"id": "wrong-type",
"endpoint": { "host": "127.0.0.1", "port": 9000 },
"account": "account",
"mode": { "value": "probe" },
"credential": { "provider": "environment", "reference": "PASSWORD" }
}
]
}
""");
Assert.Throws<JsonException>(
() => HeadlessConfigurationLoader.Load(file.Path));
}
[Fact]
public void ProbeSessionDeclaringCharacterFailsLoadNamingTheField()
{