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

XFEEDToolBox

【WinUI】XFE精英危险工具箱

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

namespace XFEEDToolBox.Test;

[TestFixture]
public class TradeProfitLedgerTests
{
    private static TradeCashEntry Entry(TradeCashKind kind, decimal amount, string commodity = "", int quantity = 0)
        => new(Guid.NewGuid().ToString("N"), kind, amount, 1, DateTimeOffset.UtcNow, commodity, quantity);

    [Test] public void BoughtCargoIsInvestmentUntilSoldAndAllIncurredServicesReduceProfit()
    {
        using var ledger = new TradeProfitLedger();
        ledger.Record(Entry(TradeCashKind.Purchase, 600, "coffee", 3));
        ledger.Record(Entry(TradeCashKind.Fuel, 7));
        ledger.Record(Entry(TradeCashKind.Repair, 11));
        ledger.Record(Entry(TradeCashKind.Rearm, 13));
        Check.Equal(-31m, ledger.Snapshot.NetProfit);
        Check.Equal(-631m, ledger.Snapshot.CashChange);
        Check.Equal(600m, ledger.Snapshot.CargoInvestment);
        ledger.Record(Entry(TradeCashKind.Sale, 500, "coffee", 2));
        Check.Equal(69m, ledger.Snapshot.NetProfit);
        Check.Equal(200m, ledger.Snapshot.CargoInvestment);
        Check.Equal(-131m, ledger.Snapshot.CashChange);
    }

    [Test] public void MultipleCargoLotsUseTheirActualPurchaseCostsWithoutRoundingLoss()
    {
        using var ledger = new TradeProfitLedger();
        ledger.Record(Entry(TradeCashKind.Purchase, 1001, "uranium", 3));
        ledger.Record(Entry(TradeCashKind.Purchase, 400, "uranium", 2));
        for (var i = 0; i < 5; i++) ledger.Record(Entry(TradeCashKind.Sale, 500, "uranium", 1));
        Check.Equal(1401m, ledger.Snapshot.SoldCargoCost);
        Check.Equal(1099m, ledger.Snapshot.NetProfit);
        Check.Equal(0m, ledger.Snapshot.CargoInvestment);
    }

    [Test] public void ReopeningRestoresUnfinishedCargoAndDuplicateReceiptsAreNotCountedAgain()
    {
        var directory = Path.Combine(Path.GetTempPath(), "xfe-trade-profit-" + Guid.NewGuid().ToString("N"));
        var buy = Entry(TradeCashKind.Purchase, 100, "polymers", 2);
        var fuel = Entry(TradeCashKind.Fuel, 5);
        using (var first = new TradeProfitLedger(directory)) { first.Record(buy); first.Record(fuel); }
        // This represents a process ending before its next ledger record was committed.
        File.WriteAllText(Path.Combine(directory, "incomplete.tmp"), "{\"Sequence\":");
        using var restored = new TradeProfitLedger(directory);
        Check.That(!restored.Record(buy)); Check.That(!restored.Record(fuel));
        restored.Record(Entry(TradeCashKind.Sale, 250, "polymers", 2));
        Check.Equal(145m, restored.Snapshot.NetProfit);
        Check.Equal(3, Directory.GetFiles(directory, "*.entry.json").Length);
        Check.Throws<InvalidDataException>(() => restored.Record(fuel with { Amount = 10 }));
        Check.Equal(145m, restored.Snapshot.NetProfit);
    }

    [Test] public async Task ConcurrentTradeAndDockedCostsPublishOneConsistentTotal()
    {
        using var ledger = new TradeProfitLedger();
        ledger.Record(Entry(TradeCashKind.Purchase, 100, "water", 1));
        var fuel = Entry(TradeCashKind.Fuel, 7);
        await Task.WhenAll(Enumerable.Range(0, 20).Select(_ => Task.Run(() => ledger.Record(fuel))).Append(
            Task.Run(() => ledger.Record(Entry(TradeCashKind.Sale, 130, "water", 1)))));
        Check.Equal(23m, ledger.Snapshot.NetProfit);
    }

    [Test] public void UnknownCostAndUnconfirmedNativeReceiptsCannotBecomeProfit()
    {
        using var ledger = new TradeProfitLedger();
        Check.Throws<InvalidDataException>(() => ledger.Record(Entry(TradeCashKind.Sale, 500, "water", 1)));
        var receipt = new ConfirmedTradeReceipt(Guid.NewGuid().ToString("N"), 1, "water", 1, 10,
            1000, 990, 0, 1, "commander", 7, DateTimeOffset.UtcNow.AddMinutes(-1), DateTimeOffset.UtcNow, false, true);
        Check.Throws<InvalidDataException>(() => ledger.RecordTrade(receipt));
        Check.Equal(TradeProfitSnapshot.Empty, ledger.Snapshot);
    }

    [Test] public void OnlyCollectedRewardsAndConfirmedServiceDebitsAreCounted()
    {
        using var ledger = new TradeProfitLedger();
        ledger.Record(new ConfirmedDockedServiceReceipt(Guid.NewGuid().ToString("N"), "integrity", 1,
            1000, 990, DateTimeOffset.UtcNow).ToCashEntry());
        Check.Equal(-10m, ledger.Snapshot.NetProfit);
        ledger.Record(Entry(TradeCashKind.CollectedReward, 5000));
        Check.Equal(4990m, ledger.Snapshot.NetProfit);
        Check.Throws<InvalidDataException>(() => new ConfirmedDockedServiceReceipt(Guid.NewGuid().ToString("N"), "refuel", 1,
            1000, 1001, DateTimeOffset.UtcNow).ToCashEntry());
    }
}