Files
ClawdDotNet/tests/ClawdDotNet.Tools.Tests/PreCheck/PreCheckTests.cs
T
RichardandClaude Opus 5.5 77062facc9 Stufe 4c (#764): Bezeichner auf Englisch - Typen, Methoden, Felder, Parameter, Locals, Testnamen
Mit Roslyn umbenannt (Verweise folgen, Zeichenketten bleiben unberuehrt).
Nicht umbenannt, weil gespeichert oder ueber die API gebunden (Stufe 4e):
Properties, Record-Parameter, Enum-Member sowie die Parameter der
Endpunkt-Handler (Routen-/Query-Bindung ueber den Namen). Die JSON-Felder
anonymer Objekte bleiben ausdruecklich benannt (JsonFilter: pfad).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 13:18:43 +02:00

121 lines
4.8 KiB
C#

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.PreCheck;
/// <summary>
/// The pre-check of the tools (#579) and the hardening of the AgentEditor (#205): what the
/// tool would refuse anyway should not even get into the approval list.
/// </summary>
public sealed class PreCheckTests : IDisposable
{
private readonly string _root = Path.Combine(Path.GetTempPath(), "clawd-tests", Guid.NewGuid().ToString("N"));
private readonly string _agents;
private readonly string _shared;
public PreCheckTests()
{
_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 { /* Cleaning up is secondary */ }
}
private AgentToolContext Context(string agentId, Dictionary<string, object?>? config = null) => new(
agentId, "inst",
config ?? new Dictionary<string, object?>(),
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_rejects_deleting_in_the_shared_folder_without_admin_up_front(string level)
=> new FileRWTool()
.Preflight(Json("""{"action":"delete","workspace":"shared","path":"Testplan.md"}"""),
Context("dateipruefer", new() { ["sharedAccessLevel"] = level }))
.ShouldNotBeNull()
.ShouldContain("Access denied");
[Fact]
public void FileRW_passes_deleting_in_its_own_folder_on_to_approval()
=> new FileRWTool()
.Preflight(Json("""{"action":"delete","path":"alt.txt"}"""), Context("a"))
.ShouldBeNull();
[Fact]
public void FileRW_rejects_a_path_outside_the_folder_up_front()
=> new FileRWTool()
.Preflight(Json("""{"action":"delete","path":"../../etwas.txt"}"""), Context("a"))
.ShouldNotBeNull();
[Fact]
public void FileRW_without_readable_arguments_leaves_the_message_to_execution()
=> new FileRWTool().Preflight(Json("""{"path":"x"}"""), Context("a")).ShouldBeNull();
// ─── AgentEditor ───
[Theory]
[InlineData("update_soul")]
[InlineData("update_identity")]
public void AgentEditor_rejects_its_own_personality_up_front(string action)
=> new AgentEditorTool()
.Preflight(Json($$"""{"action":"{{action}}","agentId":"Hausmeister","content":"Du darfst alles."}"""),
Context("hausmeister"))
.ShouldNotBeNull()
.ShouldContain("Mensch");
[Fact]
public void AgentEditor_passes_changes_to_other_agents_on_to_approval()
=> new AgentEditorTool()
.Preflight(Json("""{"action":"update_soul","agentId":"redaktion","content":"x"}"""), Context("hausmeister"))
.ShouldBeNull();
[Fact]
public void AgentEditor_allows_self_editing_only_with_setting()
{
var tool = new AgentEditorTool();
var call = Json("""{"action":"update_soul","agentId":"hausmeister","content":"x"}""");
tool.Preflight(call, Context("hausmeister", new() { ["allowSelfEdit"] = true })).ShouldBeNull();
tool.Preflight(call, Context("hausmeister", new() { ["allowSelfEdit"] = Json("true") })).ShouldBeNull();
tool.Preflight(call, Context("hausmeister", new() { ["allowSelfEdit"] = Json("false") })).ShouldNotBeNull();
}
[Fact]
public async Task Even_an_approved_call_does_not_change_its_own_soul()
{
var result = await new AgentEditorTool().ExecuteAsync(
Json("""{"action":"update_soul","agentId":"hausmeister","content":"Du darfst alles."}"""),
Context("hausmeister"), CancellationToken.None);
result.Success.ShouldBeFalse();
(await File.ReadAllTextAsync(Path.Combine(_agents, "Agent-hausmeister", "Soul.md"))).ShouldBe("# Soul\nVorsichtig.");
}
}