package app import ( "encoding/json" "net/http" "net/http/httptest" "testing" "time" ) func TestWithRequestTimeoutAddsContextDeadline(t *testing.T) { handler := withRequestTimeout(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { deadline, ok := r.Context().Deadline() if !ok { t.Fatal("request context should have deadline") } if time.Until(deadline) > 250*time.Millisecond { t.Fatalf("deadline too far away: %s", time.Until(deadline)) } w.WriteHeader(http.StatusNoContent) }), 200*time.Millisecond) rec := httptest.NewRecorder() req := httptest.NewRequest(http.MethodGet, "/api/ops/health", nil) handler.ServeHTTP(rec, req) if rec.Code != http.StatusNoContent { t.Fatalf("status = %d", rec.Code) } } func TestWithRequestTimeoutReturnsEnvelopeWithTraceID(t *testing.T) { handler := withRequestTimeout(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { <-r.Context().Done() }), time.Millisecond) rec := httptest.NewRecorder() req := httptest.NewRequest(http.MethodGet, "/api/ops/health", nil) req.Header.Set("X-Trace-Id", "trace-from-test") handler.ServeHTTP(rec, req) if rec.Code != http.StatusServiceUnavailable { t.Fatalf("status = %d body=%s", rec.Code, rec.Body.String()) } var body struct { Error struct { Code string `json:"code"` Message string `json:"message"` } `json:"error"` TraceID string `json:"traceId"` } if err := json.Unmarshal(rec.Body.Bytes(), &body); err != nil { t.Fatalf("timeout response should be JSON: %v body=%s", err, rec.Body.String()) } if body.Error.Code != "REQUEST_TIMEOUT" || body.Error.Message != "请求处理超时" || body.TraceID != "trace-from-test" { t.Fatalf("unexpected timeout envelope: %+v body=%s", body, rec.Body.String()) } }