package app import ( "io" "net/http" "strconv" "strings" "testing" "time" "ccLoad/internal/model" "github.com/gin-gonic/gin" ) func TestHandleActiveRequests_ExposesDebugAvailability(t *testing.T) { t.Parallel() srv := newInMemoryServer(t) m := newActiveRequestManager() id := m.Register(time.Now(), "claude-3-opus", "1.2.3.4", true) m.SetDebugCapture(id, &debugCapture{}) srv.activeRequests = m c, w := newTestContext(t, newRequest(http.MethodGet, "/admin/active-requests", nil)) srv.HandleActiveRequests(c) if w.Code != http.StatusOK { t.Fatalf("status=%d, want %d", w.Code, http.StatusOK) } var resp struct { Success bool `json:"success"` Data []map[string]any `json:"data"` Count int `json:"count"` } mustUnmarshalJSON(t, w.Body.Bytes(), &resp) if !resp.Success || resp.Count != 1 || len(resp.Data) != 1 { t.Fatalf("unexpected resp: %+v", resp) } value, ok := resp.Data[0]["debug_log_available"] if !ok { t.Fatalf("debug_log_available missing in response: %+v", resp.Data[0]) } if got, ok := value.(bool); !ok || !got { t.Fatalf("debug_log_available=%v, want true", value) } } func TestHandleGetActiveRequestDebugLog_ReturnsLiveSnapshot(t *testing.T) { t.Parallel() srv := newInMemoryServer(t) srv.configService.mu.Lock() srv.configService.cache["debug_log_enabled"] = &model.SystemSetting{Key: "debug_log_enabled", Value: "true"} srv.configService.mu.Unlock() req, err := http.NewRequest(http.MethodPost, "https://api.example.com/v1/messages", strings.NewReader(`{"model":"claude-3-opus"}`)) if err != nil { t.Fatalf("new request: %v", err) } req.Header.Set("Authorization", "Bearer sk-test") req.Header.Set("X-Trace-Id", "trace-123") dc := srv.captureDebugRequest(req, []byte(`{"model":"claude-3-opus"}`)) if dc == nil { t.Fatal("captureDebugRequest returned nil") } resp := &http.Response{ StatusCode: http.StatusOK, Header: http.Header{ "Content-Type": []string{"application/json"}, "X-Upstream": []string{"debug"}, }, Body: io.NopCloser(strings.NewReader(`partial-debug-body`)), } dc.wrapResponseBody(resp) buf := make([]byte, len("partial")) n, err := resp.Body.Read(buf) if err != nil && err != io.EOF { t.Fatalf("read wrapped response body: %v", err) } if n != len("partial") { t.Fatalf("read=%d, want %d", n, len("partial")) } m := newActiveRequestManager() id := m.Register(time.Now(), "claude-3-opus", "1.2.3.4", true) m.SetDebugCapture(id, dc) srv.activeRequests = m requestPath := "/admin/active-requests/" + strconv.FormatInt(id, 10) + "/debug-log" c, w := newTestContext(t, newRequest(http.MethodGet, requestPath, nil)) c.Params = gin.Params{{Key: "request_id", Value: strconv.FormatInt(id, 10)}} srv.HandleGetActiveRequestDebugLog(c) if w.Code != http.StatusOK { t.Fatalf("status=%d, want %d; body=%s", w.Code, http.StatusOK, w.Body.String()) } respPayload := mustParseAPIResponse[map[string]any](t, w.Body.Bytes()) if !respPayload.Success { t.Fatalf("success=%v, want true", respPayload.Success) } if got, ok := respPayload.Data["req_method"].(string); !ok || got != http.MethodPost { t.Fatalf("req_method=%v, want POST", respPayload.Data["req_method"]) } if got, ok := respPayload.Data["req_url"].(string); !ok || got != "https://api.example.com/v1/messages" { t.Fatalf("req_url=%v, want upstream URL", respPayload.Data["req_url"]) } if got, ok := respPayload.Data["req_body"].(string); !ok || got != `{"model":"claude-3-opus"}` { t.Fatalf("req_body=%v, want captured request body", respPayload.Data["req_body"]) } if got, ok := respPayload.Data["resp_status"].(float64); !ok || got != http.StatusOK { t.Fatalf("resp_status=%v, want %d", respPayload.Data["resp_status"], http.StatusOK) } if got, ok := respPayload.Data["resp_body"].(string); !ok || got != "partial" { t.Fatalf("resp_body=%v, want partial snapshot", respPayload.Data["resp_body"]) } }