using System.Text.Json; using XFEEDToolBox.Core.Execution; using XFEEDToolBox.Core.Models; using XFEExtension.NetCore.XUnit.Attributes; namespace XFEEDToolBox.Test; [TestFixture] public class ExternalServiceCostsTests { private static async Task<(TradeJourneyCheckpoint Checkpoint,TradeLeg Leg)> Fixture(TemporaryDirectory temp,string fid="F1",int ship=7,int market=1) { var at=DateTimeOffset.UtcNow.AddMinutes(-1);var bought=new ConfirmedTradeReceipt(Guid.NewGuid().ToString("N"),1,"gold",1,40,10000,9960,0,1,"F1",7,at.AddMinutes(-1),at,true,true); object[] rows=[new{timestamp=at.AddSeconds(-10),@event="LoadGame",FID=fid,ShipID=ship},new{timestamp=at.AddSeconds(1),@event="Docked",MarketID=market}, new{timestamp=at.AddSeconds(2),@event="RefuelAll",Cost=23},new{timestamp=at.AddSeconds(3),@event="RepairAll",Cost=65}]; await File.WriteAllLinesAsync(temp.File("Journal.fixture.log"),rows.Select(r=>JsonSerializer.Serialize(r))); return(new("F1",7,at,bought),new(){From=new(){MarketId=1},To=new(){MarketId=2}}); } [Test] public async Task ExactConfirmedServiceCostsExplainWalletGapAndNeverDuplicate() { using var temp=new TemporaryDirectory();var (checkpoint,leg)=await Fixture(temp);using var ledger=new TradeProfitLedger(temp.File("ledger"));ledger.RecordTrade(checkpoint.Bought!); Check.That(await ExternalServiceCosts.ReconcileAsync(temp.Path,temp.Path,checkpoint,leg,ledger,10000,9872,default)); Check.Equal(23m,ledger.Snapshot.Fuel);Check.Equal(65m,ledger.Snapshot.Repairs); Check.That(!await ExternalServiceCosts.ReconcileAsync(temp.Path,temp.Path,checkpoint,leg,ledger,10000,9872,default)); Check.Equal(88m,ledger.Snapshot.Expenses);Check.Equal(2,Directory.GetFiles(temp.File("external-service-proofs")).Length); } [Test] public async Task WrongWalletShipCommanderOrStationCannotCreateBalancingExpenses() { foreach(var fixture in new[]{("F1",7,1,9871L),("F2",7,1,9872L),("F1",8,1,9872L),("F1",7,99,9872L)}) { using var temp=new TemporaryDirectory();var (checkpoint,leg)=await Fixture(temp,fixture.Item1,fixture.Item2,fixture.Item3);using var ledger=new TradeProfitLedger(temp.File("ledger"));ledger.RecordTrade(checkpoint.Bought!); Check.That(!await ExternalServiceCosts.ReconcileAsync(temp.Path,temp.Path,checkpoint,leg,ledger,10000,fixture.Item4,default));Check.Equal(0m,ledger.Snapshot.Expenses); } } [Test] public async Task RecoveryAfterOnlyOneExternalCostWasSavedAddsOnlyTheRemainingReceipt() { using var temp=new TemporaryDirectory();var (checkpoint,leg)=await Fixture(temp); using(var completed=new TradeProfitLedger(temp.File("complete-ledger"))) {completed.RecordTrade(checkpoint.Bought!);Check.That(await ExternalServiceCosts.ReconcileAsync(temp.Path,temp.Path,checkpoint,leg,completed,10000,9872,default));} var first=Directory.GetFiles(temp.File("external-service-proofs")).Select(path=>JsonDocument.Parse(File.ReadAllText(path))) .Select(doc=>{using(doc)return JsonSerializer.Deserialize(doc.RootElement.GetProperty("entry"),ToolboxSettings.JsonOptions)!;}) .Single(entry=>entry.Kind==TradeCashKind.Fuel); using(var partial=new TradeProfitLedger(temp.File("partial-ledger"))){partial.RecordTrade(checkpoint.Bought!);partial.Record(first);} using var restored=new TradeProfitLedger(temp.File("partial-ledger")); Check.That(await ExternalServiceCosts.ReconcileAsync(temp.Path,temp.Path,checkpoint,leg,restored,10000,9872,default)); Check.Equal(23m,restored.Snapshot.Fuel);Check.Equal(65m,restored.Snapshot.Repairs);Check.Equal(88m,restored.Snapshot.Expenses); } }