Files
Agent-System/internal/server/access_log_middleware_test.go
weichen f3f23a3018 feat(errcode): 用稳定错误码记录 HTTP 与异步失败
失败响应此前不写日志,request_id 无法对照原因。注册表不覆盖处理器已经给出的 HTTP 状态。
2026-09-28 02:27:16 +00:00

90 lines
3.0 KiB
Go

package server
import (
"bytes"
"log/slog"
"net/http"
"net/http/httptest"
"strings"
"testing"
"chat-agent-system/internal/config"
)
func TestAccessLogPath_DropsQueryAndFragment(t *testing.T) {
got := accessLogPath("/api/v1/chat?token=secret&q=1#frag")
if got != "/api/v1/chat" {
t.Fatalf("path=%q", got)
}
msg := accessLogSafeMessage("GET", "/x?password=p", 200, 3, "req_1", "")
blob := strings.ToLower(strings.Join([]string{
msg["method"].(string),
msg["path"].(string),
msg["request_id"].(string),
}, " "))
for _, leak := range []string{"password", "token", "secret", "bearer", "authorization"} {
if strings.Contains(blob, leak) {
t.Fatalf("leak %q in %#v", leak, msg)
}
}
}
func TestAccessLogMiddleware_EmitsSafeFieldsOnly(t *testing.T) {
var buf bytes.Buffer
prev := slog.Default()
slog.SetDefault(slog.New(slog.NewJSONHandler(&buf, &slog.HandlerOptions{Level: slog.LevelInfo})))
t.Cleanup(func() { slog.SetDefault(prev) })
srv := &Server{cfg: &config.Config{HTTPAccessLogEnabled: true}}
inner := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Authorization", "Bearer should-not-log")
w.WriteHeader(http.StatusCreated)
_, _ = w.Write([]byte(`{"password":"nope","prompt":"secret text"}`))
})
handler := srv.accessLogMiddleware(inner)
req := httptest.NewRequest(http.MethodPost, "/api/v1/login?access_token=leak", strings.NewReader(`{"password":"x"}`))
req.Header.Set("Authorization", "Bearer super-secret")
req.Header.Set("Cookie", "session=abc")
req.Header.Set("X-Request-ID", "req_test_access")
rec := httptest.NewRecorder()
handler.ServeHTTP(rec, req)
if rec.Code != http.StatusCreated {
t.Fatalf("status=%d", rec.Code)
}
line := buf.String()
if !strings.Contains(line, `"msg":"http_access"`) && !strings.Contains(line, `"http_access"`) {
t.Fatalf("missing http_access log: %s", line)
}
if !strings.Contains(line, `"path":"/api/v1/login"`) {
t.Fatalf("expected redacted path, got %s", line)
}
if !strings.Contains(line, `"status":201`) {
t.Fatalf("expected status, got %s", line)
}
if !strings.Contains(line, "req_test_access") {
t.Fatalf("expected request_id, got %s", line)
}
low := strings.ToLower(line)
for _, leak := range []string{"super-secret", "access_token=leak", "password", "bearer super", "session=abc", "secret text"} {
if strings.Contains(low, leak) {
t.Fatalf("log leaked %q: %s", leak, line)
}
}
}
func TestAccessLogMiddleware_DisabledIsNoOp(t *testing.T) {
var buf bytes.Buffer
prev := slog.Default()
slog.SetDefault(slog.New(slog.NewJSONHandler(&buf, &slog.HandlerOptions{Level: slog.LevelInfo})))
t.Cleanup(func() { slog.SetDefault(prev) })
srv := &Server{cfg: &config.Config{HTTPAccessLogEnabled: false}}
handler := srv.accessLogMiddleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNoContent)
}))
handler.ServeHTTP(httptest.NewRecorder(), httptest.NewRequest(http.MethodGet, "/health", nil))
if strings.Contains(buf.String(), "http_access") {
t.Fatalf("disabled middleware must not log: %s", buf.String())
}
}