Zwei Tests zur Zeitzonen-Umstellung aus dem letzten Commit: der Zeitstempel eines Logeintrags traegt den Versatz der ANZEIGE-Zone (nicht den des Systems), und die abgelegte JSONL-Zeile traegt ihn mit. Sie liegen bewusst in AppTimeZoneTests - dort steht der uebrige Zeitzonen-Zustand, und innerhalb einer Klasse laufen Tests nacheinander. Das genuegte aber nicht. Von sechs Laeufen fiel einer um und liess sich danach nicht wieder einfangen. Die Ursache ist kein einzelner Test, sondern die Bauart: Mehrere Bausteine halten prozessweiten Zustand, den Tests bewusst umstellen - AppTimeZone, SecretProtection, die Diagnosezaehler von AccountIdSetJson. xUnit fuehrt Testklassen standardmaessig nebenlaeufig aus, also sieht eine Klasse mitten im eigenen Lauf, was eine andere gerade umgestellt hat. Neu ist das nicht, aber die Zeitzonen-Umstellung hat es verschaerft: Seit die Logzeitstempel der Anzeige-Zeitzone folgen, haengt jedes LogMessageEventArgs an AppTimeZone - waehrend AppTimeZoneTests dieselbe Einstellung nach Auckland und zurueck dreht. Deshalb laufen die Testklassen jetzt nacheinander, und die Zaehlerpruefungen arbeiten mit der Differenz statt mit absoluten Werten. Kosten: 4-5 s statt 3 s Laufzeit. Ein Testlauf, dem man glauben kann, ist das allemal wert - danach sechs Laeufe in Folge gruen. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
109 lines
4.3 KiB
C#
109 lines
4.3 KiB
C#
using System;
|
||
using PolyTraderSharp.Services;
|
||
using Xunit;
|
||
|
||
namespace PolyTrader.Tests
|
||
{
|
||
/// <summary>
|
||
/// Sicherheitsnetz für die Anzeige-Zeitzone. Die frühere Fassung rechnete hart gegen die
|
||
/// WINDOWS-ID "W. Europe Standard Time" – auf Linux ohne vollständiges ICU wirft das.
|
||
/// Diese Tests halten fest, dass beide Schreibweisen tragen und ein unbekannter Wert
|
||
/// niemals fatal ist, sondern auf die Systemzeitzone zurückfällt.
|
||
/// </summary>
|
||
public class AppTimeZoneTests
|
||
{
|
||
[Theory]
|
||
[InlineData("Europe/Berlin")] // IANA – Empfehlung, funktioniert auf beiden Plattformen
|
||
[InlineData("W. Europe Standard Time")] // Windows – Altbestand muss weiter tragen
|
||
public void Known_ids_resolve_to_the_same_zone(string id)
|
||
{
|
||
AppTimeZone.Configure(id);
|
||
|
||
// Der Bezeichner unterscheidet sich je Plattform; entscheidend ist der Versatz.
|
||
var berlinWinter = new DateTime(2026, 1, 15, 12, 0, 0, DateTimeKind.Utc);
|
||
Assert.Equal(TimeSpan.FromHours(1), AppTimeZone.Current.GetUtcOffset(berlinWinter));
|
||
|
||
var berlinSommer = new DateTime(2026, 7, 15, 12, 0, 0, DateTimeKind.Utc);
|
||
Assert.Equal(TimeSpan.FromHours(2), AppTimeZone.Current.GetUtcOffset(berlinSommer));
|
||
}
|
||
|
||
[Fact]
|
||
public void Unknown_id_falls_back_to_system_zone_and_warns()
|
||
{
|
||
string? warning = null;
|
||
AppTimeZone.Configure("Gibt/EsNicht", w => warning = w);
|
||
|
||
Assert.Equal(TimeZoneInfo.Local.Id, AppTimeZone.Current.Id);
|
||
Assert.NotNull(warning);
|
||
Assert.Contains("Gibt/EsNicht", warning);
|
||
}
|
||
|
||
[Theory]
|
||
[InlineData(null)]
|
||
[InlineData("")]
|
||
[InlineData(" ")]
|
||
public void Empty_id_means_system_zone(string? id)
|
||
{
|
||
AppTimeZone.Configure(id);
|
||
Assert.Equal(TimeZoneInfo.Local.Id, AppTimeZone.Current.Id);
|
||
}
|
||
|
||
[Fact]
|
||
public void ToDisplay_converts_utc_into_the_configured_zone()
|
||
{
|
||
AppTimeZone.Configure("Europe/Berlin");
|
||
|
||
// 12:00 UTC im Januar = 13:00 Berliner Zeit (MEZ).
|
||
var utc = new DateTime(2026, 1, 15, 12, 0, 0, DateTimeKind.Utc);
|
||
Assert.Equal(13, AppTimeZone.ToDisplay(utc).Hour);
|
||
}
|
||
|
||
[Fact]
|
||
public void ToDisplay_leaves_already_local_timestamps_alone()
|
||
{
|
||
AppTimeZone.Configure(null); // Systemzeitzone
|
||
|
||
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); }
|
||
}
|
||
}
|
||
}
|