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

XFEEDToolBox

【WinUI】XFE精英危险工具箱

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

[TestFixture]
public class GameEventLogTests
{
    [Test] public void AllHistoryIsStoredDeduplicatedAndPagedInEventTimeOrder()
    {
        using var temp=new TemporaryDirectory();var repository=new GameEventRepository(temp.File("events.db"));repository.Initialize();
        var start=DateTimeOffset.Parse("2026-10-22T00:00:00Z");
        var rows=Enumerable.Range(0,451).Select(i=>new GameJournalRecord("Journal.log",i,start.AddSeconds(i),"FSDJump",$"{{\"event\":\"FSDJump\",\"StarSystem\":\"System {i}\"}}")).ToArray();
        Check.Equal(451,repository.Insert(rows));Check.Equal(0,repository.Insert(rows));
        var all=new List<GameEventEntry>();GameEventCursor? cursor=null;
        do{var page=repository.Query(before:cursor);Check.Equal(451L,page.Count);all.AddRange(page.Entries);cursor=page.Entries.Last().Cursor;if(!page.HasMore)break;}while(true);
        Check.Equal(451,all.Count);Check.Equal(451,all.Select(r=>r.Id).Distinct().Count());Check.That(all.Zip(all.Skip(1)).All(pair=>pair.First.Time>=pair.Second.Time));
        Check.Equal(1,repository.Query("System 450").Entries.Count);
        Check.Equal(451L,repository.Query("跃迁").Count);
        Check.Equal(0,repository.Query("%_'").Entries.Count);
        var older=new GameJournalRecord("Status.json",9,start.AddMinutes(-5),"Status","{\"event\":\"Status\",\"Flags\":8}");repository.Insert([older]);Check.Equal("FSDJump",repository.Query(limit:1).Entries[0].Event);
    }
    [Test] public void EventTypeAndSearchApplyAcrossHistoryAndBothOrdersPageWithoutLosingTiedEvents()
    {
        using var temp=new TemporaryDirectory();var repository=new GameEventRepository(temp.File("events.db"));repository.Initialize();
        var start=DateTimeOffset.Parse("2026-10-22T00:00:00Z");
        var rows=Enumerable.Range(0,451).Select(i=>
        {
            var type=i%3==0?"FSDJump":i%3==1?"Docked":"UnknownFutureEvent";
            return new GameJournalRecord("Journal.log",i,start.AddSeconds(i/4),type,$"{{\"event\":\"{type}\",\"StarSystem\":\"HR 3230\",\"Count\":{i}}}");
        }).ToArray();repository.Insert(rows);
        Check.Equal("Docked,FSDJump,UnknownFutureEvent",string.Join(',',repository.EventTypes()));
        var descending=new List<GameEventEntry>();var ascending=new List<GameEventEntry>();
        foreach(var oldestFirst in new[]{false,true})
        {
            var collected=oldestFirst?ascending:descending;GameEventCursor? cursor=null;
            do
            {
                var page=repository.Query("HR 3230",cursor,17,"FSDJump",oldestFirst);
                Check.Equal(151L,page.Count);Check.That(page.Entries.All(e=>e.Event=="FSDJump"));
                collected.AddRange(page.Entries);cursor=page.Entries.Last().Cursor;
                if(!page.HasMore)break;
            }while(true);
            Check.Equal(151,collected.Count);Check.Equal(151,collected.Select(e=>e.Id).Distinct().Count());
        }
        Check.That(descending.Select(e=>e.Id).SequenceEqual(ascending.Select(e=>e.Id).Reverse()));
        Check.Equal(151L,repository.Query("跃迁",eventName:"FSDJump").Count);
        Check.Equal(0L,repository.Query("跃迁",eventName:"Docked").Count);
        Check.Equal(150L,repository.Query(eventName:"UnknownFutureEvent").Count);
        Check.Equal(0L,repository.Query(eventName:"FSDJump' OR 1=1 --").Count);
    }
    [Test] public async Task MonitorIndexesReplayUnknownEventsAndLiveSnapshotsWithoutReplayingActions()
    {
        using var temp=new TemporaryDirectory();var path=temp.File("Journal.2026-10-22.log");
        await File.WriteAllTextAsync(path,"{\"timestamp\":\"2026-10-22T00:00:00Z\",\"event\":\"NewUnknownEvent\",\"Count\":2}\n");
        var stored=new List<GameJournalRecord>();var live=new List<ObservedJournalEvent>();
        await using var monitor=new JournalMonitor(temp.Path);monitor.EventBatch+=batch=>{lock(stored)stored.AddRange(batch);};monitor.JournalEvent+=e=>{lock(live)live.Add(e);};
        await monitor.StartAsync();Check.Equal(1,stored.Count);Check.Equal(0,live.Count);
        await File.WriteAllTextAsync(temp.File("Status.json"),"{\"timestamp\":\"2026-10-22T00:00:01Z\",\"event\":\"Status\",\"Flags\":16777224}");
        await Task.Delay(1000);Check.That(stored.Any(r=>r.Event=="Status"));Check.That(live.Any(r=>r.Data.Text("event")=="Status"));
        Check.Equal(1,stored.Count(r=>r.Event=="NewUnknownEvent"));
    }
    [Test] public async Task UnchangedStatusIsReconfirmedWithoutInventingEventsOrNotifyingTheUi()
    {
        using var temp=new TemporaryDirectory();var eventAt=DateTimeOffset.UtcNow.AddMinutes(-10);
        await File.WriteAllTextAsync(temp.File("Status.json"),System.Text.Json.JsonSerializer.Serialize(new{timestamp=eventAt,eventName="unused",@event="Status",Flags=16777224}));
        var events=0;var changes=0;var live=0;
        await using var monitor=new JournalMonitor(temp.Path);
        monitor.EventBatch+=batch=>Interlocked.Add(ref events,batch.Count);monitor.PlayerChanged+=_=>Interlocked.Increment(ref changes);monitor.JournalEvent+=_=>Interlocked.Increment(ref live);
        await monitor.StartAsync();var firstRead=monitor.State.StatusReadAt;
        Check.That(firstRead is not null);await Task.Delay(1100);
        Check.That(monitor.State.StatusReadAt>firstRead);Check.Equal(eventAt,monitor.State.StatusAt!.Value);
        Check.Equal(1,events);Check.Equal(1,changes);Check.Equal(0,live);
        // A file still present but partially written cannot extend the receipt.
        await File.WriteAllTextAsync(temp.File("Status.json"),"{\"event\":");
        await Task.Delay(2600);
        Check.That(!XFEEDToolBox.Core.Navigation.TravelPreparation.StatusConfirmed(monitor.State,DateTimeOffset.UtcNow));
        Check.Equal(eventAt,monitor.State.StatusAt!.Value);Check.Equal(1,events);
        await File.WriteAllTextAsync(temp.File("Status.json"),System.Text.Json.JsonSerializer.Serialize(new{timestamp=eventAt,@event="Status"}));
        await Task.Delay(850);
        Check.That(!XFEEDToolBox.Core.Navigation.TravelPreparation.StatusConfirmed(monitor.State,DateTimeOffset.UtcNow));
        await File.WriteAllTextAsync(temp.File("Status.json"),System.Text.Json.JsonSerializer.Serialize(new{timestamp=eventAt,eventName="unused",@event="Status",Flags=16777224}));
        await Task.Delay(850);
        Check.That(XFEEDToolBox.Core.Navigation.TravelPreparation.StatusConfirmed(monitor.State,DateTimeOffset.UtcNow));
        Check.Equal(1,events);Check.Equal(1,changes);
    }
}