using System; using System.Threading; using PolyTrader.Core.Agents; using Xunit; namespace PolyTrader.Tests { /// /// Sicherheitsnetz für die aus ClawdDotNet übernommenen Agenten-Bausteine: LoopGuard (Kostendeckel /// Schritte/Tokens/Zeit) und PermissionGate (Tool-Allow-List). /// public class AgentGuardTests { // ----- LoopGuard ----- [Fact] public void LoopGuard_throws_when_step_limit_exceeded() { var guard = new LoopGuard(new AgentBudget { MaxSteps = 2, MaxTokens = 1_000_000, Timeout = TimeSpan.FromMinutes(5) }); guard.RecordStep(); // 1 guard.RecordStep(); // 2 var ex = Assert.Throws(() => guard.RecordStep()); // 3 -> Abbruch Assert.Equal(AgentBudgetKind.Steps, ex.Kind); } [Fact] public void LoopGuard_throws_when_token_budget_exceeded() { var guard = new LoopGuard(new AgentBudget { MaxSteps = 100, MaxTokens = 100, Timeout = TimeSpan.FromMinutes(5) }); guard.RecordTokens(60); var ex = Assert.Throws(() => guard.RecordTokens(60)); // 120 > 100 Assert.Equal(AgentBudgetKind.Tokens, ex.Kind); Assert.Equal(120, guard.Tokens); } [Fact] public void LoopGuard_ignores_non_positive_token_counts() { var guard = new LoopGuard(new AgentBudget { MaxTokens = 10 }); guard.RecordTokens(0); guard.RecordTokens(-5); Assert.Equal(0, guard.Tokens); } [Fact] public void LoopGuard_throws_when_time_budget_exceeded() { var guard = new LoopGuard(new AgentBudget { MaxSteps = 100, MaxTokens = 1_000_000, Timeout = TimeSpan.FromMilliseconds(10) }); Thread.Sleep(40); var ex = Assert.Throws(() => guard.ThrowIfExpired()); Assert.Equal(AgentBudgetKind.Time, ex.Kind); } // ----- PermissionGate ----- [Fact] public void PermissionGate_allows_listed_tool_case_insensitive() { var gate = new PermissionGate(); var allowed = new[] { "read_logs", "query_trades" }; Assert.True(gate.IsAllowed("READ_LOGS", allowed)); Assert.False(gate.IsAllowed("place_order", allowed)); } [Fact] public void PermissionGate_null_allowlist_permits_everything() { var gate = new PermissionGate(); Assert.True(gate.IsAllowed("anything", null)); } [Fact] public void PermissionGate_enforce_throws_for_denied_tool() { var gate = new PermissionGate(); Assert.Throws(() => gate.Enforce("place_order", new[] { "read_logs" })); } } }