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:
Richard
2026-10-04 12:45:03 +02:00
co-authored by Claude Opus 5.5
parent da4ceac7d5
commit 837a634a22
311 changed files with 7238 additions and 7252 deletions
@@ -4,17 +4,17 @@ using Shouldly;
namespace ClawdDotNet.Core.Tests.Scheduling;
/// <summary>
/// Cron-Parsing und Terminberechnung (deckt Teile von B6/B7 ab). Der bisher ungetestete
/// Basis-Scheduler bekommt hier sein Netz: gültige Ausdrücke rechnen richtig, ungültige
/// werfen eine saubere <see cref="FormatException"/> statt einer rohen Parser-Exception
/// oder einer Endlosschleife.
/// Cron parsing and schedule calculation (covers parts of B6/B7). The previously untested
/// base scheduler gets its safety net here: valid expressions calculate correctly, invalid ones
/// throw a clean <see cref="FormatException"/> instead of a raw parser exception
/// or an endless loop.
/// </summary>
public sealed class CronExpressionTests
{
private static DateTime Local(int y, int mo, int d, int h, int mi)
=> new(y, mo, d, h, mi, 0, DateTimeKind.Local);
// ─── R1: korrekte nächste Zeitpunkte ───
// ─── R1: correct next points in time ───
[Fact]
public void Alle_30_Minuten()
@@ -28,7 +28,7 @@ public sealed class CronExpressionTests
public void Werktags_um_drei_Uhr_frueh()
{
var cron = CronExpression.Parse("0 3 * * 1-5"); // Mo–Fr
// 2026-08-01 ist ein Samstag → nächster Lauf Montag, 2026-08-03 03:00
// 2026-08-01 is a Saturday → next run Monday, 2026-08-03 03:00
cron.GetNextOccurrence(Local(2026, 8, 1, 4, 0)).ShouldBe(Local(2026, 8, 3, 3, 0));
}
@@ -43,15 +43,15 @@ public sealed class CronExpressionTests
[Fact]
public void Schaltjahr_29_Februar()
{
var cron = CronExpression.Parse("0 0 29 2 *"); // nur am 29.02.
// 2028 ist das nächste Schaltjahr
var cron = CronExpression.Parse("0 0 29 2 *"); // only on 29 Feb
// 2028 is the next leap year
cron.GetNextOccurrence(Local(2026, 3, 1, 0, 0)).ShouldBe(Local(2028, 2, 29, 0, 0));
}
[Fact]
public void Bereich_mit_Schritt_alle_drei_Stunden_tagsueber()
{
// #597: "0 7-22/3 * * *" warf vorher eine FormatException, die Aufgabe lief nie.
// #597: "0 7-22/3 * * *" used to throw a FormatException, the task never ran.
var cron = CronExpression.Parse("0 7-22/3 * * *"); // 7, 10, 13, 16, 19, 22
cron.GetNextOccurrence(Local(2026, 9, 14, 12, 0)).ShouldBe(Local(2026, 9, 14, 13, 0));
cron.GetNextOccurrence(Local(2026, 9, 14, 22, 0)).ShouldBe(Local(2026, 9, 15, 7, 0));
@@ -66,7 +66,7 @@ public sealed class CronExpressionTests
cron.GetNextOccurrence(Local(2026, 9, 14, 12, 50)).ShouldBe(Local(2026, 9, 14, 13, 10));
}
// ─── R2: Invariante ───
// ─── R2: invariant ───
[Theory]
[InlineData("*/5 * * * *")]
@@ -85,27 +85,27 @@ public sealed class CronExpressionTests
cron.Matches(next.Value).ShouldBeTrue();
}
// ─── R3: ungültige Ausdrücke ───
// ─── R3: invalid expressions ───
[Theory]
[InlineData("* * *")] // zu wenige Felder
[InlineData("* * * * * *")] // zu viele Felder
[InlineData("99 * * * *")] // Minute außerhalb 0-59
[InlineData("* 25 * * *")] // Stunde außerhalb 0-23
[InlineData("a b c d e")] // keine Zahlen
[InlineData("*/0 * * * *")] // Schritt 0 (wäre Endlosschleife)
[InlineData("5-2 * * * *")] // rückwärtiger Bereich
[InlineData("0 22-7/3 * * *")] // rückwärtiger Bereich mit Schritt
[InlineData("0 7-25/3 * * *")] // Bereichsende außerhalb 0-23
[InlineData("0 7-/3 * * *")] // halber Bereich
[InlineData("* * * * ")] // ein leeres Feld nach Trim → 4 Felder
[InlineData("* * *")] // too few fields
[InlineData("* * * * * *")] // too many fields
[InlineData("99 * * * *")] // minute outside 0-59
[InlineData("* 25 * * *")] // hour outside 0-23
[InlineData("a b c d e")] // no numbers
[InlineData("*/0 * * * *")] // step 0 (would be an endless loop)
[InlineData("5-2 * * * *")] // backwards range
[InlineData("0 22-7/3 * * *")] // backwards range with step
[InlineData("0 7-25/3 * * *")] // range end outside 0-23
[InlineData("0 7-/3 * * *")] // half a range
[InlineData("* * * * ")] // an empty field after trim → 4 fields
public void Ungueltige_Ausdruecke_werfen_eine_saubere_FormatException(string expr)
=> Should.Throw<FormatException>(() => CronExpression.Parse(expr));
[Fact]
public void Ein_gueltiger_Ausdruck_mit_Schritt_bleibt_endlich()
{
// Regressionswächter: */0 hätte früher eine Endlosschleife erzeugt.
// Regression guard: */0 would formerly have produced an endless loop.
Should.CompleteIn(() => CronExpression.Parse("*/15 * * * *"), TimeSpan.FromSeconds(2));
}
}