# 现象 `/api/v1/agents` 对全部四个 Agent 返回 `mode_enforcement: ""`, 而库里四个值一直是正确的(pi=native / dsh=partial / opencode=advisory / homeagent=advisory)。这是全功能演练 Phase A 的前置检查抓到的。 # 根因 `ListAgents` 的 SELECT 里没有 `mode_enforcement`,Scan 自然也没扫它。 这与「SELECT 加了列但没加进 Scan」(列数不匹配,直接报错)**不是**一回事: 少取一列不报任何错,只是安静地少一个事实。而「这个 Agent 到底能不能真的拦住 危险操作」是使用者在派活前必须知道的事 —— 缺了它,advisory 档会被当成 native 档用。 # 修法 SELECT 补上 `COALESCE(NULLIF(mode_enforcement, ''), 'advisory')`,与 `permission_mode.go` 同一套兜底(那里也这么写,说明空串确实可能出现)。 # 测试 `repo/agent_mode_list_test.go`:三档各一条 + 一条空串(脏数据),断言**值真的 传出来**而不是「函数没报错」;另加反向对照确认行真的被查到了(避免一个都没查到 也能过)。 反向验证过判据非空转:把修复退掉,测试立刻失败。
68 lines
2.4 KiB
Go
68 lines
2.4 KiB
Go
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))
|
||
}
|
||
}
|