Aetheria/tests/Aetheria.Shared.Tests/SmokeHullCommandTests.cs
Meta Vens f7cf3257d0 Sweep: every old design-name literal in C# becomes its generic name; product texts ship without the authoring notes
Scenarios, tests, AetherDb templates and name-keyed maps now resolve designs by the generic names (231 literals in 30 files);
TargetingCatalog's product texts lose the authoring note, Steadfast carries its r3 text, DragOnBreath plight's old design text.
Not built yet; tests that depend on the generic-designs command and new tests follow.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 20:29:10 +02:00

93 lines
5.1 KiB
C#

using System;
using System.IO;
using System.Linq;
using GameCult.Caching;
using GameCult.Caching.MessagePack;
using Xunit;
// AetherDb's smoke-hull command (aetheria-release, cut scenarios-smoke), run on a scratch copy of the shipped catalog
// with the Smoke Test Hull removed from it.
public sealed class SmokeHullCommandTests : IDisposable
{
private const string HullName = "Smoke Test Hull";
private readonly string _root = Path.Combine(Path.GetTempPath(), "smoke-hull-command-" + Guid.NewGuid().ToString("N"));
private string CatalogPath => Path.Combine(_root, "GameData", "Aetheria.cc");
public SmokeHullCommandTests()
{
Directory.CreateDirectory(Path.Combine(_root, "GameData"));
Directory.CreateDirectory(Path.Combine(_root, "Assets", "Resources"));
var shipped = AetherDb.FindRoot();
File.Copy(Path.Combine(shipped, "GameData", "Aetheria.cc"), CatalogPath);
File.Copy(Path.Combine(shipped, "Assets", "Resources", "Settings.asset"), Path.Combine(_root, "Assets", "Resources", "Settings.asset"));
using var cache = new CultCache(TestCatalog.Registry());
cache.AddBackingStore(new SingleFileMessagePackBackingStore(CatalogPath), AetheriaStores.CatalogTypes);
foreach (var hull in cache.GetAll<HullData>().Where(h => h.Name == HullName).ToArray()) Assert.True(cache.Remove(cache.RefOf(hull).Key));
cache.FlushAsync().Wait();
}
public void Dispose() => Directory.Delete(_root, true);
private (HullData[] Hulls, HullData Longinus, EquippableItemData[] Fitted, int Products) Read()
{
var db = AetherDb.Open(root: _root);
try
{
var hulls = db.Cache.GetAll<HullData>().Where(h => h.Name == HullName).ToArray();
var keys = hulls.Select(h => db.Cache.RefOf(h).Key).ToArray();
var products = db.Cache.GetAll<FactionProductData>().Count(p => keys.Any(k => p.Design.Key.Equals(k)));
var fitted = new[] { "Charged Scatter Blaster", "Autocannon" }
.Select(name => db.Cache.GetAll<EquippableItemData>().Single(d => d.Name == name)).ToArray();
return (hulls, db.Cache.GetAll<HullData>().Single(h => h.Name == "Longinus"), fitted, products);
}
finally { db.Cache.Dispose(); }
}
[Fact]
public void A_dry_run_writes_nothing_and_apply_lands_one_productless_hull_once()
{
var before = File.ReadAllBytes(CatalogPath);
Assert.Empty(Read().Hulls);
Assert.Equal(0, Program.SmokeHullCatalog(apply: false, root: _root));
Assert.Equal(before, File.ReadAllBytes(CatalogPath));
Assert.Empty(Read().Hulls);
Assert.Equal(0, Program.SmokeHullCatalog(apply: true, root: _root));
var landed = File.ReadAllBytes(CatalogPath);
Assert.NotEqual(before, landed);
var (hulls, longinus, fitted, products) = Read();
var hull = Assert.Single(hulls);
Assert.Equal(0, products);
// The Longinus's own hardpoints survive (moved over by the margin the flank blocks need), and four are added: each takes
// the gear the scenarios fit to it.
Assert.Equal(longinus.Hardpoints.Count + 4, hull.Hardpoints.Count);
var added = hull.Hardpoints.Skip(longinus.Hardpoints.Count).ToArray();
var (chargeBlast, autocannon) = (fitted[0], fitted[1]);
Assert.Equal(longinus.Hardpoints.Select(h => (h.Type, h.Position.x + 2, h.Position.y, h.Rotation)),
hull.Hardpoints.Take(longinus.Hardpoints.Count).Select(h => (h.Type, h.Position.x, h.Position.y, h.Rotation)));
Assert.Single(added, h => h.Type == HardpointType.Energy && h.Rotation == ItemRotation.None && h.Takes(chargeBlast));
Assert.Single(added, h => h.Type == HardpointType.Ballistic && h.Rotation == ItemRotation.None && h.Takes(autocannon));
Assert.Single(added, h => h.Type == HardpointType.Ballistic && h.Rotation == ItemRotation.Clockwise && h.Takes(autocannon));
Assert.Single(added, h => h.Type == HardpointType.Energy && h.Rotation == ItemRotation.CounterClockwise && h.Takes(chargeBlast));
// Each is a full 2x2 block with the default firing arc (0 uses the game setting; the side mounts bear by it, never wider)
// and the Longinus-class armour the command writes.
Assert.All(added, hardpoint =>
{
Assert.Equal((2, 2, 0f, 20f), (hardpoint.Shape.Width, hardpoint.Shape.Height, hardpoint.FiringArc, hardpoint.Armor));
Assert.Equal(4, hardpoint.Shape.Coordinates.Length);
});
foreach (var hardpoint in added)
foreach (var cell in hardpoint.Shape.Coordinates)
Assert.True(hull.Shape[hardpoint.Position + cell], "a new hardpoint lies outside the hull's cells");
// A second apply finds the hull authored and writes nothing; so does a dry run.
Assert.Equal(0, Program.SmokeHullCatalog(apply: true, root: _root));
Assert.Equal(landed, File.ReadAllBytes(CatalogPath));
Assert.Equal(0, Program.SmokeHullCatalog(apply: false, root: _root));
Assert.Equal(landed, File.ReadAllBytes(CatalogPath));
Assert.Single(Read().Hulls);
}
}