Files
nyoung ed51efd8bc fix: 网关与控制台代码审查修复批次(含回归测试)
- 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 下全绿
2026-10-08 15:13:23 +08:00

62 lines
1.8 KiB
Go

package api
import (
"net/http"
"net/http/httptest"
"strings"
"testing"
"github.com/nyoungo/dsfree2api/internal/config"
)
func newMiddlewareServer(t *testing.T) *Server {
t.Helper()
cfg, err := config.Load(examplePath)
if err != nil {
t.Fatalf("load config: %v", err)
}
return newTestServerWithConfig(t, cfg)
}
// 回归:SSE 已经开流(200 + 若干字节)之后再 panic,不能又写一个 500 的
// JSON 进响应体 —— 状态码改不了,只会把 {"error"...} 拼在事件流后面,
// 客户端解析流直接失败。
func TestRecoverPanicAfterHeadersKeepsBodyIntact(t *testing.T) {
s := newMiddlewareServer(t)
h := s.recoverPanic(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/event-stream; charset=utf-8")
w.WriteHeader(http.StatusOK)
_, _ = w.Write([]byte("data: {\"a\":1}\n\n"))
panic("boom")
}))
w := httptest.NewRecorder()
h.ServeHTTP(w, httptest.NewRequest(http.MethodGet, "/v1/chat/completions", nil))
body := w.Body.String()
if strings.Contains(body, "internal server error") {
t.Fatalf("已开流的响应被追加了 500 JSON: %q", body)
}
if !strings.Contains(body, `data: {"a":1}`) {
t.Fatalf("原有事件流丢了: %q", body)
}
}
// 回归:还没发出响应头就 panic,必须照常返回 500 + 错误对象。
func TestRecoverPanicBeforeHeadersWrites500(t *testing.T) {
s := newMiddlewareServer(t)
h := s.recoverPanic(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
panic("boom")
}))
w := httptest.NewRecorder()
h.ServeHTTP(w, httptest.NewRequest(http.MethodGet, "/v1/chat/completions", nil))
if w.Code != http.StatusInternalServerError {
t.Fatalf("status = %d, want 500", w.Code)
}
if !strings.Contains(w.Body.String(), "internal server error") {
t.Fatalf("body = %q, want the error object", w.Body.String())
}
}