package hooks import ( "testing" ) // ============================================================ // ParseToolHookResponse 测试 // ============================================================ func TestParseToolHookResponse_ExitCode2(t *testing.T) { results := &ExecuteResults{ Results: []*HookResult{ {ExitCode: 2, Stderr: "dangerous operation"}, }, } blocked, reason := ParseToolHookResponse(results) if !blocked { t.Error("exit code 2 should block") } if reason != "dangerous operation" { t.Errorf("reason = %q", reason) } } func TestParseToolHookResponse_JSONBlock(t *testing.T) { results := &ExecuteResults{ Results: []*HookResult{ {ExitCode: 0, JSONOutput: map[string]any{"decision": "block", "reason": "policy violation"}}, }, } blocked, reason := ParseToolHookResponse(results) if !blocked { t.Error("JSON decision=block should block") } if reason != "policy violation" { t.Errorf("reason = %q", reason) } } func TestParseToolHookResponse_ExitCode1_NoBlock(t *testing.T) { results := &ExecuteResults{ Results: []*HookResult{ {ExitCode: 1, Stderr: "hook script error"}, }, } blocked, _ := ParseToolHookResponse(results) if blocked { t.Error("exit code 1 should NOT block (fail-open)") } } func TestParseToolHookResponse_Success_NoBlock(t *testing.T) { results := &ExecuteResults{ Results: []*HookResult{ {ExitCode: 0, Stdout: "all good"}, }, } blocked, _ := ParseToolHookResponse(results) if blocked { t.Error("success should not block") } } func TestParseToolHookResponse_Nil(t *testing.T) { blocked, _ := ParseToolHookResponse(nil) if blocked { t.Error("nil results should not block") } } func TestParseToolHookResponse_ExitCode2_StdoutFallback(t *testing.T) { results := &ExecuteResults{ Results: []*HookResult{ {ExitCode: 2, Stdout: "blocked by policy", Stderr: ""}, }, } blocked, reason := ParseToolHookResponse(results) if !blocked { t.Error("should block") } if reason != "blocked by policy" { t.Errorf("should fallback to stdout when stderr empty, got %q", reason) } } // ============================================================ // ParseStopHookResponse 测试 // ============================================================ func TestParseStopHookResponse_NonZeroExit(t *testing.T) { results := &ExecuteResults{ Results: []*HookResult{ {ExitCode: 1, Stderr: "time to stop"}, }, } shouldStop, reason := ParseStopHookResponse(results) if !shouldStop { t.Error("non-zero exit should trigger stop") } if reason != "time to stop" { t.Errorf("reason = %q", reason) } } func TestParseStopHookResponse_JSONStop(t *testing.T) { results := &ExecuteResults{ Results: []*HookResult{ {ExitCode: 0, JSONOutput: map[string]any{"decision": "stop", "reason": "quota exceeded"}}, }, } shouldStop, reason := ParseStopHookResponse(results) if !shouldStop { t.Error("JSON decision=stop should trigger stop") } if reason != "quota exceeded" { t.Errorf("reason = %q", reason) } } func TestParseStopHookResponse_Success_NoStop(t *testing.T) { results := &ExecuteResults{ Results: []*HookResult{ {ExitCode: 0}, }, } shouldStop, _ := ParseStopHookResponse(results) if shouldStop { t.Error("success should not stop") } } // ============================================================ // ParsePostCompactHookResponse 测试 // ============================================================ func TestParsePostCompactHookResponse_WithOutput(t *testing.T) { results := &ExecuteResults{ Results: []*HookResult{ {ExitCode: 0, Stdout: "Current PR: fix-auth-bug, status: approved"}, }, } summary := ParsePostCompactHookResponse(results) if summary == "" { t.Fatal("should return summary") } if !containsStr(summary, "[hook: post_compact]") { t.Error("should have source tag") } if !containsStr(summary, "fix-auth-bug") { t.Error("should contain hook stdout") } } func TestParsePostCompactHookResponse_FailedHook(t *testing.T) { results := &ExecuteResults{ Results: []*HookResult{ {ExitCode: 1, Stdout: "error output"}, }, } summary := ParsePostCompactHookResponse(results) if summary != "" { t.Error("failed hook should not inject summary") } } func TestParsePostCompactHookResponse_Empty(t *testing.T) { summary := ParsePostCompactHookResponse(nil) if summary != "" { t.Error("nil results should return empty") } } // ============================================================ // ParsePermissionResponse 测试(已有,补充) // ============================================================ func TestParsePermissionResponse_Allow(t *testing.T) { results := &ExecuteResults{ Results: []*HookResult{ {ExitCode: 0, JSONOutput: map[string]any{"decision": "allow"}}, }, } decision, _ := ParsePermissionResponse(results) if decision != "allow" { t.Errorf("decision = %q, want allow", decision) } } func TestParsePermissionResponse_DenyWithReason(t *testing.T) { results := &ExecuteResults{ Results: []*HookResult{ {ExitCode: 0, JSONOutput: map[string]any{"decision": "deny", "reason": "not authorized"}}, }, } decision, reason := ParsePermissionResponse(results) if decision != "deny" { t.Errorf("decision = %q, want deny", decision) } if reason != "not authorized" { t.Errorf("reason = %q", reason) } } func containsStr(s, substr string) bool { for i := 0; i <= len(s)-len(substr); i++ { if s[i:i+len(substr)] == substr { return true } } return false }