XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFEEDToolBox

【WinUI】XFE精英危险工具箱

公开
关注 0 Fork 0 Star 0
UTF-8
using XFEExtension.NetCore.XUnit.Attributes;
using XFEEDToolBox.Core.Data;
using XFEEDToolBox.Core.Models;
using XFEEDToolBox.Core.Native;

namespace XFEEDToolBox.Test;

[TestFixture]
public class FleetTests
{
    private const string Loadout = """
        {"timestamp":"2026-10-21T01:00:00Z","event":"Loadout","Ship":"mandalay","ShipID":3,"ShipName":"TEST","ShipIdent":"T-01","CargoCapacity":8,"MaxJumpRange":66.715927,"Rebuy":3489668,"FuelCapacity":{"Main":32,"Reserve":0.77},"Modules":[
          {"Slot":"MainEngines","Item":"int_engine_size5_class5","On":true,"Priority":0,"Health":0.96,"Value":5103960,"Engineering":{"Engineer":"Test Engineer","BlueprintName":"Engine_Dirty","Level":3,"Quality":1.0,"ExperimentalEffect":"test_effect","Modifiers":[{"Label":"Integrity","Value":96.459999,"OriginalValue":106,"LessIsGood":0},{"Label":"PowerDraw","Value":6.6096,"OriginalValue":6.12,"LessIsGood":1}]}},
          {"Slot":"Slot10","Item":"int_dockingcomputer_advanced","On":true,"Priority":1,"Health":1},
          {"Slot":"Slot11","Item":"int_supercruiseassist","On":false,"Priority":1,"Health":1}
        ]}
        """;

