- Chat 接口拒绝 n>1(此前静默丢弃多余 choice),返回 400 结构化错误 - Responses 接口透传 text.format(json_schema / json_object),三种接口协议对齐 - TLS 指纹统一升级到 Chrome_150(internal/httpx.DefaultProfile),与 UA 匹配 - metrics 落盘失败不再静默:Save() 返回错误并打日志,临时文件失败会清理 - GET /api/config 脱敏 admin.password 与 turnstile.api_key(控制台从不展示, 不应随 5s 轮询下发);PUT 改走 ReplaceJSON,先校验后替换、失败回滚 - set_limits / set_turnstile 改指针部分更新:缺字段保持现状,不会把并发、 限流或求解开关按零值清掉 - 读请求体统一 64MB 上限;配置写盘原子替换 + 0600 权限 - main.go 改为 run() 返回退出码:日志落盘、指标落盘、xray 子进程回收全部 挂 defer,任何退出路径(含启动失败)都会执行,杜绝孤儿 xray - proxypool / turnstile / upstream / openai / config / httpx / logbuf 等 逐项补红绿回归测试,新增 14 个测试文件,race 下全绿
44 lines
899 B
Go
44 lines
899 B
Go
package logbuf
|
||
|
||
import (
|
||
"testing"
|
||
"time"
|
||
)
|
||
|
||
// 回归:Add 必须在持锁状态下发送,否则会与 Subscribe 的取消闭包(锁内
|
||
// close(c),logbuf.go:82-89)竞争 —— 向已关闭的 channel 发送是 panic,
|
||
// 发生在后台 goroutine 时整个进程都会挂。
|
||
//
|
||
// 订阅者越多,遍历发送的时间越长,越容易命中这个窗口。
|
||
func TestAddConcurrentUnsubscribeDoesNotFatal(t *testing.T) {
|
||
b := New(1000)
|
||
|
||
const n = 8000
|
||
unsubs := make([]func(), 0, n)
|
||
for i := 0; i < n; i++ {
|
||
_, unsub := b.Subscribe()
|
||
unsubs = append(unsubs, unsub)
|
||
}
|
||
|
||
stop := make(chan struct{})
|
||
done := make(chan struct{})
|
||
go func() {
|
||
defer close(done)
|
||
for {
|
||
select {
|
||
case <-stop:
|
||
return
|
||
default:
|
||
b.Add(Entry{Level: "INFO", Msg: "probe"})
|
||
}
|
||
}
|
||
}()
|
||
|
||
time.Sleep(20 * time.Millisecond)
|
||
for _, u := range unsubs {
|
||
u()
|
||
}
|
||
close(stop)
|
||
<-done
|
||
}
|