using System.Text.Json; using XFEEDToolBox.Core.Execution; using XFEExtension.NetCore.XUnit.Attributes; namespace XFEEDToolBox.Test; [TestFixture] public class TradeJourneyCheckpointTests { [Test] public void IncompleteJourneyDisplaysItsActualCauseAndPreservesTheResult() { var cause=new InvalidOperationException("Original health is unavailable"); var result=new NativeTradeJourneyResult(false,Purchased(),TradeProfitSnapshot.Empty,10000,null,cause.ToString(),DateTimeOffset.UtcNow); var error=new NativeTradeJourneyIncompleteException(result,cause); Check.That(error.Message.Contains(cause.Message,StringComparison.Ordinal));Check.That(ReferenceEquals(result,error.Result));Check.That(ReferenceEquals(cause,error.InnerException)); } private static TradeJourneyCheckpoint Purchased() { var started=DateTimeOffset.UtcNow.AddHours(-2); var receipt=new ConfirmedTradeReceipt(Guid.NewGuid().ToString("N"),1,"biowaste",1,40,10000,9960,0,1,"resume-test",5,started.AddMinutes(-1),started.AddSeconds(5),true,true); return new("resume-test",5,started,receipt); } [Test] public void ExistingCamelCaseCheckpointRestoresTheOriginalPurchaseAfterRestart() { using var temp=new TemporaryDirectory();var expected=Purchased();var path=temp.File("journey.json"); // This is the format already written by the live acceptance harness. File.WriteAllText(path,JsonSerializer.Serialize(expected,new JsonSerializerOptions(JsonSerializerDefaults.Web))); var actual=TradeJourneyCheckpoints.Load(path); Check.Equal(expected,actual);Check.Equal(expected.Bought!.OperationKey,actual.Bought!.OperationKey); Check.Equal(expected.Bought.SessionStartedAt,actual.Bought.SessionStartedAt); } [Test] public void AtomicReplacementRetainsTheWholePurchaseAndSaleAcrossDifferentGameSessions() { using var temp=new TemporaryDirectory();var path=temp.File("journey.json");var bought=Purchased(); TradeJourneyCheckpoints.Save(path,bought); var sale=new ConfirmedTradeReceipt(Guid.NewGuid().ToString("N"),2,"biowaste",-1,100,9960,10060,1,0,"resume-test",5,DateTimeOffset.UtcNow.AddMinutes(-1),DateTimeOffset.UtcNow,true,true); var sold=bought with{Sold=sale};TradeJourneyCheckpoints.Save(path,sold); Check.Equal(sold,TradeJourneyCheckpoints.Load(path));Check.Equal(0,Directory.GetFiles(temp.Path,"*.tmp").Length); } [Test] public void RestoringAnOldReceiptDoesNotCountOrBuyTheCargoAgain() { using var temp=new TemporaryDirectory();var path=temp.File("journey.json");var ledgerPath=temp.File("ledger");var value=Purchased(); TradeJourneyCheckpoints.Save(path,value); using(var ledger=new TradeProfitLedger(ledgerPath))Check.That(ledger.RecordTrade(value.Bought!)); var restored=TradeJourneyCheckpoints.Load(path); using(var ledger=new TradeProfitLedger(ledgerPath)) { Check.That(!ledger.RecordTrade(restored.Bought!));Check.Equal(40m,ledger.Snapshot.Purchases); Check.Equal(40m,ledger.Snapshot.CargoInvestment);Check.Equal(-40m,ledger.Snapshot.CashChange);Check.Equal(0m,ledger.Snapshot.NetProfit); } } [Test] public void AnInconsistentReceiptCannotReplaceThePreviousCheckpoint() { using var temp=new TemporaryDirectory();var path=temp.File("journey.json");var expected=Purchased();TradeJourneyCheckpoints.Save(path,expected); var before=File.ReadAllBytes(path);var rejected=false; try{TradeJourneyCheckpoints.Save(path,expected with{Bought=expected.Bought! with{CreditsAfter=1}});}catch(InvalidDataException){rejected=true;} Check.That(rejected);Check.That(before.SequenceEqual(File.ReadAllBytes(path)));Check.Equal(expected,TradeJourneyCheckpoints.Load(path)); } }