AetherDb tests: a kindless station hull does not lock the tool out; station-kinds sets a hull's kind (red)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
soul 2026-10-23 01:18:21 +02:00
commit 17e2da0092

View file

@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using GameCult.Caching;
@ -18,8 +19,8 @@ public sealed class StationKindsCatalogTests : IDisposable
"habitat-support", "urban", "administrative", "power", "research", "salvage", "black-market", "waystation"
};
// The catalog this command first runs on holds a station hull with no kind, which Open refuses, so the command opens
// the catalog itself: Zenith gets the ruled kind, a kind it already has is kept.
// The catalog this command first runs on holds a station hull with no kind. The game's Open refuses it; the tool's Open
// names the hull and goes on, so the command can give it one: Zenith gets the ruled kind, a kind it already has is kept.
[Fact]
public void ZenithGetsTheRuledKindAndAKindedOneIsKept()
{
@ -34,12 +35,14 @@ public sealed class StationKindsCatalogTests : IDisposable
}
Assert.Throws<InvalidOperationException>(() => AetheriaStores.Open(path));
var named = new List<string>();
var dry = new StringWriter();
using (var cache = StationKindsCatalog.OpenCatalog(false, path)) StationKindsCatalog.Run(cache, false, dry);
using (var cache = AetheriaStores.Open(path, kindlessStation: named.Add)) StationKindsCatalog.Run(cache, false, dry);
Assert.Contains("Zenith", Assert.Single(named));
Assert.Contains("Zenith: kind trade-hub", dry.ToString());
var applied = new StringWriter();
using (var cache = StationKindsCatalog.OpenCatalog(true, path))
using (var cache = AetheriaStores.Open(path, catalogWritable: true, kindlessStation: _ => { }))
{
StationKindsCatalog.Run(cache, true, applied);
cache.FlushAsync().Wait();
@ -52,11 +55,77 @@ public sealed class StationKindsCatalogTests : IDisposable
}
var again = new StringWriter();
using (var cache = StationKindsCatalog.OpenCatalog(true, path)) StationKindsCatalog.Run(cache, true, again);
using (var cache = AetheriaStores.Open(path, catalogWritable: true, kindlessStation: _ => { })) StationKindsCatalog.Run(cache, true, again);
Assert.Contains("Zenith: already has a kind, kept", again.ToString());
Assert.Contains("Nothing to author", again.ToString());
}
private string SeedRoot(params string[] kindlessStations)
{
var root = Path.Combine(_root, "root" + Guid.NewGuid().ToString("N"));
Directory.CreateDirectory(Path.Combine(root, "GameData"));
using var seed = AetheriaStores.Open(Path.Combine(root, "GameData", "Aetheria.cc"), catalogWritable: true);
seed.Upsert(new VerseGrammar { Revision = 1 });
seed.Upsert(new InputLayout { Rows = new InputLayoutRow[] { new InputLayoutRowSpacer { Height = 1 } } });
foreach (var name in kindlessStations) seed.Upsert(new HullData { Name = name, HullType = HullType.Station, Shape = new Shape(2, 2), Price = 1 });
seed.Upsert(new HullData { Name = "Cutter", HullType = HullType.Ship, Shape = new Shape(2, 2), Price = 1 });
seed.FlushAsync().Wait();
return root;
}
// One kindless station hull does not lock the owner out of the catalog: the tool opens it, names the hull, and the
// command that sets kinds is reachable. The game's own Open still refuses until every station hull has a kind.
[Fact]
public void AKindlessStationHullDoesNotLockTheToolOut()
{
var root = SeedRoot("Zenith", "Second Station");
var catalog = Path.Combine(root, "GameData", "Aetheria.cc");
Assert.Throws<InvalidOperationException>(() => AetheriaStores.Open(catalog));
var warned = new List<string>();
var applied = new StringWriter();
using (var db = AetherDb.Open(catalogWritable: true, root: root, warn: warned.Add))
{
Assert.Contains(warned, line => line.Contains("Zenith"));
Assert.Contains(warned, line => line.Contains("Second Station"));
Assert.Equal(0, StationKindsCatalog.Run(db.Cache, true, applied));
Assert.Contains("Second Station", applied.ToString());
db.Cache.FlushAsync().Wait();
}
Assert.Throws<InvalidOperationException>(() => AetheriaStores.Open(catalog));
using (var db = AetherDb.Open(catalogWritable: true, root: root, warn: _ => { }))
{
var dry = new StringWriter();
Assert.Equal(0, StationKindsCatalog.SetHullKind(db.Cache, "Second Station", "mining", false, dry));
Assert.Contains("Dry run", dry.ToString());
Assert.False(db.Cache.GetAll<HullData>().Single(h => h.Name == "Second Station").Kind.IsSet());
var set = new StringWriter();
Assert.Equal(0, StationKindsCatalog.SetHullKind(db.Cache, "Second Station", "mining", true, set));
db.Cache.FlushAsync().Wait();
}
using var strict = AetheriaStores.Open(catalog);
Assert.Equal(StationKindData.KeyOf("mining").Value, strict.GetAll<HullData>().Single(h => h.Name == "Second Station").Kind.Key.Value);
}
[Fact]
public void SetHullKindRefusesWhatIsNotAStationHullOrAHeldKind()
{
var root = SeedRoot("Zenith");
using var db = AetherDb.Open(catalogWritable: true, root: root, warn: _ => { });
StationKindsCatalog.Run(db.Cache, true, new StringWriter());
string Refusal(string hull, string kind)
{
var output = new StringWriter();
Assert.Equal(1, StationKindsCatalog.SetHullKind(db.Cache, hull, kind, true, output));
return output.ToString();
}
Assert.Contains("no station kind", Refusal("Zenith", "nowhere"));
Assert.Contains("no station hull", Refusal("Cutter", "mining"));
Assert.Contains("no station hull", Refusal("Missing", "mining"));
Assert.False(db.Cache.GetAll<HullData>().Single(h => h.Name == "Cutter").Kind.IsSet());
}
[Fact]
public void DryRunWritesNothingApplyIsIdempotent()
{