diff --git a/tests/PolyTrader.Tests/AppTimeZoneTests.cs b/tests/PolyTrader.Tests/AppTimeZoneTests.cs index f91e40a..ddad4af 100644 --- a/tests/PolyTrader.Tests/AppTimeZoneTests.cs +++ b/tests/PolyTrader.Tests/AppTimeZoneTests.cs @@ -1,4 +1,4 @@ -using System; +using System; using PolyTraderSharp.Services; using Xunit; @@ -66,5 +66,43 @@ namespace PolyTrader.Tests var local = new DateTime(2026, 1, 15, 12, 0, 0, DateTimeKind.Local); Assert.Equal(12, AppTimeZone.ToDisplay(local).Hour); } + + // ── §8 des Linux-Befundberichts: Logs stempelten UTC, die Oberfläche zeigte Ortszeit ── + + [Fact] + public void Now_carries_the_offset_of_the_display_zone() + { + AppTimeZone.Configure("Pacific/Auckland"); + try + { + var now = AppTimeZone.Now; + var expected = AppTimeZone.Current.GetUtcOffset(DateTimeOffset.UtcNow); + + // Der Versatz gehört zur ANZEIGE-Zone. Ein umgerechneter DateTime trüge ihn nicht + // mit sich – beim Formatieren mit "zzz" käme dann der Versatz des Systems heraus. + Assert.Equal(expected, now.Offset); + Assert.Equal(DateTime.UtcNow.Add(expected).Date, AppTimeZone.Today); + } + finally { AppTimeZone.Configure(null); } + } + + [Fact] + public void Log_entries_are_stamped_in_the_display_zone() + { + AppTimeZone.Configure("Europe/Berlin"); + try + { + var e = new LogMessageEventArgs("egal", LogLevel.Info); + Assert.Equal(AppTimeZone.Current.GetUtcOffset(e.Timestamp.UtcDateTime), e.Timestamp.Offset); + + // Und die abgelegte JSONL-Zeile trägt denselben Versatz mit. + var parsed = DateTimeOffset.Parse( + LogJson.ParseLine(LogJson.Format(e))!.Time, + System.Globalization.CultureInfo.InvariantCulture); + Assert.Equal(e.Timestamp.Offset, parsed.Offset); + Assert.Equal(e.Timestamp.ToUnixTimeSeconds(), parsed.ToUnixTimeSeconds()); + } + finally { AppTimeZone.Configure(null); } + } } } diff --git a/tests/PolyTrader.Tests/AssemblyInfo.cs b/tests/PolyTrader.Tests/AssemblyInfo.cs new file mode 100644 index 0000000..987afcd --- /dev/null +++ b/tests/PolyTrader.Tests/AssemblyInfo.cs @@ -0,0 +1,17 @@ +using Xunit; + +// Testklassen laufen NACHEINANDER, nicht parallel. +// +// Warum: Mehrere Bausteine der Anwendung halten prozessweiten Zustand, und die Tests stellen ihn +// bewusst um — AppTimeZone (Anzeige-Zeitzone), SecretProtection (Master-Key) und die +// Diagnosezähler von AccountIdSetJson. xUnit führt Testklassen standardmäßig nebenläufig aus; +// dann sieht eine Klasse die Umstellung einer anderen mitten im eigenen Lauf. +// +// Das ist nicht theoretisch: Seit die Logzeitstempel der Anzeige-Zeitzone folgen (§8 des +// Linux-Befundberichts vom 26.08.2026), hängt jedes LogMessageEventArgs an AppTimeZone — während +// AppTimeZoneTests dieselbe Einstellung nach Auckland und zurück dreht. Am 28.08.2026 fiel dabei +// ein Lauf von sechs um, ohne sich danach wieder einfangen zu lassen. +// +// Der Preis ist gering: Der gesamte Satz läuft in rund drei Sekunden. Ein Testlauf, dem man +// glauben kann, ist das allemal wert. +[assembly: CollectionBehavior(DisableTestParallelization = true)] diff --git a/tests/PolyTrader.Tests/LinuxFindingsTests.cs b/tests/PolyTrader.Tests/LinuxFindingsTests.cs index 91e0254..0d8a3a7 100644 --- a/tests/PolyTrader.Tests/LinuxFindingsTests.cs +++ b/tests/PolyTrader.Tests/LinuxFindingsTests.cs @@ -57,36 +57,40 @@ namespace PolyTrader.Tests Assert.Empty(AccountIdSetJson.Deserialize("[]")); } + // Der Zaehler ist prozessweit. Deshalb wird die DIFFERENZ geprueft und nicht der + // absolute Wert - so bleibt der Test auch dann richtig, wenn vorher schon jemand + // einen unlesbaren Wert durchgereicht hat. + [Fact] public void Plain_number_list_is_still_understood() { - AccountIdSetJson.ResetDiagnostics(); + int before = AccountIdSetJson.MalformedCount; Assert.Equal(new System.Collections.Generic.HashSet { 1, 2 }, AccountIdSetJson.Deserialize("1,2")); - Assert.Equal(0, AccountIdSetJson.MalformedCount); + Assert.Equal(before, AccountIdSetJson.MalformedCount); } [Fact] public void Unreadable_value_yields_an_empty_set_and_is_counted() { - AccountIdSetJson.ResetDiagnostics(); + int before = AccountIdSetJson.MalformedCount; // Der Fall aus dem Test: kaputter Wert statt JSON-Array. Frueher warf die // EF-Wertkonvertierung hier und es wurde KEIN einziger Trader geladen. Assert.Empty(AccountIdSetJson.Deserialize("{kaputt")); - Assert.Equal(1, AccountIdSetJson.MalformedCount); + Assert.Equal(before + 1, AccountIdSetJson.MalformedCount); Assert.Equal("{kaputt", AccountIdSetJson.LastMalformedValue); } [Fact] public void Text_that_is_not_a_number_list_is_not_silently_accepted() { - AccountIdSetJson.ResetDiagnostics(); + int before = AccountIdSetJson.MalformedCount; Assert.Empty(AccountIdSetJson.Deserialize("1,zwei")); - Assert.Equal(1, AccountIdSetJson.MalformedCount); + Assert.Equal(before + 1, AccountIdSetJson.MalformedCount); } } }