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; /// /// Befunde aus der Sichtung der Weboberflaeche im Testbetrieb (#572): #591 (Feldnamen) und /// #536 (Richtung der A2A-Antworten). /// 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) [ 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) [ 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!"); } }