using System.Text.Json;
using XFEExtension.NetCore.XUnit.Attributes;
using XFEEDToolBox.Core.Data;
using XFEEDToolBox.Core.Models;
namespace XFEEDToolBox.Test;
[TestFixture]
public class ObservedFixtureTests
{
[Test] public void ParsesCapturedListAndTradeResponseFields()
{
using var doc = JsonDocument.Parse(File.ReadAllText(Path.Combine(AppContext.BaseDirectory, "Fixtures", "captured-commodities.json")));
var samples = doc.RootElement.EnumerateArray().ToArray(); Check.Equal(2, samples.Length);
foreach (var sample in samples)
{
var time = sample.Time("observedAt")!.Value;
var quotes = sample.Get("commodities").Items().Select(x => MarketParser.ParseQuote(x, 3224123392, time, DataSource.Capture)).ToArray();
Check.Equal("platinum", quotes[0].Commodity); Check.Equal(223785L, quotes[0].SellPrice); Check.Equal(time, quotes[0].CollectedAt);
Check.That(quotes.All(q => q.Source == DataSource.Capture));
}
}
}
[TestFixture, Explicit("Reads user supplied evidence / external public data; opt in manually.")]
public class LiveIntegrationTests
{
private static void Save(string file, object result)
{
var folder = Path.GetFullPath(Path.Combine("artifacts", "integration")); Directory.CreateDirectory(folder);
File.WriteAllText(Path.Combine(folder, file), JsonSerializer.Serialize(result, ToolboxSettings.JsonOptions));
}
[Test] public async Task ImportsActualCaptureArchive()
{
var path = Environment.GetEnvironmentVariable("XFE_ED_CAPTURE") ?? throw new InvalidOperationException("Set XFE_ED_CAPTURE to the provided capture archive.");
using var temp = new TemporaryDirectory(); var repo = temp.Repository();
var count = await CaptureImporter.ImportAsync(path, repo);
var rows = repo.Search("", 3224123392, 0, 1000);
Save("capture-import.json", new { count.Stations, count.Quotes, storedQuotes = rows.Count, onlyHistorical = rows.All(x => x.Quote.Source == DataSource.Capture), importedInventory = false });
Check.Equal(1, count.Stations); Check.Equal(404, rows.Count); Check.That(rows.All(x => x.Quote.Source == DataSource.Capture));
}
[Test] public async Task ReadsActualLocalJournal()
{
await using var monitor = new JournalMonitor(new ToolboxSettings().JournalDirectory);
var quoteCount = 0; monitor.MarketChanged += m => quoteCount = m.Quotes.Count;
await monitor.StartAsync();
var p = monitor.State; var active = p.Ships.FirstOrDefault(x => x.ShipId == p.CurrentShipId);
JsonElement expectedLoadout = default;
foreach (var path in Directory.EnumerateFiles(monitor.DirectoryPath, "Journal.*.log").Order(StringComparer.Ordinal))
{
using var file = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite | FileShare.Delete);
using var reader = new StreamReader(file);
while (await reader.ReadLineAsync() is { } line)
{
try
{
using var doc = JsonDocument.Parse(line);
var e = doc.RootElement;
if (e.Text("event") == "Loadout" && e.Long("ShipID", -1) == p.CurrentShipId && e.Time() <= p.UpdatedAt) expectedLoadout = e.Clone();
}
catch (JsonException) { }
}
}
var expectedModules = expectedLoadout.Get("Modules").EnumerateArray().ToArray();
Check.Equal(expectedModules.Length, active!.Modules.Count);
for (var i = 0; i < expectedModules.Length; i++)
{
var raw = expectedModules[i]; var parsed = active.Modules[i];
Check.Equal(raw.GetProperty("Slot").GetString(), parsed.Slot); Check.Equal(raw.GetProperty("Item").GetString(), parsed.Item);
Check.Equal(raw.GetProperty("On").GetBoolean(), parsed.Enabled); Check.Equal(raw.GetProperty("Priority").GetInt32(), parsed.Priority);
if (raw.TryGetProperty("Engineering", out var engineering))
{
var modifiers = engineering.GetProperty("Modifiers").EnumerateArray().ToArray();
Check.Equal(modifiers.Length, parsed.Engineering!.Modifiers.Count);
for (var m = 0; m < modifiers.Length; m++)
{
Check.Equal(modifiers[m].GetProperty("Label").GetString(), parsed.Engineering.Modifiers[m].Label);
if (modifiers[m].TryGetProperty("Value", out var value) && value.ValueKind == JsonValueKind.Number) Check.Equal(value.GetDouble(), parsed.Engineering.Modifiers[m].Value!.Value);
}
}
}
Save("local-journal.json", new { balanceAvailable = p.Credits != null, p.CreditsReliable, capacityAvailable = p.CargoCapacity > 0, cargoItems = p.Inventory.Count, materialItems = p.Materials.Count,
ships = p.Ships.Count, configuredShips = p.Ships.Count(x => x.ConfigurationAt != null), activeShipType = active?.Type, activeModules = active?.Modules.Count,
engineeredModules = active?.Modules.Count(x => x.Engineering != null), engineeringParameters = active?.Modules.Sum(x => x.Engineering?.Modifiers.Count ?? 0),
storedModules = p.StoredModules.Count, quoteCount, eventsRead = monitor.EventsRead, independentlyComparedModules = expectedModules.Length, checks = ReadAcceptance.Build(p, monitor.LocalMarket), updatedAt = p.UpdatedAt, gameBuild = p.GameBuild });
Check.That(monitor.State.Credits != null); Check.That(quoteCount > 0);
Check.That(active?.Modules.Count > 0, "Current ship Loadout was not read.");
}
[Test] public async Task ReadsActualSpanshSystem()
{
using var temp = new TemporaryDirectory(); var repo = temp.Repository(); using var http = new HttpClient { Timeout = TimeSpan.FromSeconds(25) };
using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(40));
var result = await new SpanshImporter(repo, http).ImportSystemAsync("Vaka", token: timeout.Token);
var origin = repo.GetStation(3224123392);
Save("spansh.json", new { result.Systems, result.Stations, result.Quotes, originCoordinates = origin?.Position, originSecurity = origin?.Security, originDistanceLs = origin?.DistanceToArrivalLs });
Check.That(result.Quotes > 0 && origin?.Position != null && origin.Security != SecurityLevel.Unknown);
}
[Test] public async Task ReceivesActualEddnStream()
{
using var temp = new TemporaryDirectory(); var repo = temp.Repository();
await using var feed = new EddnSubscriber(repo); feed.Start("tcp://eddn.edcd.io:9500");
var until = DateTimeOffset.UtcNow.AddSeconds(45);
while (feed.MarketsAccepted == 0 && DateTimeOffset.UtcNow < until) await Task.Delay(250);
Save("eddn.json", new { feed.MessagesReceived, feed.MarketsAccepted, feed.Status, feed.LastMessageAt, actualStreamValidated = feed.MessagesReceived > 0, commodityValidated = feed.MarketsAccepted > 0, storedQuotes = repo.Statistics().Quotes });
Check.That(feed.MarketsAccepted > 0, "No live commodity snapshot was accepted during the 45 second observation window.");
}
}
using System.Text.Json;
using XFEExtension.NetCore.XUnit.Attributes;
using XFEEDToolBox.Core.Data;
using XFEEDToolBox.Core.Models;
namespace XFEEDToolBox.Test;
[TestFixture]
public class ObservedFixtureTests
{
[Test] public void ParsesCapturedListAndTradeResponseFields()
{
using var doc = JsonDocument.Parse(File.ReadAllText(Path.Combine(AppContext.BaseDirectory, "Fixtures", "captured-commodities.json")));
var samples = doc.RootElement.EnumerateArray().ToArray(); Check.Equal(2, samples.Length);
foreach (var sample in samples)
{
var time = sample.Time("observedAt")!.Value;
var quotes = sample.Get("commodities").Items().Select(x => MarketParser.ParseQuote(x, 3224123392, time, DataSource.Capture)).ToArray();
Check.Equal("platinum", quotes[0].Commodity); Check.Equal(223785L, quotes[0].SellPrice); Check.Equal(time, quotes[0].CollectedAt);
Check.That(quotes.All(q => q.Source == DataSource.Capture));
}
}
}
[TestFixture, Explicit("Reads user supplied evidence / external public data; opt in manually.")]
public class LiveIntegrationTests
{
private static void Save(string file, object result)
{
var folder = Path.GetFullPath(Path.Combine("artifacts", "integration")); Directory.CreateDirectory(folder);
File.WriteAllText(Path.Combine(folder, file), JsonSerializer.Serialize(result, ToolboxSettings.JsonOptions));
}
[Test] public async Task ImportsActualCaptureArchive()
{
var path = Environment.GetEnvironmentVariable("XFE_ED_CAPTURE") ?? throw new InvalidOperationException("Set XFE_ED_CAPTURE to the provided capture archive.");
using var temp = new TemporaryDirectory(); var repo = temp.Repository();
var count = await CaptureImporter.ImportAsync(path, repo);
var rows = repo.Search("", 3224123392, 0, 1000);
Save("capture-import.json", new { count.Stations, count.Quotes, storedQuotes = rows.Count, onlyHistorical = rows.All(x => x.Quote.Source == DataSource.Capture), importedInventory = false });
Check.Equal(1, count.Stations); Check.Equal(404, rows.Count); Check.That(rows.All(x => x.Quote.Source == DataSource.Capture));
}
[Test] public async Task ReadsActualLocalJournal()
{
await using var monitor = new JournalMonitor(new ToolboxSettings().JournalDirectory);
var quoteCount = 0; monitor.MarketChanged += m => quoteCount = m.Quotes.Count;
await monitor.StartAsync();
var p = monitor.State; var active = p.Ships.FirstOrDefault(x => x.ShipId == p.CurrentShipId);
JsonElement expectedLoadout = default;
foreach (var path in Directory.EnumerateFiles(monitor.DirectoryPath, "Journal.*.log").Order(StringComparer.Ordinal))
{
using var file = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite | FileShare.Delete);
using var reader = new StreamReader(file);
while (await reader.ReadLineAsync() is { } line)
{
try
{
using var doc = JsonDocument.Parse(line);
var e = doc.RootElement;
if (e.Text("event") == "Loadout" && e.Long("ShipID", -1) == p.CurrentShipId && e.Time() <= p.UpdatedAt) expectedLoadout = e.Clone();
}
catch (JsonException) { }
}
}
var expectedModules = expectedLoadout.Get("Modules").EnumerateArray().ToArray();
Check.Equal(expectedModules.Length, active!.Modules.Count);
for (var i = 0; i < expectedModules.Length; i++)
{
var raw = expectedModules[i]; var parsed = active.Modules[i];
Check.Equal(raw.GetProperty("Slot").GetString(), parsed.Slot); Check.Equal(raw.GetProperty("Item").GetString(), parsed.Item);
Check.Equal(raw.GetProperty("On").GetBoolean(), parsed.Enabled); Check.Equal(raw.GetProperty("Priority").GetInt32(), parsed.Priority);
if (raw.TryGetProperty("Engineering", out var engineering))
{
var modifiers = engineering.GetProperty("Modifiers").EnumerateArray().ToArray();
Check.Equal(modifiers.Length, parsed.Engineering!.Modifiers.Count);
for (var m = 0; m < modifiers.Length; m++)
{
Check.Equal(modifiers[m].GetProperty("Label").GetString(), parsed.Engineering.Modifiers[m].Label);
if (modifiers[m].TryGetProperty("Value", out var value) && value.ValueKind == JsonValueKind.Number) Check.Equal(value.GetDouble(), parsed.Engineering.Modifiers[m].Value!.Value);
}
}
}
Save("local-journal.json", new { balanceAvailable = p.Credits != null, p.CreditsReliable, capacityAvailable = p.CargoCapacity > 0, cargoItems = p.Inventory.Count, materialItems = p.Materials.Count,
ships = p.Ships.Count, configuredShips = p.Ships.Count(x => x.ConfigurationAt != null), activeShipType = active?.Type, activeModules = active?.Modules.Count,
engineeredModules = active?.Modules.Count(x => x.Engineering != null), engineeringParameters = active?.Modules.Sum(x => x.Engineering?.Modifiers.Count ?? 0),
storedModules = p.StoredModules.Count, quoteCount, eventsRead = monitor.EventsRead, independentlyComparedModules = expectedModules.Length, checks = ReadAcceptance.Build(p, monitor.LocalMarket), updatedAt = p.UpdatedAt, gameBuild = p.GameBuild });
Check.That(monitor.State.Credits != null); Check.That(quoteCount > 0);
Check.That(active?.Modules.Count > 0, "Current ship Loadout was not read.");
}
[Test] public async Task ReadsActualSpanshSystem()
{
using var temp = new TemporaryDirectory(); var repo = temp.Repository(); using var http = new HttpClient { Timeout = TimeSpan.FromSeconds(25) };
using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(40));
var result = await new SpanshImporter(repo, http).ImportSystemAsync("Vaka", token: timeout.Token);
var origin = repo.GetStation(3224123392);
Save("spansh.json", new { result.Systems, result.Stations, result.Quotes, originCoordinates = origin?.Position, originSecurity = origin?.Security, originDistanceLs = origin?.DistanceToArrivalLs });
Check.That(result.Quotes > 0 && origin?.Position != null && origin.Security != SecurityLevel.Unknown);
}
[Test] public async Task ReceivesActualEddnStream()
{
using var temp = new TemporaryDirectory(); var repo = temp.Repository();
await using var feed = new EddnSubscriber(repo); feed.Start("tcp://eddn.edcd.io:9500");
var until = DateTimeOffset.UtcNow.AddSeconds(45);
while (feed.MarketsAccepted == 0 && DateTimeOffset.UtcNow < until) await Task.Delay(250);
Save("eddn.json", new { feed.MessagesReceived, feed.MarketsAccepted, feed.Status, feed.LastMessageAt, actualStreamValidated = feed.MessagesReceived > 0, commodityValidated = feed.MarketsAccepted > 0, storedQuotes = repo.Statistics().Quotes });
Check.That(feed.MarketsAccepted > 0, "No live commodity snapshot was accepted during the 45 second observation window.");
}
}