using System;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using PolyTrader.Web.Configuration;
using PolyTraderSharp.Services;
using Xunit;
namespace PolyTrader.Tests.WebUi
{
///
/// Log-Viewer für vergangene Tage über das WebUI (#564).
///
public sealed class LogViewerEndpointTests : IDisposable
{
private readonly string _dir = Path.Combine(Path.GetTempPath(), "polytrader-logs-" + Guid.NewGuid().ToString("N"));
public LogViewerEndpointTests() => Directory.CreateDirectory(_dir);
public void Dispose()
{
try { Directory.Delete(_dir, recursive: true); } catch { }
}
private void WriteDay(string date, params (string Msg, LogLevel Level, string Cid)[] lines) =>
WriteRaw(date, lines.Select(l => LogJson.Format(new LogMessageEventArgs(l.Msg, l.Level, l.Cid))).ToArray());
private void WriteRaw(string date, params string[] lines) =>
File.WriteAllLines(Path.Combine(_dir, date + ".jsonl"), lines, new UTF8Encoding(false));
private Task StartAsync() =>
WebUiTestHost.StartAsync(s => s.AddSingleton(new LogDirectory(_dir)));
[Fact]
public async Task Tagesliste_zeigt_nur_Tagesdateien_neueste_zuerst()
{
WriteDay("2026-09-12", ("a", LogLevel.Info, ""));
WriteDay("2026-09-13", ("b", LogLevel.Info, ""));
File.WriteAllText(Path.Combine(_dir, "2026-09-13-Info.log"), "[12:00:00] b");
File.WriteAllText(Path.Combine(_dir, "notizen.jsonl"), "{}");
await using var web = await StartAsync();
var files = await web.GetJsonAsync("/api/logs/files");
Assert.Equal(new[] { "2026-09-13", "2026-09-12" },
files.EnumerateArray().Select(f => f.GetProperty("date").GetString()).ToArray());
}
[Fact]
public async Task Filter_nach_Level_Korrelations_ID_und_Text()
{
WriteDay("2026-09-12",
("Start", LogLevel.Info, ""),
("Signal abgelehnt", LogLevel.Warning, "sig-42"),
("VPN weg", LogLevel.Error, ""));
await using var web = await StartAsync();
var all = await web.GetJsonAsync("/api/logs/file?date=2026-09-12");
var warnings = await web.GetJsonAsync("/api/logs/file?date=2026-09-12&level=warning");
var byCid = await web.GetJsonAsync("/api/logs/file?date=2026-09-12&cid=SIG-42");
var byText = await web.GetJsonAsync("/api/logs/file?date=2026-09-12&q=vpn");
Assert.Equal(3, all.GetProperty("total").GetInt32());
Assert.Equal("Signal abgelehnt", Assert.Single(warnings.GetProperty("items").EnumerateArray()).GetProperty("msg").GetString());
Assert.Equal("sig-42", Assert.Single(byCid.GetProperty("items").EnumerateArray()).GetProperty("cid").GetString());
Assert.Equal("Error", Assert.Single(byText.GetProperty("items").EnumerateArray()).GetProperty("level").GetString());
}
[Fact]
public async Task Kaputte_Zeilen_werden_uebersprungen_und_gezaehlt()
{
WriteRaw("2026-09-12",
LogJson.Format(new LogMessageEventArgs("gut", LogLevel.Info)),
"{kaputt",
"",
LogJson.Format(new LogMessageEventArgs("auch gut", LogLevel.Info)));
await using var web = await StartAsync();
var result = await web.GetJsonAsync("/api/logs/file?date=2026-09-12");
Assert.Equal(2, result.GetProperty("total").GetInt32());
Assert.Equal(1, result.GetProperty("unreadableLines").GetInt32()); // Leerzeilen zaehlen nicht
}
[Fact]
public async Task Seitenaufteilung()
{
WriteDay("2026-09-12", Enumerable.Range(1, 120).Select(i => ($"Zeile {i}", LogLevel.Info, "")).ToArray());
await using var web = await StartAsync();
var last = await web.GetJsonAsync("/api/logs/file?date=2026-09-12&pageSize=50&page=9");
Assert.Equal(3, last.GetProperty("totalPages").GetInt32());
Assert.Equal(3, last.GetProperty("page").GetInt32()); // auf die letzte Seite begrenzt
Assert.Equal(20, last.GetProperty("items").GetArrayLength());
Assert.Equal("Zeile 101", last.GetProperty("items")[0].GetProperty("msg").GetString());
}
[Theory]
[InlineData("../../etc/passwd")]
[InlineData("2026-09-12/../../geheim")]
[InlineData("2026-02-30")]
[InlineData("")]
public async Task Ungueltiges_Datum_400(string date)
{
WriteDay("2026-09-12", ("a", LogLevel.Info, ""));
await using var web = await StartAsync();
using var res = await web.Client.GetAsync("/api/logs/file?date=" + Uri.EscapeDataString(date));
Assert.Equal(400, (int)res.StatusCode);
}
[Fact]
public async Task Tag_ohne_Datei_404()
{
await using var web = await StartAsync();
using var res = await web.Client.GetAsync("/api/logs/file?date=2020-01-01");
Assert.Equal(404, (int)res.StatusCode);
}
}
}