Registry: one throw for a missing or non-array ships; tests pin refusal codes, title bound and plan order
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
46d92200e5
commit
a43f90b48d
2 changed files with 38 additions and 10 deletions
|
|
@ -43,16 +43,7 @@ public sealed class ConceptSubjectRegistry(
|
|||
public static IReadOnlyList<ManifestShip> ParseManifest(string json)
|
||||
{
|
||||
using var document = ParseDocument(json);
|
||||
if (document.RootElement.ValueKind != JsonValueKind.Object)
|
||||
{
|
||||
throw new ManifestRefusedException("ships", "Missing");
|
||||
}
|
||||
|
||||
if (!document.RootElement.TryGetProperty("ships", out var ships))
|
||||
{
|
||||
throw new ManifestRefusedException("ships", "Missing");
|
||||
}
|
||||
|
||||
var ships = ShipsOf(document.RootElement);
|
||||
if (ships.ValueKind != JsonValueKind.Array)
|
||||
{
|
||||
throw new ManifestRefusedException("ships", "Missing");
|
||||
|
|
@ -91,6 +82,16 @@ public sealed class ConceptSubjectRegistry(
|
|||
return result;
|
||||
}
|
||||
|
||||
private static JsonElement ShipsOf(JsonElement root)
|
||||
{
|
||||
if (root.ValueKind != JsonValueKind.Object)
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
return root.TryGetProperty("ships", out var ships) ? ships : default;
|
||||
}
|
||||
|
||||
private static JsonDocument ParseDocument(string json)
|
||||
{
|
||||
try
|
||||
|
|
|
|||
|
|
@ -170,6 +170,8 @@ public sealed class ConceptSubjectRegistryTests
|
|||
[InlineData("""{"ships":[{"id":"a","name":"bad\ntitle"}]}""", "ships[0].name", "InvalidTitle")]
|
||||
[InlineData("""{"ships":["a"]}""", "ships[0].id", "Missing")]
|
||||
[InlineData("""{"fleet":[]}""", "ships", "Missing")]
|
||||
[InlineData("[]", "ships", "Missing")]
|
||||
[InlineData("""{"ships":5}""", "ships", "Missing")]
|
||||
[InlineData("not json", "manifest", "NotJson")]
|
||||
public async Task Malformed_manifest_is_refused_whole(string json, string field, string code)
|
||||
{
|
||||
|
|
@ -180,6 +182,28 @@ public sealed class ConceptSubjectRegistryTests
|
|||
Assert.Empty(await world.Fresh(db => db.ConceptSubjects.ToListAsync()));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void A_title_of_exactly_the_column_width_is_accepted_and_one_more_is_refused()
|
||||
{
|
||||
var fits = new string('t', ConceptSubjectRegistry.MaxTitleLength);
|
||||
|
||||
Assert.Equal(fits, ConceptSubjectRegistry.ParseManifest($$"""{"ships":[{"id":"a","name":"{{fits}}"}]}""").Single().Title);
|
||||
var refusal = Assert.Throws<ManifestRefusedException>(() =>
|
||||
ConceptSubjectRegistry.ParseManifest($$"""{"ships":[{"id":"a","name":"{{fits}}x"}]}"""));
|
||||
Assert.Equal(("ships[0].name", "TooLong"), (refusal.Field, refusal.Code));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Absent_keys_are_planned_in_key_order()
|
||||
{
|
||||
using var world = new World();
|
||||
await world.Apply(Fixture());
|
||||
|
||||
var plan = await world.Plan([new ManifestShip("zzz-new", "New")]);
|
||||
|
||||
Assert.Equal(["zzz-new", "bison", "ibis-mk2", "kestrel"], plan.Entries.Select(e => e.Key));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task A_bad_entry_after_good_ones_refuses_the_file_and_writes_no_row()
|
||||
{
|
||||
|
|
@ -201,8 +225,10 @@ public sealed class ConceptSubjectRegistryTests
|
|||
using var world = new World();
|
||||
var collection = await Assert.ThrowsAsync<ManifestRefusedException>(() => world.Registry(world.NewDb()).PlanAsync(canary, [], None, default));
|
||||
Assert.DoesNotContain("CANARY", collection.Message);
|
||||
Assert.Equal(("collection", "InvalidKey"), (collection.Field, collection.Code));
|
||||
var retire = await Assert.ThrowsAsync<ManifestRefusedException>(() => world.Registry(world.NewDb()).PlanAsync(Collection, [], [canary], default));
|
||||
Assert.DoesNotContain("CANARY", retire.Message);
|
||||
Assert.Equal(("retire", "InvalidKey"), (retire.Field, retire.Code));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
|
|
@ -282,6 +308,7 @@ public sealed class ConceptSubjectRegistryTests
|
|||
var missing = new StringWriter();
|
||||
Assert.Equal(ConceptCommand.Refused, await ConceptCommand.RunAsync(["apply", Collection, Path.Combine(world.Dir, "CANARY-missing.json")], services, missing));
|
||||
Assert.DoesNotContain("CANARY", missing.ToString());
|
||||
Assert.Equal("refused manifest Unreadable", missing.ToString().Trim());
|
||||
Assert.Empty(await world.Fresh(db => db.ConceptSubjects.ToListAsync()));
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue