using System.Text.Json; using XFEExtension.NetCore.XUnit.Attributes; using XFEEDToolBox.Core.Data; using XFEEDToolBox.Core.Models; using XFEEDToolBox.Core.Services; namespace XFEEDToolBox.Test; [TestFixture] public class NearbyMarketCacheTests { [Test]public void RadiusAndUnknownPositionsDoNotAdmitTheGalaxy() { var player=new PlayerState{SystemName="Home",MarketId=1,Position=new(0,0,0)}; Check.That(NearbyMarketCachePolicy.Accepts(player,Check.Station(2,500))); Check.That(!NearbyMarketCachePolicy.Accepts(player,Check.Station(2,500.001))); Check.That(!NearbyMarketCachePolicy.Accepts(player,Check.Station(2) with{Position=null})); Check.That(NearbyMarketCachePolicy.Accepts(player,Check.Station(2) with{Position=null},Check.Station(2,149))); Check.That(!NearbyMarketCachePolicy.Accepts(player,Check.Station(2) with{Position=null,SystemName="Moved carrier"},Check.Station(2,1))); Check.That(!NearbyMarketCachePolicy.Accepts(player with{Position=null},Check.Station(2,1))); Check.That(NearbyMarketCachePolicy.Accepts(player with{Position=null},Check.Station(2) with{SystemName="home",Position=null})); Check.That(NearbyMarketCachePolicy.Accepts(player with{Position=null},Check.Station(1) with{Position=null})); Check.That(!NearbyMarketCachePolicy.Accepts(new(),Check.Station(2,0))); Check.That(!NearbyMarketCachePolicy.Accepts(player,Check.Station(2) with{Position=new(double.NaN,0,0)})); } private static JsonElement Eddn(long id,string system,DateTimeOffset time)=>Check.Event(new { // Attribute names on the wire include a dollar sign. header=new{gameversion="4.0"},message=new{timestamp=time,marketId=id,systemName=system,stationName="Station "+id, commodities=new[]{new{name="water",buyPrice=10,sellPrice=9,stock=100,demand=100}}} }); private static JsonElement Message(long id,string system,DateTimeOffset time) { using var document=JsonDocument.Parse(Eddn(id,system,time).GetRawText()); var values=new Dictionary{{"$schemaRef","https://eddn.edcd.io/schemas/commodity/3"}, {"header",document.RootElement.GetProperty("header").Clone()},{"message",document.RootElement.GetProperty("message").Clone()}}; return JsonSerializer.SerializeToElement(values); } [Test]public async Task RealFeedPathKeepsOnlyCurrentOrLocatedNearbyDataAndAlwaysKeepsJournal() { using var temporary=new TemporaryDirectory();var repository=new MarketRepository(temporary.File("markets.db"));repository.InitializeCheap(); await repository.SaveStationAsync(Check.Station(2,149),localObservation:true); await repository.SaveStationAsync(Check.Station(3,501),localObservation:true); var player=new PlayerState{SystemName="Home",Position=new(0,0,0),MarketId=1}; repository.PublicDataFilter=station=>NearbyMarketCachePolicy.Accepts(player,station,repository.GetStation(station.MarketId)); await using var feed=new EddnSubscriber(repository);var at=DateTimeOffset.UtcNow.AddMinutes(-1); Check.That(await feed.ProcessMessageAsync(Message(2,"System 2",at))); Check.That(!await feed.ProcessMessageAsync(Message(3,"System 3",at))); Check.That(!await feed.ProcessMessageAsync(Message(4,"Unknown position",at))); Check.That(await feed.ProcessMessageAsync(Message(5,"Home",at))); await feed.ProcessMessageAsync(JsonSerializer.SerializeToElement(new Dictionary {{"$schemaRef","https://eddn.edcd.io/schemas/journal/1"},{"header",new{gameversion="4.0"}}, {"message",new{StarSystem="New nearby system",StarPos=new[]{100,0,0}}}})); Check.That(await feed.ProcessMessageAsync(Message(6,"New nearby system",at))); Check.That(await repository.SaveSnapshotAsync(new(Check.Station(9,5000),[Check.Quote(9,"food",20,19)],at,DataSource.Journal))); Check.Equal(4L,repository.Statistics().Quotes);Check.Equal(3L,feed.MarketsAccepted); Check.That(repository.GetStation(4) is null);Check.That(repository.GetStation(9) is not null); player=player with{Position=null,SystemName="Home"}; Check.That(!await feed.ProcessMessageAsync(Message(2,"System 2",at.AddSeconds(1)))); Check.That(await feed.ProcessMessageAsync(Message(5,"Home",at.AddSeconds(1)))); } [Test]public async Task NormalRuntimeLeavesAnInvalidLegacyReceiptForExplicitMaintenanceWithoutScanning() { using var temporary=new TemporaryDirectory();var seed=temporary.Repository(); await seed.SaveSnapshotAsync(new(Check.Station(1),[Check.Quote(1,"water",10,9)],DateTimeOffset.UtcNow,DataSource.Journal)); var proof=seed.DatabasePath+".verified-generation.json";File.WriteAllText(proof,"{\"old\":true}"); var diagnostic=seed.DatabasePath+".diagnostics.jsonl";var before=File.Exists(diagnostic)?File.ReadAllText(diagnostic):""; var backupTime=File.GetLastWriteTimeUtc(seed.VerifiedBackupPath); await using(var runtime=new ToolboxRuntime(temporary.Path)) { await runtime.InitializeAsync(offline:true); await runtime.Markets.SaveSnapshotAsync(new(Check.Station(1),[Check.Quote(1,"water",11,10)],DateTimeOffset.UtcNow,DataSource.Journal)); var additional=File.ReadAllText(diagnostic)[before.Length..]; Check.That(!additional.Contains("full-integrity"));Check.That(!additional.Contains("verified-generation-rebuilt")); Check.Equal("{\"old\":true}",File.ReadAllText(proof)); Check.Equal(backupTime,File.GetLastWriteTimeUtc(seed.VerifiedBackupPath)); runtime.ClearMarketCacheBlockReason=()=>"请先停止当前任务。"; var refusal=await runtime.ClearMarketCacheAsync();Check.That(!refusal.Succeeded);Check.Equal("请先停止当前任务。",refusal.Message); Check.Equal(1L,runtime.Markets.Statistics().Quotes); } Check.Equal("{\"old\":true}",File.ReadAllText(proof)); } }