Stufe 4c (#764): Kommentare in C#, WebUI-Skripten, CSS und HTML auf Englisch
Rund 2.600 Kommentarblöcke in 310 C#-Dateien übersetzt, Begründungen vollständig erhalten: blockweise gleiche Anzahl wie vorher, kein Block unter 60 % seiner Länge, der Code außerhalb der Kommentare per Tokenizer byte-identisch geprüft. Zitate deutscher Oberflächentexte und Testdaten bleiben deutsch. WebUI: Kommentare in JS/CSS/HTML übersetzt, JSDoc-@param an die in 4b umbenannten Parameter angeglichen. Dabei behoben: - 4b hatte "tag" (HTML-Tag) als deutsches "Tag" gelesen und zu "dayValue" gemacht (makeElement, svg, Tagesübersicht) - zurück auf tag/day. - Aufgaben "InProgress" bekamen die Plakettenart "laufend", für die es nie eine CSS-Klasse gab - jetzt "success" wie laufende Instanzen. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
da4ceac7d5
commit
837a634a22
@@ -5,12 +5,12 @@ using Shouldly;
|
||||
namespace ClawdDotNet.Core.Tests.Tasks;
|
||||
|
||||
/// <summary>
|
||||
/// A1 Taskboard: der Ausführungszustand. Getestet gegen eine echte SQLite-Datei, weil es
|
||||
/// gerade um Schema, Sperrverhalten und atomares Claiming geht — eine Attrappe würde
|
||||
/// genau das verstecken, worauf es ankommt.
|
||||
/// A1 taskboard: the execution state. Tested against a real SQLite file, because this is
|
||||
/// precisely about schema, locking behavior and atomic claiming — a fake would
|
||||
/// hide exactly what matters.
|
||||
///
|
||||
/// Die drei Invarianten aus dem Taskboard-Konzept stehen im Mittelpunkt: nie zwei Claims
|
||||
/// auf einen Termin, kein Dispatch bei offenem Blocker, doppelter Tick = ein Lauf.
|
||||
/// The three invariants from the taskboard concept are at the center: never two claims
|
||||
/// on one occurrence, no dispatch with an open blocker, double tick = one run.
|
||||
/// </summary>
|
||||
public sealed class TaskRepositoryTests : IDisposable
|
||||
{
|
||||
@@ -29,7 +29,7 @@ public sealed class TaskRepositoryTests : IDisposable
|
||||
{
|
||||
ClawdDotNet.Core.Storage.SqliteStorage.ReleasePooledConnections(_directory);
|
||||
try { Directory.Delete(_directory, recursive: true); }
|
||||
catch { /* Aufräumen ist Nebensache */ }
|
||||
catch { /* Cleaning up is secondary */ }
|
||||
}
|
||||
|
||||
private Task<TaskItem> Seed(
|
||||
@@ -49,10 +49,10 @@ public sealed class TaskRepositoryTests : IDisposable
|
||||
private static string Occ(DateTime t) => t.ToUniversalTime().ToString("O");
|
||||
private static readonly DateTime T0 = new(2026, 7, 31, 7, 0, 0, DateTimeKind.Utc);
|
||||
private static DateTime Now => DateTime.UtcNow;
|
||||
private static DateTime NoStaleClaims => Now.AddHours(-1); // Lease-Cutoff weit in der Vergangenheit
|
||||
private static DateTime NoStaleClaims => Now.AddHours(-1); // lease cutoff far in the past
|
||||
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
// Grundfunktionen: Import (Upsert), Lesen, Auflisten
|
||||
// Basic functions: import (upsert), read, list
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
|
||||
[Fact]
|
||||
@@ -80,14 +80,14 @@ public sealed class TaskRepositoryTests : IDisposable
|
||||
[Fact]
|
||||
public async Task Ein_Re_Import_laesst_den_Ausfuehrungszustand_unangetastet()
|
||||
{
|
||||
// Der springende Punkt der Wahrheitsaufteilung: Die Datei ist Wahrheit über die
|
||||
// Definition, die DB über den Ausführungszustand. Ein erneuter Import darf einen
|
||||
// laufenden oder abgeschlossenen Zustand nicht zurücksetzen.
|
||||
// The crux of the split of truth: the file is the truth about the
|
||||
// definition, the DB about the execution state. A renewed import must not reset a
|
||||
// running or completed state.
|
||||
await Seed("t-1");
|
||||
(await _repo.TryClaimAsync("t-1", Occ(T0), "tok", Now, NoStaleClaims, default)).ShouldBeTrue();
|
||||
await _repo.CompleteClaimAsync("t-1", "tok", TaskItemStatus.Done, Now, default);
|
||||
|
||||
// Datei wird erneut importiert (Titel geändert, Status im Frontmatter noch "todo").
|
||||
// The file is imported again (title changed, status in the frontmatter still "todo").
|
||||
await _repo.UpsertAsync(new TaskItem { Id = "t-1", Title = "geändert", Status = TaskItemStatus.Todo }, default);
|
||||
|
||||
var after = await _repo.GetAsync("t-1", default);
|
||||
@@ -132,7 +132,7 @@ public sealed class TaskRepositoryTests : IDisposable
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
// Invariante 1 & 3: nie zwei Claims, doppelter Tick = ein Lauf
|
||||
// Invariants 1 & 3: never two claims, double tick = one run
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
|
||||
[Fact]
|
||||
@@ -148,7 +148,7 @@ public sealed class TaskRepositoryTests : IDisposable
|
||||
[Fact]
|
||||
public async Task Gleichzeitige_Claims_auf_denselben_Termin_ergeben_genau_einen_Gewinner()
|
||||
{
|
||||
// Die Kern-Invariante. Mehrfach wiederholt, weil ein Race sporadisch auftritt.
|
||||
// The core invariant. Repeated several times, because a race occurs sporadically.
|
||||
for (var round = 0; round < 25; round++)
|
||||
{
|
||||
var id = $"t-race-{round}";
|
||||
@@ -168,11 +168,11 @@ public sealed class TaskRepositoryTests : IDisposable
|
||||
{
|
||||
await Seed("t-1");
|
||||
|
||||
// Erster Tick beansprucht und schließt ab.
|
||||
// The first tick claims and completes.
|
||||
(await _repo.TryClaimAsync("t-1", Occ(T0), "tok", Now, NoStaleClaims, default)).ShouldBeTrue();
|
||||
await _repo.CompleteClaimAsync("t-1", "tok", TaskItemStatus.Todo, Now, default);
|
||||
|
||||
// Zweiter Tick im selben Terminfenster: derselbe Occurrence-Key.
|
||||
// Second tick in the same schedule window: the same occurrence key.
|
||||
(await _repo.TryClaimAsync("t-1", Occ(T0), "tok2", Now, NoStaleClaims, default))
|
||||
.ShouldBeFalse("derselbe Termin darf nach Abschluss nicht erneut laufen");
|
||||
}
|
||||
@@ -180,11 +180,11 @@ public sealed class TaskRepositoryTests : IDisposable
|
||||
[Fact]
|
||||
public async Task Ein_gescheiterter_Lauf_wird_nicht_automatisch_wiederholt()
|
||||
{
|
||||
// Kein Retry-Sturm: Der Marker steht schon beim Claim, nicht erst beim Erfolg.
|
||||
// No retry storm: the marker is already set at claim time, not only on success.
|
||||
await Seed("t-1");
|
||||
(await _repo.TryClaimAsync("t-1", Occ(T0), "tok", Now, NoStaleClaims, default)).ShouldBeTrue();
|
||||
|
||||
// Lauf scheitert → zurück auf todo, aber derselbe Termin bleibt verbraucht.
|
||||
// Run fails → back to todo, but the same occurrence stays used up.
|
||||
await _repo.CompleteClaimAsync("t-1", "tok", TaskItemStatus.Todo, Now, default);
|
||||
|
||||
(await _repo.TryClaimAsync("t-1", Occ(T0), "tok2", Now, NoStaleClaims, default))
|
||||
@@ -198,14 +198,14 @@ public sealed class TaskRepositoryTests : IDisposable
|
||||
(await _repo.TryClaimAsync("t-1", Occ(T0), "tok", Now, NoStaleClaims, default)).ShouldBeTrue();
|
||||
await _repo.CompleteClaimAsync("t-1", "tok", TaskItemStatus.Todo, Now, default);
|
||||
|
||||
// Ein späterer Occurrence-Key liegt über dem Marker → wieder fällig.
|
||||
// A later occurrence key lies above the marker → due again.
|
||||
var later = Occ(T0.AddDays(1));
|
||||
(await _repo.TryClaimAsync("t-1", later, "tok3", Now, NoStaleClaims, default))
|
||||
.ShouldBeTrue("ein neuer Termin darf laufen");
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
// Invariante 2: kein Dispatch bei offenem Blocker
|
||||
// Invariant 2: no dispatch with an open blocker
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
|
||||
[Fact]
|
||||
@@ -231,7 +231,7 @@ public sealed class TaskRepositoryTests : IDisposable
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
// Claim-Abschluss, Lease und Reconciliation
|
||||
// Claim completion, lease and reconciliation
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
|
||||
[Fact]
|
||||
@@ -252,11 +252,11 @@ public sealed class TaskRepositoryTests : IDisposable
|
||||
public async Task Verwaiste_Claims_werden_beim_Start_zurueckgesetzt()
|
||||
{
|
||||
await Seed("t-stuck");
|
||||
// Claim mit einem Zeitpunkt, der bereits weit zurückliegt (abgestürzter Lauf).
|
||||
// Claim with a point in time that lies far back already (crashed run).
|
||||
var longAgo = Now.AddMinutes(-30);
|
||||
(await _repo.TryClaimAsync("t-stuck", Occ(T0), "tok", longAgo, longAgo.AddMinutes(-1), default)).ShouldBeTrue();
|
||||
|
||||
// Reconciliation: Claims älter als 5 Minuten gelten als verwaist.
|
||||
// Reconciliation: claims older than 5 minutes count as orphaned.
|
||||
var reset = await _repo.ReleaseStaleClaimsAsync(Now.AddMinutes(-5), Now, default);
|
||||
|
||||
reset.ShouldBe(1);
|
||||
@@ -286,7 +286,7 @@ public sealed class TaskRepositoryTests : IDisposable
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
// Nebenläufigkeit und Persistenz
|
||||
// Concurrency and persistence
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
|
||||
[Fact]
|
||||
|
||||
Reference in New Issue
Block a user