diff --git a/tests/Aetheria.Shared.Tests/ShipProductTests.cs b/tests/Aetheria.Shared.Tests/ShipProductTests.cs index f6f3bad10..77242916f 100644 --- a/tests/Aetheria.Shared.Tests/ShipProductTests.cs +++ b/tests/Aetheria.Shared.Tests/ShipProductTests.cs @@ -272,6 +272,8 @@ public sealed class ShipProductTests : IDisposable RefusedAndUntouched("product", path, "ACM"); RefusedAndUntouched("product", path, "ACM", "--name", " "); RefusedAndUntouched("product", path, "ACM", "--remove"); + RefusedAndUntouched("product", path, "ACM", "--not-offered"); + RefusedAndUntouched("product", path, "ACM", "--name", "--not-offered"); var (code, _, error) = Run("product", path, "ACM", "--name", "Acme Skiff", "--description", "Reliable."); Assert.True(code == 0, error); @@ -283,10 +285,29 @@ public sealed class ShipProductTests : IDisposable Assert.Equal(ShipModCatalog.HullKey("mod.skiff"), product.Design.Key); Assert.Equal(hull.Roles.Select(role => role.Name), product.Roles.Select(role => role.Role)); Assert.Equal("Reliable.", product.Description); + Assert.False(product.NotOffered); + + // --not-offered writes the product off the market (ruling off-market-is-unoffered-product), with or without a description. + var (offCode, offOutput, offError) = Run("product", path, "ACM", "--name", "Acme Boss Skiff", "--not-offered"); + Assert.True(offCode == 0, offError); + Assert.Contains("not offered", offOutput); + var off = Assert.Single(ShipAuthoringStore.Load(path).Products); + Assert.True(off.NotOffered); + Assert.Equal("Acme Boss Skiff", off.Name); + Assert.Null(off.Description); + Assert.Equal(0, Run("product", path, "ACM", "--name", "Acme Boss Skiff", "--description", "Unsold.", "--not-offered").Code); + off = Assert.Single(ShipAuthoringStore.Load(path).Products); + Assert.True(off.NotOffered); + Assert.Equal("Unsold.", off.Description); // The maker by name replaces the product by short name: a maker has one product of the hull. - Assert.Equal(0, Run("product", path, "Acme", "--name", "Renamed").Code); - Assert.Equal("Renamed", Assert.Single(ShipAuthoringStore.Load(path).Products).Name); + // Written again without the flag, it is back on the market. + var (renamedCode, renamedOutput, _) = Run("product", path, "Acme", "--name", "Renamed"); + Assert.Equal(0, renamedCode); + Assert.DoesNotContain("not offered", renamedOutput); + var renamed = Assert.Single(ShipAuthoringStore.Load(path).Products); + Assert.Equal("Renamed", renamed.Name); + Assert.False(renamed.NotOffered); RefusedAndUntouched("price", path, "-1"); RefusedAndUntouched("price", path, "1.5"); @@ -294,6 +315,7 @@ public sealed class ShipProductTests : IDisposable Assert.Equal(0, Run("price", path, "250").Code); Assert.Equal(250, ShipAuthoringStore.Read(path).Hull.Price); + RefusedAndUntouched("product", path, "ACM", "--remove", "--not-offered"); Assert.Equal(0, Run("product", path, "ACM", "--remove").Code); Assert.Empty(ShipAuthoringStore.Load(path).Products); RefusedAndUntouched("product", path, "ACM", "--remove"); diff --git a/tools/AetherDb/ShipAuthoringCommands.cs b/tools/AetherDb/ShipAuthoringCommands.cs index 9fd7e690a..b745e3dbf 100644 --- a/tools/AetherDb/ShipAuthoringCommands.cs +++ b/tools/AetherDb/ShipAuthoringCommands.cs @@ -8,7 +8,7 @@ using System.Linq; // Uses the existing database executable; mod authoring does not earn another binary target. public static class ShipAuthoringCommands { - private const string Usage = "ship-authoring create [--like ] | inspect | centre | validate | product (--name [--description ] | --remove) | price | compose "; + private const string Usage = "ship-authoring create [--like ] | inspect | centre | validate | product (--name [--description ] [--not-offered] | --remove) | price | compose "; public static int Run(string[] args) { @@ -93,8 +93,11 @@ public static class ShipAuthoringCommands } if (args[0] == "product" && args.Length >= 4) { - var remove = args.Length == 4 && args[3] == "--remove"; - var named = args.Length >= 5 && args[3] == "--name" && (args.Length == 5 || args.Length == 7 && args[5] == "--description"); + // A trailing --not-offered takes the product off the market (ruling off-market-is-unoffered-product); --remove takes none. + var notOffered = args[args.Length - 1] == "--not-offered"; + var count = notOffered ? args.Length - 1 : args.Length; + var remove = !notOffered && count == 4 && args[3] == "--remove"; + var named = count >= 5 && args[3] == "--name" && (count == 5 || count == 7 && args[5] == "--description"); if (!remove && !named) { Console.Error.WriteLine(Usage); return 1; } var (hull, ship, products) = ShipAuthoringStore.Load(args[1]); var (makerKey, makerName) = ShippedFaction(args[2]); @@ -112,14 +115,15 @@ public static class ShipAuthoringCommands ShipAuthoringStore.WriteProduct(cache, ship.Id, new FactionProductData { Name = args[4], - Description = args.Length == 7 ? args[6] : null, + Description = count == 7 ? args[6] : null, Design = new CultRecordRef(ShipModCatalog.HullKey(ship.Id)), Manufacturer = new CultRecordRef(makerKey), - Roles = hull.Roles.Select(role => new ProductRole { Role = role.Name }).ToList() + Roles = hull.Roles.Select(role => new ProductRole { Role = role.Name }).ToList(), + NotOffered = notOffered }); cache.FlushAsync().Wait(); } - Console.WriteLine($"{ship.Id}: {(remove ? "removed" : "wrote")} the product by {makerName}."); + Console.WriteLine($"{ship.Id}: {(remove ? "removed" : "wrote")} the product by {makerName}{(notOffered ? ", not offered" : "")}."); return 0; } Console.Error.WriteLine(Usage);