axe/internal/middleware/cors_test.go
2026-10-18 22:07:25 +03:00

104 lines
3.7 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package middleware
import (
"net/http"
"net/http/httptest"
"strings"
"testing"
"github.com/gin-gonic/gin"
)
// Тесты CORS: разрешённый origin получает точные заголовки, чужой и
// пустой список — ничего, preflight отвечает 204 до авторизации.
// TestParseOrigins — список источников через запятую, мусор отбрасывается.
func TestParseOrigins(t *testing.T) {
cases := []struct {
raw string
want int
}{
{"", 0},
{"http://localhost:3000", 1},
{"http://a.test, http://b.test", 2},
{" http://a.test ,, ,http://b.test ", 2},
}
for _, c := range cases {
if got := ParseOrigins(c.raw); len(got) != c.want {
t.Errorf("ParseOrigins(%q) = %v, want %d origins", c.raw, got, c.want)
}
}
}
// newCORSRouter — роутер с CORS и одним эндпоинтом.
func newCORSRouter(allowed ...string) *gin.Engine {
gin.SetMode(gin.TestMode)
r := gin.New()
r.Use(CORS(allowed))
r.GET("/x", func(c *gin.Context) { c.String(http.StatusOK, "ok") })
return r
}
// serve выполняет запрос и возвращает запись ответа.
func serve(r *gin.Engine, method, origin string) *httptest.ResponseRecorder {
w := httptest.NewRecorder()
req := httptest.NewRequest(method, "/x", nil)
if origin != "" {
req.Header.Set("Origin", origin)
}
r.ServeHTTP(w, req)
return w
}
func TestCORSAllowedOrigin(t *testing.T) {
r := newCORSRouter("http://localhost:3000")
// Настоящий запрос: заголовок ровно для своего origin + Vary: Origin.
w := serve(r, http.MethodGet, "http://localhost:3000")
if w.Code != http.StatusOK {
t.Fatalf("code = %d, want 200", w.Code)
}
if got := w.Header().Get("Access-Control-Allow-Origin"); got != "http://localhost:3000" {
t.Errorf("Allow-Origin = %q, want the exact origin", got)
}
if !strings.Contains(strings.Join(w.Header()["Vary"], ","), "Origin") {
t.Error("no Vary: Origin — кэши смешают ответы разных источников")
}
if h := w.Header().Get("Access-Control-Allow-Headers"); !strings.Contains(h, "Authorization") {
t.Errorf("Allow-Headers = %q, want Authorization", h)
}
// Preflight: 204 и без тела — браузер спрашивает до запроса с токеном.
w2 := serve(r, http.MethodOptions, "http://localhost:3000")
if w2.Code != http.StatusNoContent {
t.Errorf("preflight code = %d, want 204", w2.Code)
}
if m := w2.Header().Get("Access-Control-Allow-Methods"); !strings.Contains(m, "PATCH") {
t.Errorf("Allow-Methods = %q, want PATCH", m)
}
if w2.Body.Len() != 0 {
t.Errorf("preflight body = %q, want empty", w2.Body.String())
}
}
func TestCORSDisallowedOrigin(t *testing.T) {
// Чужой origin в списке разрешённых — заголовков нет, браузер закроет.
r := newCORSRouter("http://localhost:3000")
w := serve(r, http.MethodGet, "http://evil.test")
if got := w.Header().Get("Access-Control-Allow-Origin"); got != "" {
t.Errorf("Allow-Origin for foreign origin = %q, want empty", got)
}
// Пустой список (CORS_ORIGINS не задан) — заголовков нет вообще.
r2 := newCORSRouter()
w2 := serve(r2, http.MethodGet, "http://localhost:3000")
if got := w2.Header().Get("Access-Control-Allow-Origin"); got != "" {
t.Errorf("Allow-Origin with empty list = %q, want empty", got)
}
// Запрос без Origin (тот же домен) живёт независимо от списка.
w3 := serve(r2, http.MethodGet, "")
if w3.Code != http.StatusOK {
t.Errorf("same-origin request code = %d, want 200", w3.Code)
}
}