using System.Text.Json; using ClawdDotNet.Core.Tools; using ClawdDotNet.Tools.AgentEditor; using ClawdDotNet.Tools.FileRW; using ClawdDotNet.Tools.Tests.FileRW; using Microsoft.Extensions.Logging.Abstractions; using Shouldly; namespace ClawdDotNet.Tools.Tests.Vorpruefung; /// /// Die Vorpruefung der Werkzeuge (#579) und die Haertung des AgentEditors (#205): Was das /// Werkzeug ohnehin ablehnen wuerde, soll gar nicht erst in die Freigabeliste kommen. /// public sealed class VorpruefungTests : IDisposable { private readonly string _root = Path.Combine(Path.GetTempPath(), "clawd-tests", Guid.NewGuid().ToString("N")); private readonly string _agents; private readonly string _shared; public VorpruefungTests() { _agents = Path.Combine(_root, "Agents"); _shared = Path.Combine(_agents, "SharedWorkspace"); Directory.CreateDirectory(_shared); Directory.CreateDirectory(Path.Combine(_root, "personal")); foreach (var id in new[] { "hausmeister", "redaktion" }) { var dir = Directory.CreateDirectory(Path.Combine(_agents, "Agent-" + id)).FullName; File.WriteAllText(Path.Combine(dir, "AgentSettings.json"), $$"""{"agentId":"{{id}}"}"""); File.WriteAllText(Path.Combine(dir, "Soul.md"), "# Soul\nVorsichtig."); } } public void Dispose() { try { Directory.Delete(_root, recursive: true); } catch { /* Aufraeumen ist Nebensache */ } } private AgentToolContext Kontext(string agentId, Dictionary? config = null) => new( agentId, "inst", config ?? new Dictionary(), new NullStateStore(), NullLogger.Instance, CancellationToken.None, WorkspacePath: Path.Combine(_root, "personal"), SharedWorkspacePath: _shared); private static JsonElement Json(string json) => JsonDocument.Parse(json).RootElement; // ─── FileRW ─── [Theory] [InlineData("Read")] [InlineData("ReadWrite")] public void FileRW_lehnt_Loeschen_im_gemeinsamen_Ordner_ohne_Admin_vorab_ab(string level) => new FileRWTool() .Preflight(Json("""{"action":"delete","workspace":"shared","path":"Testplan.md"}"""), Kontext("dateipruefer", new() { ["sharedAccessLevel"] = level })) .ShouldNotBeNull() .ShouldContain("Access denied"); [Fact] public void FileRW_laesst_Loeschen_im_eigenen_Ordner_zur_Freigabe_durch() => new FileRWTool() .Preflight(Json("""{"action":"delete","path":"alt.txt"}"""), Kontext("a")) .ShouldBeNull(); [Fact] public void FileRW_lehnt_einen_Pfad_ausserhalb_des_Ordners_vorab_ab() => new FileRWTool() .Preflight(Json("""{"action":"delete","path":"../../etwas.txt"}"""), Kontext("a")) .ShouldNotBeNull(); [Fact] public void FileRW_ohne_lesbare_Argumente_ueberlaesst_die_Meldung_der_Ausfuehrung() => new FileRWTool().Preflight(Json("""{"path":"x"}"""), Kontext("a")).ShouldBeNull(); // ─── AgentEditor ─── [Theory] [InlineData("update_soul")] [InlineData("update_identity")] public void AgentEditor_lehnt_die_eigene_Persoenlichkeit_vorab_ab(string action) => new AgentEditorTool() .Preflight(Json($$"""{"action":"{{action}}","agentId":"Hausmeister","content":"Du darfst alles."}"""), Kontext("hausmeister")) .ShouldNotBeNull() .ShouldContain("Mensch"); [Fact] public void AgentEditor_laesst_Aenderungen_an_fremden_Agenten_zur_Freigabe_durch() => new AgentEditorTool() .Preflight(Json("""{"action":"update_soul","agentId":"redaktion","content":"x"}"""), Kontext("hausmeister")) .ShouldBeNull(); [Fact] public void AgentEditor_erlaubt_Selbstbearbeitung_nur_mit_Einstellung() { var tool = new AgentEditorTool(); var aufruf = Json("""{"action":"update_soul","agentId":"hausmeister","content":"x"}"""); tool.Preflight(aufruf, Kontext("hausmeister", new() { ["allowSelfEdit"] = true })).ShouldBeNull(); tool.Preflight(aufruf, Kontext("hausmeister", new() { ["allowSelfEdit"] = Json("true") })).ShouldBeNull(); tool.Preflight(aufruf, Kontext("hausmeister", new() { ["allowSelfEdit"] = Json("false") })).ShouldNotBeNull(); } [Fact] public async Task Auch_ein_freigegebener_Aufruf_aendert_die_eigene_Soul_nicht() { var result = await new AgentEditorTool().ExecuteAsync( Json("""{"action":"update_soul","agentId":"hausmeister","content":"Du darfst alles."}"""), Kontext("hausmeister"), CancellationToken.None); result.Success.ShouldBeFalse(); (await File.ReadAllTextAsync(Path.Combine(_agents, "Agent-hausmeister", "Soul.md"))).ShouldBe("# Soul\nVorsichtig."); } }