package lua import ( "os" "strings" "testing" ) // **测试绝不允许写生产存储** —— 这条是真实事故的回归判据。 // // 事故:channels 插件用 storeDir() 决定自己的存储位置,而 storeDir 有多级兜底: // 环境变量 → settings → **读 /etc/llmsproxy/config.yaml 的 plugin_dir** → // 硬编码 /etc/llmsproxy/plugins。测试台没设环境变量时,后两级直接命中**生产**: // // 测试插件目录 = /tmp/TestXXX/002/plugins // storeDir 结果 = /etc/llmsproxy/plugins ← 实测 // // 于是测试加载真 channels.lua 时**读的是生产存储、写的也是生产存储**。一次全量 // `go test ./internal/lua` 就把生产账号池覆盖成测试的空 pool: // /etc/llmsproxy/plugins/.channels-store.json 从 1730 变 1986 字节, // 13 个账号变成 0 个(凭据还留在保险库,所以症状是"账号凭空消失")。 // // 同类前科(本文件存在的另一半理由):曾经有 26 个测试账号被写进生产 // .channels-store.json。两次都是"测试没隔离存储目录",所以这条判据必须存在。 func TestStoreDirIsolatedInTests(t *testing.T) { _, ps, pdir := runtimePlugins(t) src, err := bundledAdapters.ReadFile("plugins/channels.lua") if err != nil { t.Fatal(err) } if err := ps.LoadSource("channels", string(src)); err != nil { t.Fatal(err) } out, err := ps.Call("channels", "__store_dir_probe", nil) if err != nil { t.Fatalf("探针: %v", err) } w, _ := out.(map[string]interface{}) j, _ := w["json"].(map[string]interface{}) if j == nil { t.Fatalf("探针无 json: %#v", out) } got, _ := j["store_dir"].(string) t.Logf("测试插件目录 = %s", pdir) t.Logf("storeDir 结果 = %s", got) // ① 绝不能是生产目录 if got == "/etc/llmsproxy/plugins" || strings.HasPrefix(got, "/etc/llmsproxy") { t.Fatalf("storeDir 指向了**生产**目录 %q —— 测试会覆盖生产账号池\n"+ "(真实事故:13 个账号被覆盖成 0 个)。修法:runtimePlugins 设\n"+ "LLMSPROXY_PLUGIN_DIR 到 t.TempDir()", got) } // ② 必须落在测试的临时目录里 if !strings.Contains(got, pdir) && !strings.HasPrefix(got, os.TempDir()) { t.Errorf("storeDir 应落在临时目录(%s 或 %s),实得 %q", pdir, os.TempDir(), got) } // ③ 环境变量必须真的被设上(机制而非巧合) if os.Getenv("LLMSPROXY_PLUGIN_DIR") == "" { t.Error("runtimePlugins 必须设 LLMSPROXY_PLUGIN_DIR(否则 storeDir 会读生产 config.yaml)") } }