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<TradeCashEntry>(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);
}
}
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<TradeCashEntry>(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);
}
}