mirror of
https://gitcode.com/JianFeeeee/ModelRouter.git
synced 2026-11-07 09:47:23 +00:00
按发布纪律回流。**注意这是内容对齐而非 merge**,理由与依据:
1. 纪律禁止 merge 回主干,因为 merge 会把发布分支的**版本号变更**带进 main。
本项目版本号**只由 `git describe --tags` 推导**(Makefile VERSION),
仓库里没有任何文件存放版本号 ⇒ 回流不存在版本污染风险。
2. 逐项核对确认 **main 是 release 的内容严格子集**:
- `main` 独有的**文件**:0 个;
- `internal/lua/plugins.go` / `internal/core/kernel_actions.go` 等内核文件
main 独有行数:0;
- `deploy.sh` 那 14 行只是 emoji 清理(release 刻意去掉了 emoji);
- `channels.lua` 那 333 行是**旧版内容**(旧 exports 表、旧模型名单)。
- main 独有的 9 个提交按 patch-id **全部已在 release 中等效存在**
(`git cherry` 逐条列出均为 `-`,即已合入)。
3. 故回流 = 把 release 的内容带到 main,等价于"只捡拾修复、不带版本号",
且不会丢任何 main 独有的修复。
本次带回 v2.0.12 的全部内容(37 个提交):
- 源串行 + 按模型节流(照 model-gate)
- 已失效模型存储 + 远端目录闸门
- 余额分档选号
- 模型能力元数据(models.dev,isFree 权威判定)
- **凭据续期修复**(此前恒报 ok:true 却一个请求都不发)
- 通用件 TTL 缓存 + 串行锁;account_test/account_adopt/account_delete 修正
- 内核级:出站授权持久化重启后不生效、`__llmsproxy_plugin_dir` ground truth
校验:`git diff release/v2.0.x` = 0 行;`go build -tags luajit ./...` 通过。
62 lines
2.5 KiB
Go
62 lines
2.5 KiB
Go
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)")
|
||
}
|
||
}
|