package repo import ( "context" "testing" "github.com/agentmail/gateway/internal/db" ) // ListAgents 必须把 mode_enforcement 带出来。 // // # 为什么需要这条测试 // // 该字段曾是**静默丢失**的:`ListAgents` 的 SELECT 里没有它,Scan 自然也没扫, // 于是 `/api/v1/agents` 对全部 Agent 返回空串 —— 而库里四个 Agent 的值 // (native / partial / advisory)一直是对的。 // // 这类故障跟「SELECT 加列但没加进 Scan」(列数不匹配,直接报错)**不是**一回事: // 少取一列不会报任何错,只是安静地少一个事实。而「这个 Agent 到底能不能真的 // 拦住危险操作」是使用者在派活前必须知道的事,缺了它,advisory 档会被当成 // native 档用 —— 正是本项目一直在治的那种「静默不一致」。 // // 所以判据是**值真的传出来**,不是「函数没报错」。 func TestListAgentsCarriesModeEnforcement(t *testing.T) { ctx := context.Background() setupTestDB(t) database := db.DB // 三轮覆盖三档,并包含「空串」这种脏数据(permission_mode.go 用 // NULLIF(...,'') 兜底,说明空串确实可能出现)。 cases := []struct{ name, mode, want string }{ {"t-mode-native", "native", "native"}, {"t-mode-partial", "partial", "partial"}, {"t-mode-advisory", "advisory", "advisory"}, {"t-mode-empty", "", "advisory"}, } for _, c := range cases { if _, err := database.ExecContext(ctx, `INSERT INTO agents (agent_name, secret, platform, status, mode_enforcement) VALUES ($1, 's', 'pi', 'online', $2)`, c.name, c.mode); err != nil { t.Fatalf("插入 %s 失败: %v", c.name, err) } } agents, err := ListAgents(ctx, "all") if err != nil { t.Fatalf("ListAgents 出错: %v", err) } got := map[string]string{} for _, a := range agents { got[a.Name] = a.ModeEnforcement } for _, c := range cases { if got[c.name] != c.want { t.Errorf("%s:mode_enforcement 传出来是 %q,应为 %q —— "+ "列表接口必须带出档位,否则调用方会把 advisory 当 native 用", c.name, got[c.name], c.want) } } // 反向对照:确认这些行确实被查到了(避免「一个都没查到」也能过)。 if len(agents) < len(cases) { t.Errorf("只查到 %d 行,期望至少 %d 行 —— 上面的断言可能是空转", len(agents), len(cases)) } }