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

XFEEDToolBox

【WinUI】XFE精英危险工具箱

公开
关注 0 Fork 0 Star 0
UTF-8
using System.Text.Json;
using XFEEDToolBox.Core.Execution;
using XFEEDToolBox.Core.Models;
using XFEExtension.NetCore.XUnit.Attributes;

namespace XFEEDToolBox.Test;

[TestFixture]
public class AutomaticTradeRoundsTests
{
    private static readonly DateTimeOffset Start=DateTimeOffset.UtcNow.AddHours(-2);
    private static Station Station(long id)=>new(){MarketId=id,Name="Station "+id,SystemName="System "+id};
    private static PlayerState Player=>new(){CommanderId="round-test",CurrentShipId=7,InGame=true,Docked=true,MarketId=2,LocationAt=Start.AddMinutes(5)};
    private static AutomaticTradeConfiguration Configuration(string root,bool returnTrade=true)
        =>new(){Source=Station(1),Destination=Station(2),WorkingDirectory=root,ReturnTrade=returnTrade};
    private static string Journey(string root,string name,long from,long to,int minute,bool passed=true,bool sold=true)
    {
        var directory=Path.Combine(root,"journey-"+name);Directory.CreateDirectory(directory);
        var begin=Start.AddMinutes(minute);
        var buy=new ConfirmedTradeReceipt(Guid.NewGuid().ToString("N"),from,"gold",2,10,1000,980,0,2,"round-test",7,Start.AddMinutes(-1),begin.AddSeconds(1),true,true);
        var sale=sold?new ConfirmedTradeReceipt(Guid.NewGuid().ToString("N"),to,"gold",-2,20,980,1020,2,0,"round-test",7,Start.AddMinutes(-1),begin.AddMinutes(1),true,true):null;
        var checkpoint=new TradeJourneyCheckpoint("round-test",7,begin,buy,sale);
        TradeJourneyCheckpoints.Save(Path.Combine(directory,"journey.json"),checkpoint);
        var leg=new TradeLeg{From=Station(from),To=Station(to),Orders=[new("gold","Gold",2,10,20)]};
        File.WriteAllText(Path.Combine(directory,"leg.json"),JsonSerializer.Serialize(leg,ToolboxSettings.JsonOptions));
        if(sold)File.WriteAllText(Path.Combine(directory,"result.json"),JsonSerializer.Serialize(
            new NativeTradeJourneyResult(passed,checkpoint,TradeProfitSnapshot.Empty,1000,1020,passed?null:"services incomplete",begin.AddMinutes(1).AddSeconds(1)),ToolboxSettings.JsonOptions));
        return directory;
    }

