Files
opsxdb/server/internal/aitools/confirm_bind_test.go
T
logan 2c0bfafd8d 同步完整源码至远程:扁平化重构现状 + 未入库源文件
- 补入库:server/internal/db/dump.go、redis 分页/扫描预算测试、opsxdb-mcp main_test、
  packages/opsxdb-mcp package-lock、docs 审计与对比文档等 37 个新文件
- 含结构扁平化进行中的 80 处修改与 155 处旧结构删除(server/ src/ src-tauri/ 为当前有效源)
- 构建产物(bin/target/dist)与签名私钥仍按规则排除
2026-10-16 03:47:33 +08:00

91 lines
2.9 KiB
Go

package aitools
import "testing"
func TestConfirmBindHashStableAndSensitive(t *testing.T) {
a := ConfirmBindHash("c1", "INSERT INTO t VALUES (1)")
b := ConfirmBindHash("c1", " INSERT INTO t VALUES (1) ")
if a != b {
t.Fatalf("normalize mismatch %s vs %s", a, b)
}
c := ConfirmBindHash("c1", "INSERT INTO t VALUES (2)")
if a == c {
t.Fatal("different sql should differ")
}
if err := VerifyConfirmBind("c1", "INSERT INTO t VALUES (1)", a); err != nil {
t.Fatal(err)
}
if err := VerifyConfirmBind("c1", "INSERT INTO t VALUES (2)", a); err == nil {
t.Fatal("want mismatch")
}
if err := VerifyConfirmBind("c1", "INSERT INTO t VALUES (1)", ""); err == nil {
t.Fatal("want empty bind error")
}
}
func TestAgentConfirmBindIncludesDatabase(t *testing.T) {
bind := ConfirmAgentBindHash("c1", "demo", "DELETE FROM users WHERE id=1")
if err := VerifyAgentBind("c1", "demo", "DELETE FROM users WHERE id=1", bind); err != nil {
t.Fatal(err)
}
if err := VerifyAgentBind("c1", "other", "DELETE FROM users WHERE id=1", bind); err == nil {
t.Fatal("database switch must invalidate confirmation")
}
if err := VerifyAgentBind("c2", "demo", "DELETE FROM users WHERE id=1", bind); err == nil {
t.Fatal("connection switch must invalidate confirmation")
}
}
func TestNeedConfirmIncludesBind(t *testing.T) {
rt := NewRuntime(Policy{
Mode: ModeReadWrite,
AllowedConnectionIDs: []string{"c1"},
DefaultLimit: 100,
MaxLimit: 500,
})
rt.RejectCallerConfirm = true
rt.FindConn = func(id string) (*SavedConnection, error) {
return &SavedConnection{ID: id, Driver: "mysql", Host: "127.0.0.1"}, nil
}
rt.Audit = func(AuditEntry) {}
out, err := rt.Call(nil, ToolExecuteQuery, map[string]any{
"connection_id": "c1",
"sql": "DELETE FROM users WHERE id=1",
})
if err != nil {
t.Fatal(err)
}
m, ok := out.(map[string]any)
if !ok || m["needConfirm"] != true {
t.Fatalf("%v", out)
}
bind, _ := m["confirmBind"].(string)
if bind == "" {
t.Fatal("missing confirmBind")
}
want := ConfirmBindHash("c1", "DELETE FROM users WHERE id=1")
if bind != want {
t.Fatalf("bind %s want %s", bind, want)
}
}
func TestAgentWriteProposalBindsCurrentDatabase(t *testing.T) {
rt := NewRuntime(Policy{Mode: ModeReadWrite, AllowedConnectionIDs: []string{"c1"}})
rt.BoundConnectionID = "c1"
rt.BoundDatabase = "demo"
rt.RejectCallerConfirm = true
rt.Audit = func(AuditEntry) {}
statement := "DELETE FROM users WHERE id=1"
out, err := rt.Call(nil, ToolExecuteQuery, map[string]any{"connection_id": "c1", "sql": statement, "database": "demo"})
if err != nil {
t.Fatal(err)
}
bind := out.(map[string]any)["confirmBind"].(string)
if err := VerifyAgentBind("c1", "demo", statement, bind); err != nil {
t.Fatal(err)
}
if err := VerifyAgentBind("c1", "other", statement, bind); err == nil {
t.Fatal("confirmation must be scoped to demo")
}
}