104 lines
3.7 KiB
Go
104 lines
3.7 KiB
Go
|
|
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)
|
|||
|
|
}
|
|||
|
|
}
|