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,19 +5,19 @@ using Shouldly;
|
||||
namespace ClawdDotNet.Core.Tests.Engine;
|
||||
|
||||
/// <summary>
|
||||
/// Regressionstests für Bug B2: _chatContexts[agentId] ist eine geteilte
|
||||
/// List<ChatMessage>. Nur der Lookup läuft unter Lock — alle Add-Aufrufe im
|
||||
/// Schleifenkörper sind ungeschützt.
|
||||
/// Regression tests for bug B2: _chatContexts[agentId] is a shared
|
||||
/// List<ChatMessage>. Only the lookup runs under lock — all Add calls in the
|
||||
/// loop body are unprotected.
|
||||
///
|
||||
/// Derselbe Agent kann gleichzeitig von mehreren Seiten angestoßen werden:
|
||||
/// Telegram-/Mail-ToolJob, WebView-Nachricht des Benutzers, send_message eines
|
||||
/// anderen Agenten. Ergebnis: korrupte Liste, verschränkte Tool-Sequenzen und
|
||||
/// dadurch HTTP-400-Fehler.
|
||||
/// The same agent can be triggered from several sides at the same time:
|
||||
/// Telegram/mail tool job, the user's WebView message, send_message of
|
||||
/// another agent. Result: corrupt list, interleaved tool sequences and
|
||||
/// thus HTTP 400 errors.
|
||||
/// </summary>
|
||||
public sealed class AgentEngineConcurrencyTests
|
||||
{
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
// E1 — Zwei parallele Chats auf demselben Agenten
|
||||
// E1 — two parallel chats on the same agent
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
|
||||
[Theory]
|
||||
@@ -30,8 +30,8 @@ public sealed class AgentEngineConcurrencyTests
|
||||
.WithTool(FakeTool.Slow(TimeSpan.FromMilliseconds(15)));
|
||||
var agent = fixture.AddAgent("agent-a", "TestTool");
|
||||
|
||||
// Jeder Lauf: ein Tool-Schritt, dann eine Textantwort — unabhängig davon,
|
||||
// wie die beiden Konversationen ineinander verschränkt werden.
|
||||
// Every run: one tool step, then a text answer — regardless of
|
||||
// how the two conversations get interleaved.
|
||||
fixture.Client.RespondsContextually();
|
||||
|
||||
await Task.WhenAll(
|
||||
@@ -43,7 +43,7 @@ public sealed class AgentEngineConcurrencyTests
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
// E2 — Stresstest
|
||||
// E2 — stress test
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
|
||||
[Fact]
|
||||
@@ -65,8 +65,8 @@ public sealed class AgentEngineConcurrencyTests
|
||||
var context = fixture.Engine.GetChatContext(agent.AgentId);
|
||||
ContextInvariants.AssertValid(context);
|
||||
|
||||
// Jede der 30 Anfragen muss genau einmal im Kontext stehen — nichts verloren,
|
||||
// nichts doppelt durch verlorene Schreibzugriffe auf die Liste.
|
||||
// Each of the 30 requests must be in the context exactly once — nothing lost,
|
||||
// nothing doubled through lost writes to the list.
|
||||
var userMessages = context.Count(m => m.Role == "user");
|
||||
userMessages.ShouldBe(30);
|
||||
}
|
||||
@@ -87,7 +87,7 @@ public sealed class AgentEngineConcurrencyTests
|
||||
var first = fixture.Engine.ChatAsync(agent, "Erste", "test-instance", default);
|
||||
var second = fixture.Engine.ChatAsync(agent, "Zweite", "test-instance", default);
|
||||
|
||||
// Warten, bis beide tatsächlich angelaufen sind.
|
||||
// Wait until both have actually started.
|
||||
await WaitUntilAsync(() => fixture.Engine.IsRunning(agent.AgentId));
|
||||
await Task.Delay(50);
|
||||
|
||||
@@ -99,29 +99,29 @@ public sealed class AgentEngineConcurrencyTests
|
||||
|
||||
results.ShouldAllBe(r => r.Status == AgentRunStatus.Cancelled);
|
||||
|
||||
// Entscheidend: _runningChats[agentId] hielt bisher nur EINE CancellationTokenSource —
|
||||
// der zweite Lauf überschrieb den ersten. AbortChat brach dann nur einen ab, der
|
||||
// andere lief bis ins Run-Timeout. Ohne diese Zeitprüfung wäre der Test grün,
|
||||
// obwohl der Abbruch gar nicht gegriffen hat.
|
||||
// Crucial: _runningChats[agentId] used to hold only ONE CancellationTokenSource —
|
||||
// the second run overwrote the first. AbortChat then only aborted one, the
|
||||
// other ran until the run timeout. Without this time check the test would be green
|
||||
// although the abort did not take effect at all.
|
||||
sw.Elapsed.ShouldBeLessThan(TimeSpan.FromSeconds(5),
|
||||
"AbortChat muss beide Läufe sofort beenden, nicht erst über das Timeout");
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
// E4 — Gegenprobe: verschiedene Agenten dürfen parallel laufen
|
||||
// E4 — counter-check: different agents may run in parallel
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
|
||||
[Fact]
|
||||
public async Task Verschiedene_Agenten_laufen_wirklich_parallel()
|
||||
{
|
||||
// Wichtige Gegenprobe: Der Fix für B2 darf nicht versehentlich alle Agenten
|
||||
// gegeneinander sperren.
|
||||
// Important counter-check: the fix for B2 must not accidentally lock all agents
|
||||
// against each other.
|
||||
//
|
||||
// Bis zum 2026-09-24 antwortete die Attrappe hier nur mit Text: Das langsame
|
||||
// Werkzeug lief nie, die Läufe waren sofort fertig, und geprüft wurde nur, ob drei
|
||||
// leere Läufe unter 450 ms bleiben. Das bestand auch mit gegenseitig gesperrten
|
||||
// Agenten - und schlug unter Last fehl, weil es die Maschine mass statt der Engine.
|
||||
// Jetzt ruft jeder Agent das Werkzeug wirklich auf, und bewiesen wird die Überlappung.
|
||||
// Until 2026-09-24 the fake here only answered with text: the slow
|
||||
// tool never ran, the runs were finished immediately, and all that was checked was whether three
|
||||
// empty runs stay below 450 ms. That passed even with agents locking each other
|
||||
// - and failed under load, because it measured the machine instead of the engine.
|
||||
// Now every agent really calls the tool, and the overlap is proven.
|
||||
var tool = FakeTool.Slow(TimeSpan.FromMilliseconds(500));
|
||||
var fixture = new EngineFixture().WithTool(tool);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user