    [Test]public void OutboundSaleAloneCompletesANonReturnRoutePass()
    {
        using var temporary=new TemporaryDirectory();Journey(temporary.Path,"outbound",1,2,0);
        var config=Configuration(temporary.Path,returnTrade:false);
        Check.Equal(1,AutomaticTradeRounds.Refresh(config,Player));
        Check.Equal(1,AutomaticTradeRounds.Refresh(config,Player with{Docked=false,MarketId=null}));
    }
    [Test]public void ReturnRouteNeedsCompletedHomeArrivalAndSurvivesRepeatedRecovery()
    {
        using var temporary=new TemporaryDirectory();var config=Configuration(temporary.Path);
        Journey(temporary.Path,"outbound",1,2,0);
        Check.Equal(0,AutomaticTradeRounds.Refresh(config,Player));
        Journey(temporary.Path,"return-pending",2,1,3,sold:false);
        Check.Equal(0,AutomaticTradeRounds.Refresh(config,Player with{Docked=false,MarketId=null}));
        Journey(temporary.Path,"return-completed",2,1,4);
        Check.Equal(1,AutomaticTradeRounds.Refresh(config,Player with{Docked=false,MarketId=null}));
        Check.Equal(1,AutomaticTradeRounds.Refresh(config,Player));
        Check.Equal(1,Directory.EnumerateFiles(Path.Combine(temporary.Path,"rounds-v1"),"*.json",SearchOption.AllDirectories).Count());
    }
    [Test]public void ConfirmedEmptyReturnCountsOnceAndNewOutboundStartsTheNextRound()
    {
        using var temporary=new TemporaryDirectory();var config=Configuration(temporary.Path);
        Journey(temporary.Path,"outbound",1,2,0);
        var home=Player with{MarketId=1,LocationAt=Start.AddMinutes(3)};
        Check.Equal(1,AutomaticTradeRounds.Refresh(config,home));
        Journey(temporary.Path,"next-outbound",1,2,4,sold:false);
        Check.Equal(1,AutomaticTradeRounds.Refresh(config,Player with{Docked=false,MarketId=null}));
        Journey(temporary.Path,"second-outbound",1,2,5);
        Check.Equal(1,AutomaticTradeRounds.Refresh(config,Player));
        Check.Equal(2,AutomaticTradeRounds.Refresh(config,home with{LocationAt=Start.AddMinutes(8)}));
        Check.Equal(2,AutomaticTradeRounds.Refresh(config,home with{LocationAt=Start.AddMinutes(8)}));
    }
    [Test]public void SourcePurchaseProvesTheReturnIfTheWorkerRestartedBeforeSavingItsRound()
    {
        using var temporary=new TemporaryDirectory();var config=Configuration(temporary.Path);
        Journey(temporary.Path,"outbound",1,2,0);Journey(temporary.Path,"next-purchase",1,2,4,sold:false);
        Check.Equal(1,AutomaticTradeRounds.Refresh(config,Player with{Docked=false,MarketId=null}));
    }
    [Test]public void InitialBackwardFailedForeignAndDifferentRouteJourneysDoNotCreateRounds()
    {
        using var temporary=new TemporaryDirectory();var config=Configuration(temporary.Path);
        Journey(temporary.Path,"initial-return",2,1,0);Journey(temporary.Path,"failed-outbound",1,2,4,passed:false);
        var home=Player with{MarketId=1,LocationAt=Start.AddMinutes(8)};
        Check.Equal(0,AutomaticTradeRounds.Refresh(config,home));
        Journey(temporary.Path,"outbound",1,2,10);
        Check.Equal(0,AutomaticTradeRounds.Refresh(config,home)); // Old source docking cannot attest a later return.
        Check.Equal(0,AutomaticTradeRounds.Refresh(config,home with{CommanderId="other",LocationAt=Start.AddMinutes(15)}));
        Check.Equal(0,AutomaticTradeRounds.Refresh(config,home with{CurrentShipId=8,LocationAt=Start.AddMinutes(15)}));
        Check.Equal(0,AutomaticTradeRounds.Refresh(config with{Destination=Station(3)},home with{LocationAt=Start.AddMinutes(15)}));
    }
    [Test]public void DuplicateJourneyCopiesDoNotDuplicateTheOriginalSaleRound()
    {
        using var temporary=new TemporaryDirectory();var original=Journey(temporary.Path,"outbound",1,2,0);
        var copied=Path.Combine(temporary.Path,"journey-copy");Directory.CreateDirectory(copied);
        foreach(var file in Directory.EnumerateFiles(original))File.Copy(file,Path.Combine(copied,Path.GetFileName(file)));
        Check.Equal(1,AutomaticTradeRounds.Refresh(Configuration(temporary.Path,false),Player));
    }
    [Test]public void OneFutureHomeObservationCannotCompleteOverlappingDistinctOutboundClaims()
    {
        using var temporary=new TemporaryDirectory();Journey(temporary.Path,"first",1,2,0);Journey(temporary.Path,"overlapping",1,2,0);
        var home=Player with{MarketId=1,LocationAt=Start.AddMinutes(5)};
        Check.Equal(0,AutomaticTradeRounds.Refresh(Configuration(temporary.Path),home));
        Check.Equal(0,AutomaticTradeRounds.Refresh(Configuration(temporary.Path,false),home));
    }
    [Test]public void AChangedReceiptInTheResultCannotCountAsTheValidatedCheckpoint()
    {
        using var temporary=new TemporaryDirectory();var directory=Journey(temporary.Path,"outbound",1,2,0);
        var resultPath=Path.Combine(directory,"result.json");
        var result=JsonSerializer.Deserialize<NativeTradeJourneyResult>(File.ReadAllText(resultPath),ToolboxSettings.JsonOptions)!;
        result=result with{Checkpoint=result.Checkpoint with{Sold=result.Checkpoint.Sold! with{OperationKey=Guid.NewGuid().ToString("N")}}};
        File.WriteAllText(resultPath,JsonSerializer.Serialize(result,ToolboxSettings.JsonOptions));
        Check.Equal(0,AutomaticTradeRounds.Refresh(Configuration(temporary.Path,false),Player));
    }
    private static void GoalProof(string directory,ulong goal=864)
        =>File.WriteAllText(Path.Combine(directory,"membership-before.json"),JsonSerializer.Serialize(new{goalId=goal,contribution=0}));
    [Test]public void DynamicCgSourceChangesAndRestartPreserveAllConfirmedRounds()
    {
        using var temporary=new TemporaryDirectory();
        var config=Configuration(temporary.Path,false) with{Mode=AutomaticTradeMode.CommunityGoal,CommunityGoalId=864,Commodities=["gold"]};
        GoalProof(Journey(temporary.Path,"first",1,2,0));
        Check.Equal(1,AutomaticTradeRounds.Refresh(config,Player));
        GoalProof(Journey(temporary.Path,"different-source",3,2,4));
        var changed=config with{Source=Station(3)};
        Check.Equal(2,AutomaticTradeRounds.Refresh(changed,Player));Check.Equal(2,AutomaticTradeRounds.Refresh(config,Player));
        Check.Equal(0,AutomaticTradeRounds.Refresh(config with{Commodities=["uranium"]},Player));
        GoalProof(Journey(temporary.Path,"other-goal",4,2,8),865);
        Journey(temporary.Path,"unverified-goal",5,2,12);
        Check.Equal(2,AutomaticTradeRounds.Refresh(changed,Player));
        Check.Equal(2,Directory.EnumerateFiles(Path.Combine(temporary.Path,"rounds-v1"),"*.json",SearchOption.AllDirectories).Count());
    }
    [Test]public void DynamicReturnRouteRequiresTheActualSourceOfEachBatch()
    {
        using var temporary=new TemporaryDirectory();
        var config=Configuration(temporary.Path) with{Mode=AutomaticTradeMode.CommunityGoal,CommunityGoalId=864,Commodities=["gold"]};
        GoalProof(Journey(temporary.Path,"from-three",3,2,0));
        Journey(temporary.Path,"buy-at-other-source",1,2,4,sold:false);
        Check.Equal(0,AutomaticTradeRounds.Refresh(config,Player with{MarketId=1,LocationAt=Start.AddMinutes(5)}));
        Check.Equal(1,AutomaticTradeRounds.Refresh(config,Player with{MarketId=3,LocationAt=Start.AddMinutes(5)}));
        Check.Equal(1,AutomaticTradeRounds.Refresh(config with{Source=Station(4)},Player));
    }
}