using LehrerApp.Core.Services; using Xunit; namespace LehrerApp.Tests; public sealed class BackupServiceTests { [Fact] public void CreateBackup_OhneVorhandeneDatenbank_GibtNullZurueck() { using var temp = new TempAppData(); var service = new BackupService(temp.Path); var result = service.CreateBackup(Path.Combine(temp.Path, "fehlt.db")); Assert.Null(result); } [Fact] public void CreateBackup_KopiertDieDatenbankdatei() { using var temp = new TempAppData(); var dbPath = Path.Combine(temp.Path, "lehrerapp.db"); File.WriteAllText(dbPath, "Testinhalt"); var service = new BackupService(temp.Path); var backupPath = service.CreateBackup(dbPath); Assert.NotNull(backupPath); Assert.True(File.Exists(backupPath)); Assert.Equal("Testinhalt", File.ReadAllText(backupPath!)); } [Fact] public void ListBackups_SortiertNeuesteZuerst() { using var temp = new TempAppData(); var dbPath = Path.Combine(temp.Path, "lehrerapp.db"); File.WriteAllText(dbPath, "v1"); var service = new BackupService(temp.Path); var first = service.CreateBackup(dbPath)!; File.SetLastWriteTime(first, DateTime.Now.AddMinutes(-5)); File.WriteAllText(dbPath, "v2"); var second = service.CreateBackup(dbPath)!; var backups = service.ListBackups(); Assert.Equal(2, backups.Count); Assert.Equal(second, backups[0].Path); } [Fact] public void CreateBackup_BehaeltNurDieLetztenNBackups() { using var temp = new TempAppData(); var dbPath = Path.Combine(temp.Path, "lehrerapp.db"); var service = new BackupService(temp.Path); for (var i = 0; i < 5; i++) { File.WriteAllText(dbPath, $"v{i}"); var backup = service.CreateBackup(dbPath, keepCount: 3)!; File.SetLastWriteTime(backup, DateTime.Now.AddMinutes(-5 + i)); } Assert.Equal(3, service.ListBackups().Count); } [Fact] public void RestoreBackup_KopiertBackupUeberDieZieldatei() { using var temp = new TempAppData(); var dbPath = Path.Combine(temp.Path, "lehrerapp.db"); File.WriteAllText(dbPath, "alt"); var service = new BackupService(temp.Path); var backupPath = service.CreateBackup(dbPath)!; File.WriteAllText(dbPath, "neu-kaputt"); service.RestoreBackup(backupPath, dbPath); Assert.Equal("alt", File.ReadAllText(dbPath)); } [Fact] public void RestoreBackup_OhneVorhandenesBackup_WirftException() { using var temp = new TempAppData(); var service = new BackupService(temp.Path); Assert.Throws(() => service.RestoreBackup(Path.Combine(temp.Path, "backups", "fehlt.db"), Path.Combine(temp.Path, "lehrerapp.db"))); } [Fact] public void CreateBackup_MitSecondaryDirectory_SpiegeltDasBackupDorthin() { using var temp = new TempAppData(); var dbPath = Path.Combine(temp.Path, "lehrerapp.db"); File.WriteAllText(dbPath, "Testinhalt"); var secondaryDir = Path.Combine(temp.Path, "secondary"); var service = new BackupService(temp.Path) { SecondaryBackupDirectory = secondaryDir }; var backupPath = service.CreateBackup(dbPath); Assert.NotNull(backupPath); var mirrored = Path.Combine(secondaryDir, Path.GetFileName(backupPath!)); Assert.True(File.Exists(mirrored)); Assert.Equal("Testinhalt", File.ReadAllText(mirrored)); } [Fact] public void CreateBackup_SecondaryDirectoryNichtErreichbar_HauptbackupBleibtErhalten() { using var temp = new TempAppData(); var dbPath = Path.Combine(temp.Path, "lehrerapp.db"); File.WriteAllText(dbPath, "Testinhalt"); // Eine gewöhnliche Datei blockiert den Pfad, sodass darunter kein Verzeichnis angelegt // werden kann — simuliert plattformunabhängig ein nicht eingestecktes USB-Ziel bzw. eine // nicht erreichbare Netzwerkfreigabe. var blockingFile = Path.Combine(temp.Path, "blockiert"); File.WriteAllText(blockingFile, "x"); var unreachable = Path.Combine(blockingFile, "secondary"); var service = new BackupService(temp.Path) { SecondaryBackupDirectory = unreachable }; var backupPath = service.CreateBackup(dbPath); Assert.NotNull(backupPath); Assert.True(File.Exists(backupPath)); } [Fact] public void CreateBackup_MitSecondaryDirectory_BehaeltNurDieLetztenNAuchDort() { using var temp = new TempAppData(); var dbPath = Path.Combine(temp.Path, "lehrerapp.db"); var secondaryDir = Path.Combine(temp.Path, "secondary"); var service = new BackupService(temp.Path) { SecondaryBackupDirectory = secondaryDir }; for (var i = 0; i < 5; i++) { File.WriteAllText(dbPath, $"v{i}"); var backup = service.CreateBackup(dbPath, keepCount: 3)!; var mirrored = Path.Combine(secondaryDir, Path.GetFileName(backup)); File.SetLastWriteTime(mirrored, DateTime.Now.AddMinutes(-5 + i)); } Assert.Equal(3, Directory.GetFiles(secondaryDir, "lehrerapp-*.db").Length); } private sealed class TempAppData : IDisposable { public string Path { get; } = System.IO.Path.Combine( System.IO.Path.GetTempPath(), $"lehrerapp-backup-tests-{Guid.NewGuid():N}"); public TempAppData() => Directory.CreateDirectory(Path); public void Dispose() { if (Directory.Exists(Path)) Directory.Delete(Path, recursive: true); } } }