package permission import ( "testing" ) // TestNewDenialTracker_Default 测试默认配置创建 func TestNewDenialTracker_Default(t *testing.T) { dt := NewDenialTracker(0, 0) stats := dt.Stats() if stats.ConsecutiveDenials != 0 { t.Errorf("初始连续拒绝应为 0, 实际: %d", stats.ConsecutiveDenials) } if stats.TotalDenials != 0 { t.Errorf("初始总拒绝应为 0, 实际: %d", stats.TotalDenials) } if stats.LastDeniedTool != "" { t.Errorf("初始最后拒绝工具应为空, 实际: %s", stats.LastDeniedTool) } } // TestDenialTracker_RecordDenial_SameTool 测试连续拒绝同一工具 func TestDenialTracker_RecordDenial_SameTool(t *testing.T) { dt := NewDenialTracker(3, 10) dt.RecordDenial("Bash", "rm -rf /") dt.RecordDenial("Bash", "rm -rf /tmp") dt.RecordDenial("Bash", "rm -rf /var") stats := dt.Stats() if stats.ConsecutiveDenials != 3 { t.Errorf("连续拒绝同一工具 3 次, 期望 3, 实际: %d", stats.ConsecutiveDenials) } if stats.TotalDenials != 3 { t.Errorf("总拒绝应为 3, 实际: %d", stats.TotalDenials) } if stats.LastDeniedTool != "Bash" { t.Errorf("最后拒绝工具应为 Bash, 实际: %s", stats.LastDeniedTool) } if stats.LastDeniedInput != "rm -rf /var" { t.Errorf("最后拒绝输入应为 'rm -rf /var', 实际: %s", stats.LastDeniedInput) } } // TestDenialTracker_RecordDenial_DifferentTools 测试拒绝不同工具时重置连续计数 func TestDenialTracker_RecordDenial_DifferentTools(t *testing.T) { dt := NewDenialTracker(3, 10) dt.RecordDenial("Bash", "command1") dt.RecordDenial("Bash", "command2") dt.RecordDenial("Edit", "file.txt") // 不同工具,重置连续计数 stats := dt.Stats() if stats.ConsecutiveDenials != 1 { t.Errorf("切换工具后连续拒绝应重置为 1, 实际: %d", stats.ConsecutiveDenials) } if stats.TotalDenials != 3 { t.Errorf("总拒绝应累加为 3, 实际: %d", stats.TotalDenials) } if stats.LastDeniedTool != "Edit" { t.Errorf("最后拒绝工具应为 Edit, 实际: %s", stats.LastDeniedTool) } } // TestDenialTracker_RecordApproval 测试批准后重置连续计数 func TestDenialTracker_RecordApproval(t *testing.T) { dt := NewDenialTracker(3, 10) dt.RecordDenial("Bash", "command1") dt.RecordDenial("Bash", "command2") dt.RecordApproval("Grep") // 批准后重置连续计数 stats := dt.Stats() if stats.ConsecutiveDenials != 0 { t.Errorf("批准后连续拒绝应重置为 0, 实际: %d", stats.ConsecutiveDenials) } // 总拒绝不应被重置 if stats.TotalDenials != 2 { t.Errorf("批准后总拒绝应保持为 2, 实际: %d", stats.TotalDenials) } if stats.LastDeniedTool != "" { t.Errorf("批准后最后拒绝工具应被清空, 实际: %s", stats.LastDeniedTool) } } // TestDenialTracker_ShouldWarnAgent 测试连续拒绝达到阈值时的警告 func TestDenialTracker_ShouldWarnAgent(t *testing.T) { dt := NewDenialTracker(3, 10) // 未达阈值,不应警告 dt.RecordDenial("Bash", "cmd1") dt.RecordDenial("Bash", "cmd2") warn, _ := dt.ShouldWarnAgent() if warn { t.Error("连续拒绝 2 次时不应警告") } // 达到阈值,应警告 dt.RecordDenial("Bash", "cmd3") warn, msg := dt.ShouldWarnAgent() if !warn { t.Error("连续拒绝 3 次应触发警告") } if msg == "" { t.Error("警告消息不应为空") } // 检查消息包含工具名和次数 if !containsSubstring(msg, "Bash") || !containsSubstring(msg, "3") { t.Errorf("警告消息应包含工具名和次数, 实际: %s", msg) } } // TestDenialTracker_ShouldStopAndAsk 测试总拒绝达到阈值 func TestDenialTracker_ShouldStopAndAsk(t *testing.T) { dt := NewDenialTracker(3, 5) // 降低阈值以便测试 // 未达阈值 for i := 0; i < 4; i++ { dt.RecordDenial("Bash", "cmd") if dt.ShouldStopAndAsk() { t.Errorf("总拒绝 %d 次时不应停止", i+1) } } // 达到阈值 dt.RecordDenial("Bash", "cmd") if !dt.ShouldStopAndAsk() { t.Error("总拒绝 5 次应建议停止") } } // TestDenialTracker_Reset 测试重置 func TestDenialTracker_Reset(t *testing.T) { dt := NewDenialTracker(3, 10) dt.RecordDenial("Bash", "cmd1") dt.RecordDenial("Bash", "cmd2") dt.RecordDenial("Bash", "cmd3") dt.Reset() stats := dt.Stats() if stats.ConsecutiveDenials != 0 || stats.TotalDenials != 0 { t.Error("重置后所有计数应为 0") } if stats.LastDeniedTool != "" || stats.LastDeniedInput != "" { t.Error("重置后最后拒绝信息应为空") } warn, _ := dt.ShouldWarnAgent() if warn { t.Error("重置后不应有警告") } if dt.ShouldStopAndAsk() { t.Error("重置后不应建议停止") } } // TestDenialTracker_CustomThresholds 测试自定义阈值 func TestDenialTracker_CustomThresholds(t *testing.T) { dt := NewDenialTracker(2, 4) // 自定义阈值 // 连续拒绝 2 次即警告 dt.RecordDenial("Edit", "file.txt") dt.RecordDenial("Edit", "file2.txt") warn, _ := dt.ShouldWarnAgent() if !warn { t.Error("自定义阈值 2 次, 连续拒绝 2 次应警告") } // 总拒绝 4 次即停止 dt.RecordDenial("Edit", "file3.txt") dt.RecordDenial("Edit", "file4.txt") if !dt.ShouldStopAndAsk() { t.Error("自定义阈值 4 次, 总拒绝 4 次应停止") } } // TestDenialTracker_ApprovalAfterWarning 测试警告后批准重置 func TestDenialTracker_ApprovalAfterWarning(t *testing.T) { dt := NewDenialTracker(3, 10) // 触发警告 dt.RecordDenial("Bash", "cmd1") dt.RecordDenial("Bash", "cmd2") dt.RecordDenial("Bash", "cmd3") warn, _ := dt.ShouldWarnAgent() if !warn { t.Error("应触发警告") } // 批准后重置 dt.RecordApproval("Bash") warn, _ = dt.ShouldWarnAgent() if warn { t.Error("批准后不应再警告") } } // containsSubstring 检查字符串是否包含子串. func containsSubstring(s, sub string) bool { return len(s) >= len(sub) && (s == sub || len(s) > 0 && containsHelper(s, sub)) } func containsHelper(s, sub string) bool { for i := 0; i <= len(s)-len(sub); i++ { if s[i:i+len(sub)] == sub { return true } } return false }