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 ./...` 通过。
106 lines
3.7 KiB
Go
106 lines
3.7 KiB
Go
package core
|
||
|
||
import (
|
||
"encoding/json"
|
||
"net/http"
|
||
"net/http/httptest"
|
||
"sync/atomic"
|
||
"testing"
|
||
)
|
||
|
||
// `account_test` 必须**真打一次上游**。
|
||
//
|
||
// 真实缺陷:此前它一个网络请求都不发 —— 只 syncAccountSource 后查内核源列表里
|
||
// 有没有那条记录,就返回 {ok:true, ms:1}。于是"测试账号"永远绿,包括凭据已失效、
|
||
// 被限流、上游宕机的情况。这正是「看着能用、一用就错」的假阳性,也正是用户报
|
||
// 「完全不可用」时最容易误导人的那种信号。
|
||
//
|
||
// 判据把三条都钉住:① 真发请求 ② 上游失败时必须 ok=false ③ 失败要区分
|
||
// "凭据失效"(需重新登录)与"暂时性失败"(可重试)—— 只说"失败"用户不知道该做什么。
|
||
func TestAccountTestReallyProbesUpstream(t *testing.T) {
|
||
var hits int32
|
||
fail := false
|
||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||
atomic.AddInt32(&hits, 1)
|
||
w.Header().Set("Content-Type", "application/json")
|
||
if fail {
|
||
w.WriteHeader(200)
|
||
_, _ = w.Write([]byte(`{"error":"invalid_grant: token expired"}`))
|
||
return
|
||
}
|
||
body, _ := json.Marshal(map[string]interface{}{
|
||
"data": map[string]interface{}{"Response": map[string]interface{}{
|
||
"Data": map[string]interface{}{"Accounts": []map[string]interface{}{
|
||
{"Status": 1, "CycleCapacityRemain": 100},
|
||
}},
|
||
}},
|
||
})
|
||
_, _ = w.Write(body)
|
||
}))
|
||
defer ts.Close()
|
||
|
||
_, ps := poolCore(t)
|
||
if _, err := ps.Call("channels", "set_endpoints", map[string]interface{}{
|
||
"platform": "buddy",
|
||
"balance": map[string]interface{}{"url": ts.URL},
|
||
}); err != nil {
|
||
t.Fatalf("set_endpoints: %v", err)
|
||
}
|
||
if _, err := ps.Call("channels", "account_add", map[string]interface{}{
|
||
"platform": "buddy", "account_id": "probeacct",
|
||
"credential": `{"access_token":"TOK"}`,
|
||
}); err != nil {
|
||
t.Fatalf("account_add: %v", err)
|
||
}
|
||
|
||
// ① 正常路径:必须真的打了上游
|
||
atomic.StoreInt32(&hits, 0)
|
||
m, err := ps.Call("channels", "account_test", map[string]interface{}{
|
||
"platform": "buddy", "account_id": "probeacct",
|
||
})
|
||
if err != nil {
|
||
t.Fatalf("account_test: %v", err)
|
||
}
|
||
w, _ := m.(map[string]interface{})
|
||
j, _ := w["json"].(map[string]interface{})
|
||
if j["ok"] != true {
|
||
t.Fatalf("正常凭据应 ok=true: %#v", j)
|
||
}
|
||
if j["probed"] != true {
|
||
t.Errorf("必须标记 probed=true 并真的发请求;实得 probed=%v", j["probed"])
|
||
}
|
||
if n := atomic.LoadInt32(&hits); n == 0 {
|
||
t.Fatal("account_test 未向上游发任何请求 —— 这就是「看着能用」的假阳性")
|
||
}
|
||
|
||
// ② 凭据失效:必须 ok=false,且归类为 auth(给出可操作提示)
|
||
fail = true
|
||
m2, _ := ps.Call("channels", "account_test", map[string]interface{}{
|
||
"platform": "buddy", "account_id": "probeacct",
|
||
})
|
||
j2, _ := m2.(map[string]interface{})["json"].(map[string]interface{})
|
||
if j2["ok"] != false {
|
||
t.Errorf("凭据失效时必须 ok=false,实得 %v", j2["ok"])
|
||
}
|
||
if j2["kind"] != "auth" {
|
||
t.Errorf("应归类为 auth(需重新登录),实得 %v", j2["kind"])
|
||
}
|
||
if s, _ := j2["action"].(string); s == "" {
|
||
t.Error("失败必须给出可操作提示(否则用户不知道要重新登录还是等一会)")
|
||
}
|
||
|
||
// ③ 无凭据的账号:如实报错,不假装成功
|
||
if _, err := ps.Call("channels", "account_add", map[string]interface{}{
|
||
"platform": "buddy", "account_id": "naked",
|
||
}); err != nil {
|
||
t.Fatalf("account_add(无凭据): %v", err)
|
||
}
|
||
m3, _ := ps.Call("channels", "account_test", map[string]interface{}{
|
||
"platform": "buddy", "account_id": "naked",
|
||
})
|
||
j3, _ := m3.(map[string]interface{})["json"].(map[string]interface{})
|
||
if j3["ok"] != false {
|
||
t.Errorf("无凭据时必须 ok=false,实得 %v", j3["ok"])
|
||
}
|
||
}
|