    [Test] public void LoadoutPreservesSlotsEngineeringAndAssistState()
    {
        var p = new PlayerProjector(); p.Apply(Check.Json(Loadout)); var s = p.State.Ships.Single();
        Check.Equal(3L, p.State.CurrentShipId!.Value); Check.Equal(8, s.CargoCapacity!.Value); Check.Equal(32d, s.FuelMain!.Value);
        Check.Equal(3, s.Modules.Count); var engine = s.Modules[0]; Check.Equal("MainEngines", engine.Slot); Check.Equal(0, engine.Priority);
        Check.Equal("Engine_Dirty", engine.Engineering!.Blueprint); Check.Equal(3, engine.Engineering.Level); Check.Equal(106d, engine.Engineering.Modifiers[0].OriginalValue!.Value);
        Check.Equal(96.459999d, engine.Engineering.Modifiers[0].Value!.Value); Check.That(engine.Engineering.Modifiers[1].LessIsGood);
        Check.That(p.State.AdvancedDocking && !p.State.SupercruiseAssist);
    }
    [Test] public void StoredShipsRetainsKnownConfigurationWithoutInventingRemoteModules()
    {
        var p = new PlayerProjector(); p.Apply(Check.Json(Loadout));
        p.Apply(Check.Json("""{"timestamp":"2026-10-21T01:01:00Z","event":"StoredShips","StarSystem":"Vaka","MarketID":10,"ShipsHere":[{"ShipID":3,"ShipType":"mandalay","Value":100,"Hot":false}],"ShipsRemote":[{"ShipID":7,"ShipType":"python","Name":"Remote","StarSystem":"Sol","ShipMarketID":20,"TransferPrice":123,"TransferTime":300,"Value":500}]}"""));
        Check.Equal(2, p.State.Ships.Count); var remote = p.State.Ships.Single(x => x.ShipId == 7); Check.Equal("Sol", remote.SystemName); Check.Equal(20L, remote.MarketId!.Value);
        Check.That(remote.ConfigurationAt == null && remote.Modules.Count == 0); Check.Equal(3, p.State.Ships.Single(x => x.ShipId == 3).Modules.Count);
        p.Apply(Check.Json("""{"event":"ShipyardSell","SellShipID":7}""")); Check.Equal(1, p.State.Ships.Count);
    }
    [Test] public void ModuleChangeNeedsNewerFullLoadout()
    {
        var p = new PlayerProjector(); p.Apply(Check.Json(Loadout));
        p.Apply(Check.Json("""{"timestamp":"2026-10-21T01:02:00Z","event":"ModuleSwap","ShipID":3}"""));
        p.Apply(Check.Json(Loadout)); Check.That(p.State.Ships.Single().ConfigurationChanged);
        p.Apply(Check.Json(Loadout.Replace("01:00:00Z", "01:03:00Z"))); Check.That(!p.State.Ships.Single().ConfigurationChanged);
    }
    [Test] public void EmptyStoredModulesIsObservedAndOldSnapshotCannotRestoreItems()
    {
        var p = new PlayerProjector();
        p.Apply(Check.Json("""{"timestamp":"2026-10-21T01:00:00Z","event":"StoredModules","Items":[{"Name":"engine","Name_Localised":"Engine","StorageSlot":4,"StarSystem":"Sol","MarketID":1,"BuyPrice":100,"TransferCost":20,"TransferTime":60,"EngineerModifications":"Engine_Dirty","Level":3,"Quality":1}]}"""));
        Check.Equal("Engine_Dirty", p.State.StoredModules.Single().Engineering); Check.Equal(3, p.State.StoredModules.Single().EngineeringLevel!.Value);
        p.Apply(Check.Json("""{"timestamp":"2026-10-21T01:02:00Z","event":"StoredModules","Items":[]}"""));
        p.Apply(Check.Json("""{"timestamp":"2026-10-21T01:01:00Z","event":"StoredModules","Items":[{"Name":"stale"}]}"""));
        Check.Equal(0, p.State.StoredModules.Count); Check.That(p.State.StoredModulesAt != null);
        Check.Equal("已读取", ReadAcceptance.Build(p.State, null).Single(x => x.Category == "寄存模块").Status);
    }
    [Test] public void AccountChangeClearsFleetAndStoredModules()
    {
        var p = new PlayerProjector(); p.Apply(Check.Json("""{"event":"LoadGame","FID":"a"}""")); p.Apply(Check.Json(Loadout));
        p.Apply(Check.Json("""{"event":"StoredModules","Items":[{"Name":"test"}]}"""));
        p.Apply(Check.Json("""{"event":"LoadGame","FID":"b","ShipID":0}"""));
        Check.Equal(0, p.State.Ships.Count); Check.Equal(0, p.State.StoredModules.Count); Check.That(p.State.StoredModulesAt == null);
    }
    [Test] public void MaterialConsumptionAndOldSnapshotDoNotRestoreSpentIngredients()
    {
        var p = new PlayerProjector();
        p.Apply(Check.Json("""{"timestamp":"2026-10-21T01:00:00Z","event":"Materials","Raw":[{"Name":"iron","Count":8}],"Manufactured":[],"Encoded":[]}"""));
        p.Apply(Check.Json("""{"timestamp":"2026-10-21T01:01:00Z","event":"EngineerCraft","Ingredients":[{"Name":"iron","Count":3}]}"""));
        p.Apply(Check.Json("""{"timestamp":"2026-10-21T01:00:00Z","event":"Materials","Raw":[{"Name":"iron","Count":8}]}"""));
        Check.Equal(5, p.State.Materials.Single().Count); Check.Equal("Raw", p.State.Materials.Single().Group); Check.That(p.State.MaterialsReliable);
    }
    [Test] public void MissingBackpackAndEmptyBackpackHaveDifferentCoverage()
    {
        var p = new PlayerProjector();
        Check.Equal("缺少快照", ReadAcceptance.Build(p.State, null).Single(x => x.Category == "随身背包").Status);
        p.Apply(Check.Json("""{"timestamp":"2026-10-21T01:00:00Z","event":"Backpack","Items":[],"Components":[],"Consumables":[],"Data":[]}"""));
        Check.Equal("已读取", ReadAcceptance.Build(p.State, null).Single(x => x.Category == "随身背包").Status);
        p.Apply(Check.Json("""{"event":"TransferMicroResources"}""")); Check.That(!p.State.BackpackReliable);
    }
    [Test] public async Task NativeResearchRejectsStandardGameBeforeStartingAnyHelper()
    {
        await using var bridge = new NativeBridgeClient();
        try { await bridge.ConnectAsync("must-not-execute", "must-not-execute", new() { ProcessId = 1, ImageSha256 = new string('a', 64), ProfileMode = "standard" }); }
        catch (InvalidOperationException e) { Check.That(e.Message.Contains("禁止连接在线客户端")); Check.That(!bridge.Connected); return; }
        throw new InvalidOperationException("Standard game environment was not rejected.");
    }
}