first commit

This commit is contained in:
2026-08-24 13:06:29 +05:30
commit 7d12ebef3d
86 changed files with 39996 additions and 0 deletions

View File

@@ -0,0 +1,143 @@
package httpserver_test
import (
"net/http"
"net/http/httptest"
"testing"
"github.com/krow/krow-backend/go-api/internal/testutil"
)
const devOrigin = "http://localhost:5173"
// corsAPI is newAPI with an explicit CORS allowlist. It is separate because
// every other test in this package asserts the same-origin behaviour, where the
// middleware is not installed at all.
func corsAPI(t *testing.T, origins ...string) http.Handler {
t.Helper()
h := testutil.New(t)
srv := newServer(t, h, origins)
handler := srv.Handler()
// The API routes below now require a session. Signing in once and attaching
// the cookie to every request keeps these tests about CORS: without it they
// would assert 401 and prove nothing about the headers.
userID, email := seededUser(t, h.Pool)
setPassword(t, h.Pool, userID)
result := signIn(t, handler, email, harnessPassword, false)
if result.code != http.StatusOK || result.cookie == nil {
t.Fatalf("the CORS harness could not sign in: status %d", result.code)
}
return withSession(handler, result.cookie)
}
func send(handler http.Handler, method, path string, headers map[string]string) *httptest.ResponseRecorder {
req := httptest.NewRequest(method, path, nil)
for k, v := range headers {
req.Header.Set(k, v)
}
rec := httptest.NewRecorder()
handler.ServeHTTP(rec, req)
return rec
}
// An allowed origin gets its own origin echoed back, never "*".
func TestCORSAllowsConfiguredOrigin(t *testing.T) {
handler := corsAPI(t, devOrigin)
rec := send(handler, "GET", "/api/v1/job-postings", map[string]string{"Origin": devOrigin})
if rec.Code != http.StatusOK {
t.Fatalf("expected 200, got %d", rec.Code)
}
if got := rec.Header().Get("Access-Control-Allow-Origin"); got != devOrigin {
t.Fatalf("Access-Control-Allow-Origin = %q, want %q", got, devOrigin)
}
if rec.Header().Get("Vary") == "" {
t.Fatal("a response that varies by Origin must say so")
}
}
// The preflight the browser sends before a PATCH must succeed without reaching
// the router, and must name the methods the frontend uses.
func TestCORSPreflight(t *testing.T) {
handler := corsAPI(t, devOrigin)
rec := send(handler, "OPTIONS", "/api/v1/job-applications/some-id", map[string]string{
"Origin": devOrigin,
"Access-Control-Request-Method": "PATCH",
"Access-Control-Request-Headers": "content-type",
})
if rec.Code != http.StatusNoContent {
t.Fatalf("preflight: expected 204, got %d (%s)", rec.Code, rec.Body.String())
}
allow := rec.Header().Get("Access-Control-Allow-Methods")
for _, m := range []string{"GET", "POST", "PATCH", "DELETE"} {
if !contains(allow, m) {
t.Fatalf("Access-Control-Allow-Methods = %q, missing %s", allow, m)
}
}
if got := rec.Header().Get("Access-Control-Allow-Headers"); got != "content-type" {
t.Fatalf("Access-Control-Allow-Headers = %q, want the requested header echoed", got)
}
if rec.Header().Get("Access-Control-Max-Age") == "" {
t.Fatal("preflight result should be cacheable")
}
}
// An origin that is not on the list gets no CORS headers, so the browser will
// not hand the response to the page.
func TestCORSRefusesUnknownOrigin(t *testing.T) {
handler := corsAPI(t, devOrigin)
rec := send(handler, "GET", "/api/v1/job-postings", map[string]string{
"Origin": "http://evil.example",
})
if got := rec.Header().Get("Access-Control-Allow-Origin"); got != "" {
t.Fatalf("an unlisted origin was allowed: %q", got)
}
pre := send(handler, "OPTIONS", "/api/v1/job-postings", map[string]string{
"Origin": "http://evil.example",
"Access-Control-Request-Method": "GET",
})
if pre.Code != http.StatusForbidden {
t.Fatalf("preflight from an unlisted origin: expected 403, got %d", pre.Code)
}
}
// A caller with no Origin — curl, a health checker, anything server-to-server —
// is untouched by the middleware.
func TestCORSIgnoresRequestsWithoutOrigin(t *testing.T) {
handler := corsAPI(t, devOrigin)
rec := send(handler, "GET", "/health", nil)
if rec.Code != http.StatusOK {
t.Fatalf("expected 200, got %d", rec.Code)
}
if got := rec.Header().Get("Access-Control-Allow-Origin"); got != "" {
t.Fatalf("a request with no Origin got CORS headers: %q", got)
}
}
// With no allowlist the middleware is not installed, which is the posture for
// any deployment serving the frontend from the API's own origin.
func TestCORSOffByDefault(t *testing.T) {
handler := corsAPI(t) // no origins
rec := send(handler, "GET", "/api/v1/job-postings", map[string]string{"Origin": devOrigin})
if rec.Code != http.StatusOK {
t.Fatalf("expected 200, got %d", rec.Code)
}
if got := rec.Header().Get("Access-Control-Allow-Origin"); got != "" {
t.Fatalf("CORS answered with no allowlist configured: %q", got)
}
}
func contains(haystack, needle string) bool {
for i := 0; i+len(needle) <= len(haystack); i++ {
if haystack[i:i+len(needle)] == needle {
return true
}
}
return false
}