jobs-monorepo/internal/infrastructure/http/middlewares_test.go
Elshimy Ziad Magdy Taha c2dd58c019
All checks were successful
Deploy scraper-google / build (push) Successful in 21m26s
Deploy scraper-google / deploy (push) Has been skipped
refactor middleware
2026-08-04 18:17:19 +05:00

137 lines
4.5 KiB
Go

package http
import (
"net/http"
"net/http/httptest"
"testing"
)
func corsHandler(t *testing.T, cfg CORSConfig) http.Handler {
t.Helper()
middleware, err := CORS(cfg)
if err != nil {
t.Fatalf("CORS(%+v): %v", cfg, err)
}
return middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
}))
}
func request(h http.Handler, method, origin string, preflight bool) *httptest.ResponseRecorder {
r := httptest.NewRequest(method, "/jobs", nil)
if origin != "" {
r.Header.Set("Origin", origin)
}
if preflight {
r.Header.Set("Access-Control-Request-Method", "POST")
}
w := httptest.NewRecorder()
h.ServeHTTP(w, r)
return w
}
// A wildcard origin combined with credentials would let any site issue
// credentialed requests and read the responses, so it must not be constructible.
func TestCORSRejectsWildcardWithCredentials(t *testing.T) {
if _, err := CORS(CORSConfig{AllowedOrigins: []string{"*"}, AllowCredentials: true}); err == nil {
t.Fatal("expected an error for wildcard origins combined with credentials")
}
}
func TestCORSRejectsSchemelessOrigin(t *testing.T) {
if _, err := CORS(CORSConfig{AllowedOrigins: []string{"app.example.com"}}); err == nil {
t.Fatal("expected an error for an origin without a scheme")
}
}
func TestCORSDoesNotEchoDisallowedOrigin(t *testing.T) {
h := corsHandler(t, CORSConfig{
AllowedOrigins: []string{"https://app.example.com"},
AllowCredentials: true,
})
w := request(h, http.MethodGet, "https://evil.example.com", false)
if got := w.Header().Get("Access-Control-Allow-Origin"); got != "" {
t.Errorf("Access-Control-Allow-Origin = %q, want empty for a disallowed origin", got)
}
if got := w.Header().Get("Access-Control-Allow-Credentials"); got != "" {
t.Errorf("Access-Control-Allow-Credentials = %q, want empty for a disallowed origin", got)
}
if w.Code != http.StatusOK {
t.Errorf("status = %d, want %d: the handler should still run", w.Code, http.StatusOK)
}
}
func TestCORSAllowsListedOrigin(t *testing.T) {
h := corsHandler(t, CORSConfig{
AllowedOrigins: []string{"https://app.example.com"},
AllowCredentials: true,
})
w := request(h, http.MethodGet, "https://app.example.com", false)
if got, want := w.Header().Get("Access-Control-Allow-Origin"), "https://app.example.com"; got != want {
t.Errorf("Access-Control-Allow-Origin = %q, want %q", got, want)
}
if got, want := w.Header().Get("Access-Control-Allow-Credentials"), "true"; got != want {
t.Errorf("Access-Control-Allow-Credentials = %q, want %q", got, want)
}
// Without Vary, a shared cache could serve one origin's response to another.
if got, want := w.Header().Get("Vary"), "Origin"; got != want {
t.Errorf("Vary = %q, want %q", got, want)
}
}
func TestCORSOriginMatchIsCaseInsensitive(t *testing.T) {
h := corsHandler(t, CORSConfig{AllowedOrigins: []string{"https://App.Example.com"}})
w := request(h, http.MethodGet, "https://app.example.com", false)
if w.Header().Get("Access-Control-Allow-Origin") == "" {
t.Error("origin differing only in case should match the allowlist")
}
}
// An unconfigured service must fail closed rather than allowing every origin.
func TestCORSEmptyAllowlistDeniesCrossOrigin(t *testing.T) {
h := corsHandler(t, CORSConfig{})
w := request(h, http.MethodGet, "https://anything.example.com", false)
if got := w.Header().Get("Access-Control-Allow-Origin"); got != "" {
t.Errorf("Access-Control-Allow-Origin = %q, want empty when no origins are configured", got)
}
}
func TestCORSPreflightShortCircuits(t *testing.T) {
h := corsHandler(t, CORSConfig{AllowedOrigins: []string{"https://app.example.com"}})
w := request(h, http.MethodOptions, "https://app.example.com", true)
if w.Code != http.StatusNoContent {
t.Errorf("status = %d, want %d", w.Code, http.StatusNoContent)
}
// Defaults must apply to a directly constructed config, not only to one
// built by LoadCORSConfigFromEnv.
if w.Header().Get("Access-Control-Allow-Methods") == "" {
t.Error("preflight response is missing Access-Control-Allow-Methods")
}
if w.Header().Get("Access-Control-Allow-Headers") == "" {
t.Error("preflight response is missing Access-Control-Allow-Headers")
}
}
func TestCORSPlainOptionsReachesHandler(t *testing.T) {
h := corsHandler(t, CORSConfig{AllowedOrigins: []string{"https://app.example.com"}})
// OPTIONS without Access-Control-Request-Method is not a preflight.
w := request(h, http.MethodOptions, "https://app.example.com", false)
if w.Code != http.StatusOK {
t.Errorf("status = %d, want %d", w.Code, http.StatusOK)
}
}