Files
adminandClaude Sonnet 5 db1afff7e4 Fehlerbehandlung, Logging und feldbezogene Validierung (Kapitel 13.2)
Zentrale Exception-Behandlung (Dispatcher.UIThread.UnhandledException,
AppDomain, TaskScheduler) verhindert Abstürze und protokolliert Fehler
über AppLogger in eine rotierende Log-Datei im App-Datenverzeichnis.
Toast-Benachrichtigungen zeigen Erfolg/Fehler global an. Alle Dialoge
mit Formularfeldern zeigen Validierungsmeldungen jetzt direkt am
betroffenen Feld statt in einem Sammel-Label.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-13 12:38:30 +02:00

106 lines
3.5 KiB
C#

using LehrerApp.Core.Services;
using Xunit;
namespace LehrerApp.Tests;
public sealed class AppLoggerTests
{
[Fact]
public void Konstruktor_LegtLogVerzeichnisAn()
{
using var temp = new TempAppData();
_ = new AppLogger(temp.Path);
Assert.True(Directory.Exists(Path.Combine(temp.Path, "logs")));
}
[Fact]
public void Info_SchreibtZeileInDieHeutigeLogdatei()
{
using var temp = new TempAppData();
var logger = new AppLogger(temp.Path);
logger.Info("Anwendung gestartet.");
var expectedFile = Path.Combine(temp.Path, "logs", $"lehrerapp-{DateTime.Now:yyyy-MM-dd}.log");
Assert.True(File.Exists(expectedFile));
var content = File.ReadAllText(expectedFile);
Assert.Contains("[INFO ]", content);
Assert.Contains("Anwendung gestartet.", content);
}
[Fact]
public void Error_MitException_SchreibtStapelverfolgungMit()
{
using var temp = new TempAppData();
var logger = new AppLogger(temp.Path);
try { throw new InvalidOperationException("Testfehler"); }
catch (Exception ex) { logger.Error("Etwas ist schiefgelaufen.", ex); }
var content = File.ReadAllText(Path.Combine(temp.Path, "logs", $"lehrerapp-{DateTime.Now:yyyy-MM-dd}.log"));
Assert.Contains("[ERROR]", content);
Assert.Contains("Testfehler", content);
Assert.Contains("InvalidOperationException", content);
}
[Fact]
public void MehrereEintraege_WerdenAlleAngehaengt()
{
using var temp = new TempAppData();
var logger = new AppLogger(temp.Path);
logger.Info("Erster Eintrag");
logger.Warn("Zweiter Eintrag");
logger.Error("Dritter Eintrag");
var lines = File.ReadAllLines(Path.Combine(temp.Path, "logs", $"lehrerapp-{DateTime.Now:yyyy-MM-dd}.log"));
Assert.Equal(3, lines.Length);
}
[Fact]
public void Konstruktor_LoeschtLogdateienAelterAlsDieAufbewahrungsfrist()
{
using var temp = new TempAppData();
var logDir = Path.Combine(temp.Path, "logs");
Directory.CreateDirectory(logDir);
var oldFile = Path.Combine(logDir, "lehrerapp-2000-01-01.log");
var recentFile = Path.Combine(logDir, $"lehrerapp-{DateTime.Now:yyyy-MM-dd}.log");
File.WriteAllText(oldFile, "alt");
File.WriteAllText(recentFile, "aktuell");
File.SetLastWriteTime(oldFile, DateTime.Now.AddDays(-30));
_ = new AppLogger(temp.Path);
Assert.False(File.Exists(oldFile));
Assert.True(File.Exists(recentFile));
}
[Fact]
public void Konstruktor_BehaeltLogdateienInnerhalbDerAufbewahrungsfrist()
{
using var temp = new TempAppData();
var logDir = Path.Combine(temp.Path, "logs");
Directory.CreateDirectory(logDir);
var recentButOldFile = Path.Combine(logDir, "lehrerapp-recent.log");
File.WriteAllText(recentButOldFile, "vor 5 Tagen");
File.SetLastWriteTime(recentButOldFile, DateTime.Now.AddDays(-5));
_ = new AppLogger(temp.Path);
Assert.True(File.Exists(recentButOldFile));
}
private sealed class TempAppData : IDisposable
{
public string Path { get; } = System.IO.Path.Combine(
System.IO.Path.GetTempPath(), $"lehrerapp-logger-tests-{Guid.NewGuid():N}");
public TempAppData() => Directory.CreateDirectory(Path);
public void Dispose() { if (Directory.Exists(Path)) Directory.Delete(Path, recursive: true); }
}
}