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

XFEEDToolBox

【WinUI】XFE精英危险工具箱

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

namespace XFEEDToolBox.Test;

[TestFixture]
public class ConfirmedTradeReceiptTests
{
    private sealed class Fixture:IConfirmedTradeCommandDriver
    {
        public PlayerState Player=new(){InGame=true,Docked=true,MarketId=1,CommanderId="receipt-test",CurrentShipId=7,Ship="Type9",
            LoadGameAt=DateTimeOffset.UtcNow,Credits=10000,CreditsReliable=true,CargoReliable=true,CargoCapacity=10};
        public readonly TradeRunner Runner;
        public readonly List<long> Serviced=[];
        public readonly HashSet<string> JournalNames=[];
        public readonly ConcurrentQueue<string> Messages=new();
        public Func<ConfirmedTradeReceipt,ConfirmedTradeReceipt>? ChangeReceipt;
        public int Trades,Travel;public bool Released;public bool RepeatKey;
        private readonly string firstKey=Guid.NewGuid().ToString("N");
        public Fixture()
        {Runner=new(()=>Player,(id,key)=>Check.Quote(id,key,id==1?100:200,id==1?90:180));Runner.Progress+=progress=>Messages.Enqueue(progress.Message);}
        public static RoutePlan Plan=>new(){Request=Check.Request with{MaxLegs=1},Legs=[new(){Number=1,From=Check.Station(1),To=Check.Station(2),Orders=[new("gold","Gold",3,100,180)]}]};
        public Task PreflightAsync(CancellationToken token)=>Task.CompletedTask;
        public async Task ServiceDockedAsync(DockedServiceRequest request,Func<CancellationToken,Task> beforeInput,CancellationToken token)
        {
            await Task.Delay(30,token);await beforeInput(token);
            Player=Player with{Credits=Player.Credits-11};Serviced.Add(request.MarketId);
        }
        public async Task<ConfirmedTradeReceipt> ExecuteTradeAsync(TradeAction action,Func<CancellationToken,Task> beforeInput,CancellationToken token)
        {
            await beforeInput(token);Trades++;var before=Player;var buy=action.Kind==TradeActionKind.Buy;
            var delta=buy?3:-3;var price=buy?100:180;
            Player=Player with{Credits=Player.Credits-price*delta,CargoCount=before.CargoCount+delta,Inventory=buy?[new("gold","Gold",3)]:[]};
            var receipt=new ConfirmedTradeReceipt(RepeatKey||buy?firstKey:Guid.NewGuid().ToString("N"),Player.MarketId!.Value,"gold",delta,price,
                (long)before.Credits!.Value,(long)Player.Credits!.Value,before.CargoCount,Player.CargoCount,Player.CommanderId,7,Player.LoadGameAt!.Value,DateTimeOffset.UtcNow,true,true);
            return ChangeReceipt?.Invoke(receipt)??receipt;
        }
        public async Task ExecuteAsync(TradeAction action,Func<CancellationToken,Task> beforeInput,CancellationToken token)
        {
            if(action.Kind!=TradeActionKind.Travel)throw new InvalidOperationException("Receipted orders must not execute twice.");
            await beforeInput(token);Check.That(Serviced.Contains(1));Travel++;Player=Player with{MarketId=2};JournalNames.Add("Docked");
            Runner.Observe(new(1,DateTimeOffset.UtcNow,Check.Event(new{@event="Docked",MarketID=2})));
        }
        public Task ReleaseAllAsync(){Released=true;return Task.CompletedTask;}
    }
    [Test] public async Task ServerReceiptsCompleteWithBothMaintenanceTasksAndOnlyAnActualDockingEvent()
    {
        var fixture=new Fixture();decimal profit=0;fixture.Runner.Progress+=p=>profit=p.ActualProfit;
        await fixture.Runner.RunAsync(Fixture.Plan,ExecutionMode.NativeBridge,fixture).WaitAsync(TimeSpan.FromSeconds(2));
        Check.Equal(RunState.Completed,fixture.Runner.State);Check.Equal(2,fixture.Trades);Check.Equal(1,fixture.Travel);
        Check.Equal(2,fixture.Serviced.Count);Check.Equal(240m,profit);Check.Equal(10218m,fixture.Player.Credits);
        Check.Equal(1,fixture.JournalNames.Count);Check.That(fixture.JournalNames.Contains("Docked"));Check.That(fixture.Released);
    }
    [Test] public async Task WrongServerOrderOrSessionStopsBeforeDeparture()
    {
        foreach(var change in new Func<ConfirmedTradeReceipt,ConfirmedTradeReceipt>[]
        {
            r=>r with{MarketId=99},r=>r with{QuantityDelta=1},r=>r with{ServerConfirmed=false},r=>r with{ClientApplied=false},
            r=>r with{CreditsAfter=r.CreditsAfter+1},r=>r with{CargoAfter=9},r=>r with{ShipId=8},r=>r with{SessionStartedAt=r.SessionStartedAt.AddSeconds(-1)},
            r=>r with{ConfirmedAt=r.ConfirmedAt.AddMinutes(-5)},r=>r with{OperationKey=""}
        })
        {
            var fixture=new Fixture{ChangeReceipt=change};
            await fixture.Runner.RunAsync(Fixture.Plan,ExecutionMode.NativeBridge,fixture).WaitAsync(TimeSpan.FromSeconds(2));
            Check.Equal(RunState.Failed,fixture.Runner.State);Check.Equal(1,fixture.Trades);Check.Equal(0,fixture.Travel);Check.That(fixture.Released);
        }
    }
    [Test] public async Task ReusedNativeOperationKeyCannotCountASecondTrade()
    {
        var fixture=new Fixture{RepeatKey=true};
        await fixture.Runner.RunAsync(Fixture.Plan,ExecutionMode.NativeBridge,fixture).WaitAsync(TimeSpan.FromSeconds(2));
        Check.Equal(RunState.Failed,fixture.Runner.State);Check.Equal(2,fixture.Trades);Check.Equal(1,fixture.Travel);Check.That(fixture.Released);
    }
    [Test] public async Task UnknownActualShipStopsBeforeAnyServiceTradeOrDeparture()
    {
        var fixture=new Fixture();fixture.Player=fixture.Player with{Ship=""};
        await fixture.Runner.RunAsync(Fixture.Plan,ExecutionMode.NativeBridge,fixture).WaitAsync(TimeSpan.FromSeconds(2));
        Check.Equal(RunState.Failed,fixture.Runner.State);Check.Equal(0,fixture.Trades);Check.Equal(0,fixture.Travel);Check.Equal(0,fixture.Serviced.Count);
        Check.That(fixture.Released);Check.That(fixture.Messages.Any(message=>message.Contains("实际船型尺寸",StringComparison.Ordinal)));
    }
    [Test] public async Task TooSmallDestinationPadStopsBeforeAnyServiceTradeOrDeparture()
    {
        var fixture=new Fixture();var plan=Fixture.Plan;var leg=plan.Legs[0];
        plan=plan with{Legs=[leg with{To=leg.To with{MaxLandingPad=LandingPad.Medium}}]};
        await fixture.Runner.RunAsync(plan,ExecutionMode.NativeBridge,fixture).WaitAsync(TimeSpan.FromSeconds(2));
        Check.Equal(RunState.Failed,fixture.Runner.State);Check.Equal(0,fixture.Trades);Check.Equal(0,fixture.Travel);Check.Equal(0,fixture.Serviced.Count);
        Check.That(fixture.Released);Check.That(fixture.Messages.Any(message=>message.Contains("停机坪",StringComparison.Ordinal)));
    }
}