- 补入库: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)与签名私钥仍按规则排除
95 lines
2.7 KiB
Go
95 lines
2.7 KiB
Go
package aitools
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestSaveLoadPolicyRoundTrip(t *testing.T) {
|
|
dir := t.TempDir()
|
|
t.Setenv("HOME", dir)
|
|
// PolicyPath uses UserHomeDir → ~/.opsxdb/mcp.json
|
|
p := Policy{
|
|
AllowedConnectionIDs: []string{" a ", "b", "a", ""},
|
|
Mode: "READ_WRITE",
|
|
AllowDangerous: true,
|
|
DefaultLimit: 50,
|
|
MaxLimit: 200,
|
|
}
|
|
if err := SavePolicy(p); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
path := filepath.Join(dir, ".opsxdb", "mcp.json")
|
|
if _, err := os.Stat(path); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
got, err := LoadPolicy()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got.Mode != ModeReadWrite {
|
|
t.Fatalf("mode=%s", got.Mode)
|
|
}
|
|
if !got.AllowDangerous {
|
|
t.Fatal("want allow_dangerous")
|
|
}
|
|
if len(got.AllowedConnectionIDs) != 2 || got.AllowedConnectionIDs[0] != "a" || got.AllowedConnectionIDs[1] != "b" {
|
|
t.Fatalf("ids=%v", got.AllowedConnectionIDs)
|
|
}
|
|
if got.DefaultLimit != 50 || got.MaxLimit != 200 {
|
|
t.Fatalf("limits %d/%d", got.DefaultLimit, got.MaxLimit)
|
|
}
|
|
}
|
|
|
|
func TestDangerousSQLRequiresAllowFlag(t *testing.T) {
|
|
rt := NewRuntime(Policy{
|
|
AllowedConnectionIDs: []string{"c1"},
|
|
Mode: ModeReadWrite,
|
|
AllowDangerous: false,
|
|
})
|
|
rt.FindConn = func(id string) (*SavedConnection, error) {
|
|
return &SavedConnection{ID: id, Name: "c1", Driver: "mysql", Host: "127.0.0.1", Port: 3306}, nil
|
|
}
|
|
rt.Audit = func(AuditEntry) {}
|
|
rt.AllowConfirmedWrite = true
|
|
_, err := rt.Call(context.Background(), ToolExecuteQuery, map[string]any{
|
|
"connection_id": "c1",
|
|
"sql": "DROP TABLE users",
|
|
"confirm_danger": true,
|
|
})
|
|
if err == nil || !contains(err.Error(), "dangerous sql disabled") {
|
|
t.Fatalf("want dangerous disabled, got %v", err)
|
|
}
|
|
}
|
|
|
|
func TestDefaultWritePolicyStillRequiresConfirmation(t *testing.T) {
|
|
p := DefaultPolicy()
|
|
if !p.WritesEnabled() || !p.AllowsDangerousDDL() {
|
|
t.Fatal("新配置应默认允许读写和危险 SQL")
|
|
}
|
|
if err := p.AllowConnection("c1"); err == nil {
|
|
t.Fatal("默认权限不能自动授权连接")
|
|
}
|
|
p.AllowedConnectionIDs = []string{"c1"}
|
|
rt := NewRuntime(p)
|
|
rt.RejectCallerConfirm = true
|
|
rt.Audit = func(AuditEntry) {}
|
|
rt.FindConn = func(id string) (*SavedConnection, error) {
|
|
return &SavedConnection{ID: id, Driver: "mysql"}, nil
|
|
}
|
|
out, err := rt.Call(context.Background(), ToolExecuteQuery, map[string]any{
|
|
"connection_id": "c1", "sql": "DROP TABLE demo", "confirm_danger": true,
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if out.(map[string]any)["needConfirm"] != true {
|
|
t.Fatalf("默认允许危险 SQL 后,Agent 仍必须等待用户确认: %v", out)
|
|
}
|
|
}
|
|
|
|
func contains(s, sub string) bool { return strings.Contains(s, sub) }
|