Files
ClawdDotNet/tests/ClawdDotNet.Web.Tests/TestbetriebWebTests.cs
T
RichardandClaude Opus 5 6581fd0187 Befunde aus dem Testbetrieb auf der Linux-VM behoben (#572)
- #578: Abgebrochene Laeufe (Schrittgrenze, Zeitueberschreitung, Abbruch,
  Ausnahme) tragen ihre bezahlten Token - vorher 0 in RunUsage, fuer das
  Budget unsichtbar.
- #580: WebFetch prueft die aufgeloeste Zieladresse beim Verbinden; ein
  erlaubter Name, der auf 127.0.0.1 aufloest, wird gesperrt.
- #575: Dateien entstehen unter Unix mit 0600 (AtomicFile, Protokolle,
  FileRW-Anhaengen, state.db), Protokollverzeichnisse mit 0700.
- #576: SIGTERM/SIGINT beenden auch mit Fenster geordnet.
- #593: OpenRouter-Statusdienst wird gestartet.
- #589: Auftragsnachricht nennt aktuelle Zeit, Zeitzone und Termin.
- #581: Unvollstaendiges Argument-JSON erreicht das Werkzeug nicht mehr;
  das Modell bekommt eine verstaendliche Rueckmeldung statt Parsertext.
- #591: Weboberflaeche liest die Felder, die API und Ereignisstrom liefern
  (Freigaben, Protokoll, Taskboard mit Termin, Werkzeug-Einstellungen,
  Tokennutzung); Ereignisstrom in camelCase.
- #592: Chat zeigt Nachrichten nicht mehr doppelt.
- #536 (teilweise): A2A-Antworten tragen Absender und Empfaenger, Parser
  wirft nicht mehr; Datums- und Volltextfilter in Agenten-Chats.
- deploy/publish.py: Packager-Pfad net10.0, Schalter --agent-file.
- Version 0.1.4.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 15:40:56 +02:00

133 lines
4.6 KiB
C#

using ClawdDotNet.Core.Engine;
using ClawdDotNet.Core.Staging;
using ClawdDotNet.Core.Tasks;
using ClawdDotNet.Web.Contracts;
using ClawdDotNet.Web.Endpoints;
using Shouldly;
namespace ClawdDotNet.Web.Tests;
/// <summary>
/// Befunde aus der Sichtung der Weboberflaeche im Testbetrieb (#572): #591 (Feldnamen) und
/// #536 (Richtung der A2A-Antworten).
/// </summary>
public sealed class TestbetriebWebTests
{
// ─── #591 Ereignisstrom und Uebertragungstypen ───
[Fact]
public void Protokollzeilen_im_Ereignisstrom_kommen_in_camelCase()
{
var ereignis = EventEndpoints.Format("log", new LogLineDto("Startup", "INF", "ClawdDotNet startet"));
ereignis.ShouldStartWith("event: log\n");
ereignis.ShouldContain("\"module\":\"Startup\"");
ereignis.ShouldContain("\"level\":\"INF\"");
ereignis.ShouldContain("\"text\":\"ClawdDotNet startet\"");
ereignis.ShouldNotContain("\"Text\"", Case.Sensitive);
}
[Fact]
public void Freigabe_traegt_ihren_Zeitpunkt()
{
var angelegt = new DateTime(2026, 9, 14, 10, 18, 53, DateTimeKind.Utc);
var dto = new StagedCall
{
Id = 1, AgentId = "hausmeister", Tool = "FileRW", Action = "delete",
ArgumentsJson = "{\"path\":\"betrieb/probe-loeschung.txt\"}", CreatedAt = angelegt
}.ToDto();
dto.CreatedAt.ShouldBe(angelegt);
dto.Tool.ShouldBe("FileRW");
dto.Action.ShouldBe("delete");
}
[Fact]
public void Aufgabe_traegt_ihren_Termin()
{
var dto = new TaskItem
{
Id = "t-a66932", Title = "Wetter Berlin stuendlich", Assignee = "@new:wetterdienst",
When = new TaskWhen { Kind = TaskWhenKind.Cron, Value = "5 * * * *", TimeZone = "Europe/Berlin" }
}.ToDto();
dto.When.ShouldNotBeNull();
dto.When.Kind.ShouldBe("cron");
dto.When.Value.ShouldBe("5 * * * *");
dto.When.TimeZone.ShouldBe("Europe/Berlin");
dto.Assignee.ShouldBe("@new:wetterdienst");
}
[Fact]
public void Einmalige_Aufgabe_hat_keinen_Termin()
=> new TaskItem { Id = "t-1", Title = "Einrichtung" }.ToDto().When.ShouldBeNull();
// ─── #536 Absender und Empfaenger ───
private static ChatEntry Anfrage(string vonName, string vonId, string text, int minute)
=> new("user", $"[Nachricht von Agent \"{vonName}\" ({vonId})]\n\n{text}",
new DateTime(2026, 9, 14, 12, minute, 0), ChatSource.AgentComm);
private static ChatEntry Antwort(string text, int minute)
=> new("assistant", text, new DateTime(2026, 9, 14, 12, minute, 30), ChatSource.AgentComm);
[Fact]
public void Antwort_geht_vom_Antwortenden_an_den_Fragenden()
{
var liste = AgentCommunicationBuilder.Build(
[
("redaktion", "Redaktion", (IEnumerable<ChatEntry>)
[
Anfrage("Hausmeister", "hausmeister", "Testnachricht", 48),
Antwort("Empfang bestaetigt", 48)
])
]);
liste.Count.ShouldBe(2);
var anfrage = liste[0];
anfrage.SourceAgentId.ShouldBe("hausmeister");
anfrage.TargetAgentId.ShouldBe("redaktion");
anfrage.Content.ShouldBe("Testnachricht");
var antwort = liste[1];
antwort.SourceAgentId.ShouldBe("redaktion");
antwort.TargetAgentId.ShouldBe("hausmeister", "vorher stand hier der Antwortende selbst");
antwort.TargetAgentName.ShouldBe("Hausmeister");
}
[Fact]
public void Nur_Agentenkommunikation_wird_aufgenommen()
{
var liste = AgentCommunicationBuilder.Build(
[
("hausmeister", "Hausmeister", (IEnumerable<ChatEntry>)
[
new ChatEntry("user", "Hey, wie gehts?", DateTime.Now, ChatSource.WebUi),
new ChatEntry("assistant", "Moin!", DateTime.Now, ChatSource.WebUi)
])
]);
liste.ShouldBeEmpty();
}
[Theory]
[InlineData("[Nachricht von Agent \"Hausmeister\" ohne Klammer]\n\nText")]
[InlineData("[Nachricht von Agent \"Bo\"b\" (bob)]\n\nText")]
[InlineData("[Nachricht von Agent \"")]
public void Absender_lesen_wirft_bei_unerwarteter_Form_nicht(string inhalt)
=> Should.NotThrow(() => AgentCommunicationBuilder.LeseAbsender(inhalt));
[Fact]
public void Absender_lesen_liefert_Kennung_Name_und_Text()
{
var (id, name, text) = AgentCommunicationBuilder.LeseAbsender(
"[Nachricht von Agent \"Hausmeister\" (hausmeister)]\n\nHallo Redaktion!");
id.ShouldBe("hausmeister");
name.ShouldBe("Hausmeister");
text.ShouldBe("Hallo Redaktion!");
}
}