mirror of
https://github.com/Mezeporta/Erupe.git
synced 2026-03-22 07:32:32 +01:00
feat(api): add v2 routes, auth middleware, structured errors, and server status endpoint
Introduces incremental API improvements for custom launcher support (mhf-iel, stratic-dev's Rust launcher): - Standardize all error responses to JSON envelopes with error/message - Add Bearer token auth middleware for v2 routes (legacy body-token preserved) - Add `returning` (>90d inactive) and `courses` fields to auth response - Add /v2/ route prefix with HTTP method enforcement - Add GET /v2/server/status for MezFes, featured weapon, and event status - Add APIEventRepo for read-only event data access Closes #44
This commit is contained in:
122
server/api/middleware_test.go
Normal file
122
server/api/middleware_test.go
Normal file
@@ -0,0 +1,122 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestAuthMiddleware_MissingHeader(t *testing.T) {
|
||||
logger := NewTestLogger(t)
|
||||
server := &APIServer{
|
||||
logger: logger,
|
||||
erupeConfig: NewTestConfig(),
|
||||
}
|
||||
|
||||
handler := server.AuthMiddleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
t.Error("handler should not be called")
|
||||
}))
|
||||
|
||||
req := httptest.NewRequest("POST", "/test", nil)
|
||||
rec := httptest.NewRecorder()
|
||||
handler.ServeHTTP(rec, req)
|
||||
|
||||
if rec.Code != http.StatusUnauthorized {
|
||||
t.Errorf("status = %d, want 401", rec.Code)
|
||||
}
|
||||
var errResp ErrorResponse
|
||||
if err := json.NewDecoder(rec.Body).Decode(&errResp); err != nil {
|
||||
t.Fatalf("decode error: %v", err)
|
||||
}
|
||||
if errResp.Error != "unauthorized" {
|
||||
t.Errorf("error = %q, want unauthorized", errResp.Error)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAuthMiddleware_MalformedHeader(t *testing.T) {
|
||||
logger := NewTestLogger(t)
|
||||
server := &APIServer{
|
||||
logger: logger,
|
||||
erupeConfig: NewTestConfig(),
|
||||
}
|
||||
|
||||
handler := server.AuthMiddleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
t.Error("handler should not be called")
|
||||
}))
|
||||
|
||||
req := httptest.NewRequest("POST", "/test", nil)
|
||||
req.Header.Set("Authorization", "Basic dXNlcjpwYXNz")
|
||||
rec := httptest.NewRecorder()
|
||||
handler.ServeHTTP(rec, req)
|
||||
|
||||
if rec.Code != http.StatusUnauthorized {
|
||||
t.Errorf("status = %d, want 401", rec.Code)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAuthMiddleware_InvalidToken(t *testing.T) {
|
||||
logger := NewTestLogger(t)
|
||||
server := &APIServer{
|
||||
logger: logger,
|
||||
erupeConfig: NewTestConfig(),
|
||||
sessionRepo: &mockAPISessionRepo{
|
||||
userIDErr: sql.ErrNoRows,
|
||||
},
|
||||
}
|
||||
|
||||
handler := server.AuthMiddleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
t.Error("handler should not be called")
|
||||
}))
|
||||
|
||||
req := httptest.NewRequest("POST", "/test", nil)
|
||||
req.Header.Set("Authorization", "Bearer invalid-token")
|
||||
rec := httptest.NewRecorder()
|
||||
handler.ServeHTTP(rec, req)
|
||||
|
||||
if rec.Code != http.StatusUnauthorized {
|
||||
t.Errorf("status = %d, want 401", rec.Code)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAuthMiddleware_ValidToken(t *testing.T) {
|
||||
logger := NewTestLogger(t)
|
||||
server := &APIServer{
|
||||
logger: logger,
|
||||
erupeConfig: NewTestConfig(),
|
||||
sessionRepo: &mockAPISessionRepo{
|
||||
userID: 42,
|
||||
},
|
||||
}
|
||||
|
||||
var gotUserID uint32
|
||||
var gotOK bool
|
||||
handler := server.AuthMiddleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
gotUserID, gotOK = UserIDFromContext(r.Context())
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}))
|
||||
|
||||
req := httptest.NewRequest("POST", "/test", nil)
|
||||
req.Header.Set("Authorization", "Bearer valid-token")
|
||||
rec := httptest.NewRecorder()
|
||||
handler.ServeHTTP(rec, req)
|
||||
|
||||
if rec.Code != http.StatusOK {
|
||||
t.Errorf("status = %d, want 200", rec.Code)
|
||||
}
|
||||
if !gotOK {
|
||||
t.Fatal("userID not found in context")
|
||||
}
|
||||
if gotUserID != 42 {
|
||||
t.Errorf("userID = %d, want 42", gotUserID)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUserIDFromContext_Missing(t *testing.T) {
|
||||
req := httptest.NewRequest("GET", "/test", nil)
|
||||
uid, ok := UserIDFromContext(req.Context())
|
||||
if ok {
|
||||
t.Errorf("expected ok=false, got uid=%d", uid)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user