package httpapi import ( "net/http" "net/http/httptest" "strings" "testing" ) func TestCORS(t *testing.T) { t.Setenv("CORS_ORIGINS", "https://pangolin.yanmeiai.com") mw := NewCORS() next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(200) }) h := mw(next) // 允许的 Origin:补 Allow-Origin,普通请求继续。 r := httptest.NewRequest("POST", "/v1/auth/login", nil) r.Header.Set("Origin", "https://pangolin.yanmeiai.com") w := httptest.NewRecorder() h.ServeHTTP(w, r) if got := w.Header().Get("Access-Control-Allow-Origin"); got != "https://pangolin.yanmeiai.com" { t.Fatalf("allowed origin: want header, got %q", got) } if w.Code != 200 { t.Fatalf("non-preflight should reach next, got %d", w.Code) } // OPTIONS 预检:204,不落到业务。 r = httptest.NewRequest("OPTIONS", "/v1/auth/login", nil) r.Header.Set("Origin", "https://pangolin.yanmeiai.com") w = httptest.NewRecorder() h.ServeHTTP(w, r) if w.Code != http.StatusNoContent { t.Fatalf("preflight: want 204, got %d", w.Code) } if w.Header().Get("Access-Control-Allow-Methods") == "" { t.Fatalf("preflight missing Allow-Methods") } // 静默续期/登出把 refresh token 放 X-Refresh-Token 头,必须在允许头里, // 否则浏览器预检拦截 /v1/auth/refresh → 登录后一刷新即掉线。 if ah := w.Header().Get("Access-Control-Allow-Headers"); !strings.Contains(ah, "X-Refresh-Token") { t.Fatalf("preflight Allow-Headers must include X-Refresh-Token, got %q", ah) } // 未白名单 Origin:不补 Allow-Origin。 r = httptest.NewRequest("POST", "/v1/auth/login", nil) r.Header.Set("Origin", "https://evil.example.com") w = httptest.NewRecorder() h.ServeHTTP(w, r) if got := w.Header().Get("Access-Control-Allow-Origin"); got != "" { t.Fatalf("disallowed origin should get no header, got %q", got) } }