// integration_test.go -- Hook 集成辅助函数的单元测试. // // 覆盖场景: // - BuildPreToolEnv 环境变量构建 // - BuildPostToolEnv 环境变量构建 // - BuildSessionEnv 环境变量构建 // - BuildPermissionEnv 环境变量构建 // - ParsePermissionResponse 解析权限决策 // - marshalJSON 序列化 package hooks import ( "runtime" "testing" ) // TestBuildPreToolEnv 测试 pre_tool_use 环境变量 func TestBuildPreToolEnv(t *testing.T) { env := BuildPreToolEnv("Bash", map[string]string{"command": "ls"}, "/project") checks := map[string]string{ "HOOK_TYPE": "pre_tool_use", "TOOL_NAME": "Bash", "PROJECT_ROOT": "/project", "PLATFORM": runtime.GOOS, } for key, want := range checks { if env[key] != want { t.Errorf("%s = %q, 期望 %q", key, env[key], want) } } if env["TOOL_INPUT"] == "" { t.Error("TOOL_INPUT 不应为空") } } // TestBuildPostToolEnv 测试 post_tool_use 环境变量 func TestBuildPostToolEnv(t *testing.T) { env := BuildPostToolEnv("Bash", nil, "output text", false, "/project") if env["HOOK_TYPE"] != "post_tool_use" { t.Errorf("HOOK_TYPE: %q", env["HOOK_TYPE"]) } if env["TOOL_OUTPUT"] != "output text" { t.Errorf("TOOL_OUTPUT: %q", env["TOOL_OUTPUT"]) } if env["TOOL_IS_ERROR"] != "false" { t.Errorf("TOOL_IS_ERROR: %q", env["TOOL_IS_ERROR"]) } // 测试失败情况 env = BuildPostToolEnv("Bash", nil, "error", true, "/project") if env["HOOK_TYPE"] != "post_tool_use_failure" { t.Errorf("失败时 HOOK_TYPE 应为 post_tool_use_failure: %q", env["HOOK_TYPE"]) } if env["TOOL_IS_ERROR"] != "true" { t.Errorf("TOOL_IS_ERROR 应为 true: %q", env["TOOL_IS_ERROR"]) } } // TestBuildSessionEnv 测试 session 环境变量 func TestBuildSessionEnv(t *testing.T) { env := BuildSessionEnv("sess-123", "/project", HookSessionStart) if env["SESSION_ID"] != "sess-123" { t.Errorf("SESSION_ID: %q", env["SESSION_ID"]) } if env["HOOK_TYPE"] != "session_start" { t.Errorf("HOOK_TYPE: %q", env["HOOK_TYPE"]) } } // TestBuildPermissionEnv 测试权限环境变量 func TestBuildPermissionEnv(t *testing.T) { env := BuildPermissionEnv("Bash", "dangerous command", "/project", HookPermission) if env["TOOL_NAME"] != "Bash" { t.Errorf("TOOL_NAME: %q", env["TOOL_NAME"]) } if env["PERMISSION_REASON"] != "dangerous command" { t.Errorf("PERMISSION_REASON: %q", env["PERMISSION_REASON"]) } if env["HOOK_TYPE"] != "permission_request" { t.Errorf("HOOK_TYPE: %q", env["HOOK_TYPE"]) } } // TestParsePermissionResponse 测试解析权限决策 func TestParsePermissionResponse(t *testing.T) { // allow 决策 results := &ExecuteResults{ Results: []*HookResult{ {JSONOutput: map[string]any{"decision": "allow"}}, }, } decision, reason := ParsePermissionResponse(results) if decision != "allow" { t.Errorf("decision: %q, 期望 'allow'", decision) } // deny 决策带 reason results = &ExecuteResults{ Results: []*HookResult{ {JSONOutput: map[string]any{"decision": "deny", "reason": "not allowed"}}, }, } decision, reason = ParsePermissionResponse(results) if decision != "deny" { t.Errorf("decision: %q, 期望 'deny'", decision) } if reason != "not allowed" { t.Errorf("reason: %q, 期望 'not allowed'", reason) } // 无 JSON 输出 results = &ExecuteResults{ Results: []*HookResult{ {Stdout: "just text"}, }, } decision, _ = ParsePermissionResponse(results) if decision != "" { t.Errorf("无 JSON 时 decision 应为空: %q", decision) } // nil results decision, _ = ParsePermissionResponse(nil) if decision != "" { t.Errorf("nil results 时 decision 应为空: %q", decision) } } // TestMarshalJSON 测试 JSON 序列化 func TestMarshalJSON(t *testing.T) { // nil 值 if marshalJSON(nil) != "{}" { t.Error("nil 应返回 {}") } // 字符串值 if marshalJSON("hello") != "hello" { t.Error("字符串应原样返回") } // 结构化值 result := marshalJSON(map[string]string{"key": "value"}) if result == "" || result == "{}" { t.Errorf("结构化值不应为空: %q", result) } } // TestBuildNotificationEnv 测试通知环境变量 func TestBuildNotificationEnv(t *testing.T) { env := BuildNotificationEnv("task done", "info", "/project") if env["NOTIFICATION_MESSAGE"] != "task done" { t.Errorf("NOTIFICATION_MESSAGE: %q", env["NOTIFICATION_MESSAGE"]) } if env["NOTIFICATION_LEVEL"] != "info" { t.Errorf("NOTIFICATION_LEVEL: %q", env["NOTIFICATION_LEVEL"]) } } // TestBuildTaskEnv 测试任务环境变量 func TestBuildTaskEnv(t *testing.T) { env := BuildTaskEnv("task-1", "fix bug", "/project", HookTaskCreated) if env["TASK_ID"] != "task-1" { t.Errorf("TASK_ID: %q", env["TASK_ID"]) } if env["TASK_DESCRIPTION"] != "fix bug" { t.Errorf("TASK_DESCRIPTION: %q", env["TASK_DESCRIPTION"]) } }