Stufe 4c (#764): Bezeichner auf Englisch - Typen, Methoden, Felder, Parameter, Locals, Testnamen
Mit Roslyn umbenannt (Verweise folgen, Zeichenketten bleiben unberuehrt). Nicht umbenannt, weil gespeichert oder ueber die API gebunden (Stufe 4e): Properties, Record-Parameter, Enum-Member sowie die Parameter der Endpunkt-Handler (Routen-/Query-Bindung ueber den Namen). Die JSON-Felder anonymer Objekte bleiben ausdruecklich benannt (JsonFilter: pfad). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
837a634a22
commit
77062facc9
@@ -18,15 +18,15 @@ public sealed class ManagerBackupsTests : IAsyncDisposable
|
||||
private readonly string _root = Path.Combine(
|
||||
Path.GetTempPath(), "clawd-tests", "mbak-" + Guid.NewGuid().ToString("N")[..8]);
|
||||
|
||||
private readonly InstanceDirectoryManager _verzeichnisse;
|
||||
private readonly InstanceDirectoryManager _directories;
|
||||
private readonly ManagerSettingsStore _settings;
|
||||
private readonly FakeUploader _upload = new();
|
||||
private readonly FesteUhr _uhr = new(new DateTimeOffset(2026, 9, 11, 2, 0, 0, TimeSpan.Zero));
|
||||
private readonly FixedClock _clock = new(new DateTimeOffset(2026, 9, 11, 2, 0, 0, TimeSpan.Zero));
|
||||
|
||||
public ManagerBackupsTests()
|
||||
{
|
||||
Directory.CreateDirectory(_root);
|
||||
_verzeichnisse = new InstanceDirectoryManager(Path.Combine(_root, "Instances"));
|
||||
_directories = new InstanceDirectoryManager(Path.Combine(_root, "Instances"));
|
||||
_settings = new ManagerSettingsStore(_root);
|
||||
_settings.Settings.LicenseKey = Lizenz;
|
||||
}
|
||||
@@ -39,36 +39,36 @@ public sealed class ManagerBackupsTests : IAsyncDisposable
|
||||
catch { /* A leftover test folder does not turn a run red. */ }
|
||||
}
|
||||
|
||||
private string Instanz(string name, bool automatisch = true, string uhrzeit = "03:00", int behalten = 14)
|
||||
private string Instance(string name, bool automatic = true, string timeOfDay = "03:00", int kept = 14)
|
||||
{
|
||||
var pfad = _verzeichnisse.CreateInstance(name);
|
||||
var datei = Path.Combine(pfad, "InstanceSettings.json");
|
||||
var path = _directories.CreateInstance(name);
|
||||
var datei = Path.Combine(path, "InstanceSettings.json");
|
||||
|
||||
var config = JsonSerializer.Deserialize<InstanceConfig>(File.ReadAllText(datei))!;
|
||||
config.AutoBackupEnabled = automatisch;
|
||||
config.AutoBackupTime = uhrzeit;
|
||||
config.BackupKeepCount = behalten;
|
||||
config.AutoBackupEnabled = automatic;
|
||||
config.AutoBackupTime = timeOfDay;
|
||||
config.BackupKeepCount = kept;
|
||||
File.WriteAllText(datei, JsonSerializer.Serialize(config));
|
||||
|
||||
return pfad;
|
||||
return path;
|
||||
}
|
||||
|
||||
private async Task<ManagerBackups> DienstAsync(IBackupUploader? upload = null)
|
||||
private async Task<ManagerBackups> ServiceAsync(IBackupUploader? upload = null)
|
||||
{
|
||||
var register = new InstanceRegistry(
|
||||
_verzeichnisse, new FakeLauncher(), new FakeChannel(), NullLoggerFactory.Instance,
|
||||
_directories, new FakeLauncher(), new FakeChannel(), NullLoggerFactory.Instance,
|
||||
SupervisionOptions.Default);
|
||||
|
||||
await register.RefreshAsync();
|
||||
|
||||
return new ManagerBackups(
|
||||
_settings, register, upload ?? _upload, NullLogger.Instance, _uhr);
|
||||
_settings, register, upload ?? _upload, NullLogger.Instance, _clock);
|
||||
}
|
||||
|
||||
private static string[] Sicherungen(string pfad)
|
||||
private static string[] Backups(string path)
|
||||
{
|
||||
var ordner = Path.Combine(pfad, "Backups");
|
||||
return Directory.Exists(ordner) ? Directory.GetFiles(ordner) : [];
|
||||
var folder = Path.Combine(path, "Backups");
|
||||
return Directory.Exists(folder) ? Directory.GetFiles(folder) : [];
|
||||
}
|
||||
|
||||
// ─── Names ───
|
||||
@@ -78,21 +78,21 @@ public sealed class ManagerBackupsTests : IAsyncDisposable
|
||||
/// <c>Test</c> — otherwise the cleanup of one throws away the backups of the other.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Ineinanderliegende_Instanznamen_werden_nicht_verwechselt()
|
||||
public void Overlapping_instance_names_are_not_confused()
|
||||
{
|
||||
var eigene = BackupNaming.FileNameFor("Test", new DateTime(2026, 9, 11, 3, 0, 0));
|
||||
var fremde = BackupNaming.FileNameFor("Test_2", new DateTime(2026, 9, 11, 3, 0, 0));
|
||||
var ownOnes = BackupNaming.FileNameFor("Test", new DateTime(2026, 9, 11, 3, 0, 0));
|
||||
var foreignOnes = BackupNaming.FileNameFor("Test_2", new DateTime(2026, 9, 11, 3, 0, 0));
|
||||
|
||||
BackupNaming.BelongsTo(eigene, "Test").ShouldBeTrue();
|
||||
BackupNaming.BelongsTo(fremde, "Test").ShouldBeFalse();
|
||||
BackupNaming.BelongsTo(fremde, "Test_2").ShouldBeTrue();
|
||||
BackupNaming.BelongsTo(ownOnes, "Test").ShouldBeTrue();
|
||||
BackupNaming.BelongsTo(foreignOnes, "Test").ShouldBeFalse();
|
||||
BackupNaming.BelongsTo(foreignOnes, "Test_2").ShouldBeTrue();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("backup_Alpha_2026-09-11_0300.zip")] // daily backup of the instance
|
||||
[InlineData("backup_Alpha_20260911_030000.zip")] // first administration view
|
||||
[InlineData("backup_Alpha_2026-09-11_030000.clawdbak")] // Manager
|
||||
public void Aeltere_Schreibweisen_werden_erkannt(string name)
|
||||
public void Older_spellings_are_recognised(string name)
|
||||
{
|
||||
BackupNaming.TimestampOf(name, "Alpha").ShouldBe(new DateTime(2026, 9, 11, 3, 0, 0));
|
||||
}
|
||||
@@ -101,7 +101,7 @@ public sealed class ManagerBackupsTests : IAsyncDisposable
|
||||
[InlineData("notizen.txt")]
|
||||
[InlineData("backup_Alpha_kaputt.zip")]
|
||||
[InlineData("backup_Alpha_2026-09-11_0300.zip.part")]
|
||||
public void Fremde_Dateien_gehoeren_niemandem(string name)
|
||||
public void Foreign_files_belong_to_nobody(string name)
|
||||
{
|
||||
BackupNaming.BelongsTo(name, "Alpha").ShouldBeFalse();
|
||||
}
|
||||
@@ -109,35 +109,35 @@ public sealed class ManagerBackupsTests : IAsyncDisposable
|
||||
// ─── Backing up ───
|
||||
|
||||
[Fact]
|
||||
public async Task Ohne_Lizenzschluessel_wird_keine_Sicherung_angelegt()
|
||||
public async Task Without_license_key_no_backup_is_created()
|
||||
{
|
||||
var pfad = Instanz("Alpha");
|
||||
var path = Instance("Alpha");
|
||||
_settings.Settings.LicenseKey = "";
|
||||
|
||||
await using var dienst = await DienstAsync();
|
||||
var instanz = (await RegisterAsync()).Single();
|
||||
await using var service = await ServiceAsync();
|
||||
var instance = (await RegisterAsync()).Single();
|
||||
|
||||
await Should.ThrowAsync<BackupException>(() =>
|
||||
dienst.CreateAsync(instanz, false, true, CancellationToken.None));
|
||||
service.CreateAsync(instance, false, true, CancellationToken.None));
|
||||
|
||||
Sicherungen(pfad).ShouldBeEmpty("Unverschlüsselt wird nichts abgelegt — auch nicht ersatzweise.");
|
||||
Backups(path).ShouldBeEmpty("Unverschlüsselt wird nichts abgelegt — auch nicht ersatzweise.");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Eine_Sicherung_ist_verschluesselt_und_wird_hochgeladen()
|
||||
public async Task A_backup_is_encrypted_and_uploaded()
|
||||
{
|
||||
var pfad = Instanz("Alpha");
|
||||
var path = Instance("Alpha");
|
||||
|
||||
await using var dienst = await DienstAsync();
|
||||
var instanz = (await RegisterAsync()).Single();
|
||||
await using var service = await ServiceAsync();
|
||||
var instance = (await RegisterAsync()).Single();
|
||||
|
||||
var ergebnis = await dienst.CreateAsync(instanz, false, true, CancellationToken.None);
|
||||
var result = await service.CreateAsync(instance, false, true, CancellationToken.None);
|
||||
|
||||
LicenseBackupCipher.IsEncrypted(ergebnis.Path).ShouldBeTrue();
|
||||
LicenseBackupCipher.MatchesKey(ergebnis.Path, Lizenz).ShouldBeTrue();
|
||||
LicenseBackupCipher.IsEncrypted(result.Path).ShouldBeTrue();
|
||||
LicenseBackupCipher.MatchesKey(result.Path, Lizenz).ShouldBeTrue();
|
||||
|
||||
ergebnis.Uploaded.ShouldBeTrue();
|
||||
_upload.Hochgeladen.ShouldBe([ergebnis.Path]);
|
||||
result.Uploaded.ShouldBeTrue();
|
||||
_upload.Hochgeladen.ShouldBe([result.Path]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -145,71 +145,71 @@ public sealed class ManagerBackupsTests : IAsyncDisposable
|
||||
/// failed — it lies locally, after all.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task Ein_gescheiterter_Upload_laesst_die_Sicherung_stehen()
|
||||
public async Task A_failed_upload_keeps_the_backup()
|
||||
{
|
||||
Instanz("Alpha");
|
||||
Instance("Alpha");
|
||||
_upload.Fehler = new IOException("Server nicht erreichbar");
|
||||
|
||||
await using var dienst = await DienstAsync();
|
||||
var instanz = (await RegisterAsync()).Single();
|
||||
await using var service = await ServiceAsync();
|
||||
var instance = (await RegisterAsync()).Single();
|
||||
|
||||
var ergebnis = await dienst.CreateAsync(instanz, false, true, CancellationToken.None);
|
||||
var result = await service.CreateAsync(instance, false, true, CancellationToken.None);
|
||||
|
||||
File.Exists(ergebnis.Path).ShouldBeTrue();
|
||||
ergebnis.Uploaded.ShouldBeFalse();
|
||||
ergebnis.UploadProblem.ShouldBe("Server nicht erreichbar");
|
||||
File.Exists(result.Path).ShouldBeTrue();
|
||||
result.Uploaded.ShouldBeFalse();
|
||||
result.UploadProblem.ShouldBe("Server nicht erreichbar");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Aufgeraeumt_wird_nur_die_eigene_Instanz()
|
||||
public async Task Only_the_own_instance_is_cleaned_up()
|
||||
{
|
||||
var ordner = Path.Combine(_root, "ablage");
|
||||
Directory.CreateDirectory(ordner);
|
||||
var folder = Path.Combine(_root, "ablage");
|
||||
Directory.CreateDirectory(folder);
|
||||
|
||||
for (var tag = 1; tag <= 5; tag++)
|
||||
{
|
||||
var zeit = new DateTime(2026, 9, tag, 3, 0, 0);
|
||||
File.WriteAllText(Path.Combine(ordner, BackupNaming.FileNameFor("Test", zeit)), "");
|
||||
File.WriteAllText(Path.Combine(ordner, BackupNaming.FileNameFor("Test_2", zeit)), "");
|
||||
var time = new DateTime(2026, 9, tag, 3, 0, 0);
|
||||
File.WriteAllText(Path.Combine(folder, BackupNaming.FileNameFor("Test", time)), "");
|
||||
File.WriteAllText(Path.Combine(folder, BackupNaming.FileNameFor("Test_2", time)), "");
|
||||
}
|
||||
|
||||
await using var dienst = await DienstAsync();
|
||||
dienst.Rotate(ordner, "Test", keep: 2);
|
||||
await using var service = await ServiceAsync();
|
||||
service.Rotate(folder, "Test", keep: 2);
|
||||
|
||||
var namen = Directory.GetFiles(ordner).Select(Path.GetFileName).ToList();
|
||||
var names = Directory.GetFiles(folder).Select(Path.GetFileName).ToList();
|
||||
|
||||
namen.Count(n => BackupNaming.BelongsTo(n!, "Test")).ShouldBe(2);
|
||||
namen.Count(n => BackupNaming.BelongsTo(n!, "Test_2")).ShouldBe(5, "Die andere Instanz bleibt unberührt.");
|
||||
namen.ShouldContain(BackupNaming.FileNameFor("Test", new DateTime(2026, 9, 5, 3, 0, 0)));
|
||||
names.Count(n => BackupNaming.BelongsTo(n!, "Test")).ShouldBe(2);
|
||||
names.Count(n => BackupNaming.BelongsTo(n!, "Test_2")).ShouldBe(5, "Die andere Instanz bleibt unberührt.");
|
||||
names.ShouldContain(BackupNaming.FileNameFor("Test", new DateTime(2026, 9, 5, 3, 0, 0)));
|
||||
}
|
||||
|
||||
// ─── The daily timer ───
|
||||
|
||||
[Fact]
|
||||
public async Task Vor_der_Uhrzeit_passiert_nichts()
|
||||
public async Task Before_the_time_nothing_happens()
|
||||
{
|
||||
var pfad = Instanz("Alpha", uhrzeit: "03:00");
|
||||
var path = Instance("Alpha", timeOfDay: "03:00");
|
||||
|
||||
await using var dienst = await DienstAsync();
|
||||
await dienst.TickAsync(); // 02:00
|
||||
await using var service = await ServiceAsync();
|
||||
await service.TickAsync(); // 02:00
|
||||
|
||||
Sicherungen(pfad).ShouldBeEmpty();
|
||||
Backups(path).ShouldBeEmpty();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Einmal_am_Tag_und_nicht_oefter()
|
||||
public async Task Once_a_day_and_not_more_often()
|
||||
{
|
||||
var pfad = Instanz("Alpha", uhrzeit: "03:00");
|
||||
var path = Instance("Alpha", timeOfDay: "03:00");
|
||||
|
||||
await using var dienst = await DienstAsync();
|
||||
await using var service = await ServiceAsync();
|
||||
|
||||
_uhr.Jetzt = _uhr.Jetzt.AddHours(1.5); // 03:30
|
||||
await dienst.TickAsync();
|
||||
_clock.Jetzt = _clock.Jetzt.AddHours(1.5); // 03:30
|
||||
await service.TickAsync();
|
||||
|
||||
_uhr.Jetzt = _uhr.Jetzt.AddMinutes(30); // 04:00
|
||||
await dienst.TickAsync();
|
||||
_clock.Jetzt = _clock.Jetzt.AddMinutes(30); // 04:00
|
||||
await service.TickAsync();
|
||||
|
||||
Sicherungen(pfad).Length.ShouldBe(1);
|
||||
Backups(path).Length.ShouldBe(1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -217,32 +217,32 @@ public sealed class ManagerBackupsTests : IAsyncDisposable
|
||||
/// restarts after the backup time gets a second backup.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task Ein_Neustart_des_Managers_erzeugt_keine_zweite_Sicherung()
|
||||
public async Task A_manager_restart_creates_no_second_backup()
|
||||
{
|
||||
var pfad = Instanz("Alpha", uhrzeit: "03:00");
|
||||
_uhr.Jetzt = _uhr.Jetzt.AddHours(1.5);
|
||||
var path = Instance("Alpha", timeOfDay: "03:00");
|
||||
_clock.Jetzt = _clock.Jetzt.AddHours(1.5);
|
||||
|
||||
await using (var erster = await DienstAsync())
|
||||
await erster.TickAsync();
|
||||
await using (var firstOne = await ServiceAsync())
|
||||
await firstOne.TickAsync();
|
||||
|
||||
_uhr.Jetzt = _uhr.Jetzt.AddHours(1);
|
||||
_clock.Jetzt = _clock.Jetzt.AddHours(1);
|
||||
|
||||
await using (var zweiter = await DienstAsync())
|
||||
await zweiter.TickAsync();
|
||||
await using (var secondOne = await ServiceAsync())
|
||||
await secondOne.TickAsync();
|
||||
|
||||
Sicherungen(pfad).Length.ShouldBe(1);
|
||||
Backups(path).Length.ShouldBe(1);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Ohne_Schalter_keine_automatische_Sicherung()
|
||||
public async Task Without_switch_no_automatic_backup()
|
||||
{
|
||||
var pfad = Instanz("Alpha", automatisch: false);
|
||||
_uhr.Jetzt = _uhr.Jetzt.AddHours(5);
|
||||
var path = Instance("Alpha", automatic: false);
|
||||
_clock.Jetzt = _clock.Jetzt.AddHours(5);
|
||||
|
||||
await using var dienst = await DienstAsync();
|
||||
await dienst.TickAsync();
|
||||
await using var service = await ServiceAsync();
|
||||
await service.TickAsync();
|
||||
|
||||
Sicherungen(pfad).ShouldBeEmpty();
|
||||
Backups(path).ShouldBeEmpty();
|
||||
}
|
||||
|
||||
// ─── Helpers ───
|
||||
@@ -250,7 +250,7 @@ public sealed class ManagerBackupsTests : IAsyncDisposable
|
||||
private async Task<IReadOnlyList<ManagedInstance>> RegisterAsync()
|
||||
{
|
||||
var register = new InstanceRegistry(
|
||||
_verzeichnisse, new FakeLauncher(), new FakeChannel(), NullLoggerFactory.Instance,
|
||||
_directories, new FakeLauncher(), new FakeChannel(), NullLoggerFactory.Instance,
|
||||
SupervisionOptions.Default);
|
||||
|
||||
await register.RefreshAsync();
|
||||
@@ -274,7 +274,7 @@ public sealed class ManagerBackupsTests : IAsyncDisposable
|
||||
}
|
||||
|
||||
/// <summary>A clock in UTC, so "local" means the same everywhere in the test.</summary>
|
||||
private sealed class FesteUhr(DateTimeOffset start) : TimeProvider
|
||||
private sealed class FixedClock(DateTimeOffset start) : TimeProvider
|
||||
{
|
||||
public DateTimeOffset Jetzt { get; set; } = start;
|
||||
public override DateTimeOffset GetUtcNow() => Jetzt;
|
||||
|
||||
Reference in New Issue
Block a user