package session_test import ( "errors" "net/http" "net/http/httptest" "testing" "time" "sneak.berlin/go/pixa/internal/session" ) func TestManager_CreateAndValidate(t *testing.T) { t.Parallel() mgr, err := session.NewManager("test-signing-key-12345") if err != nil { t.Fatalf("NewManager() error = %v", err) } // Create a session w := httptest.NewRecorder() err = mgr.CreateSession(w) if err != nil { t.Fatalf("CreateSession() error = %v", err) } // Extract the cookie from response resp := w.Result() cookies := resp.Cookies() if len(cookies) == 0 { t.Fatal("CreateSession() did not set a cookie") } var sessionCookie *http.Cookie for _, c := range cookies { if c.Name == session.CookieName { sessionCookie = c break } } if sessionCookie == nil { t.Fatalf("CreateSession() did not set cookie named %q", session.CookieName) } // Validate the session req := httptest.NewRequestWithContext(t.Context(), http.MethodGet, "/", nil) req.AddCookie(sessionCookie) data, err := mgr.ValidateSession(req) if err != nil { t.Fatalf("ValidateSession() error = %v", err) } if !data.Authenticated { t.Error("ValidateSession() returned unauthenticated session") } if data.ExpiresAt.Before(time.Now()) { t.Error("ValidateSession() returned already-expired session") } } func TestManager_ValidateSession_NoCookie(t *testing.T) { t.Parallel() mgr, _ := session.NewManager("test-signing-key-12345") req := httptest.NewRequestWithContext(t.Context(), http.MethodGet, "/", nil) _, err := mgr.ValidateSession(req) if err == nil { t.Error("ValidateSession() should fail with no cookie") } if !errors.Is(err, session.ErrNoSession) { t.Errorf("ValidateSession() error = %v, want %v", err, session.ErrNoSession) } } func TestManager_ValidateSession_TamperedCookie(t *testing.T) { t.Parallel() mgr, _ := session.NewManager("test-signing-key-12345") req := httptest.NewRequestWithContext(t.Context(), http.MethodGet, "/", nil) req.AddCookie(&http.Cookie{ Name: session.CookieName, Value: "tampered-invalid-cookie-value", Secure: true, HttpOnly: true, SameSite: http.SameSiteStrictMode, }) _, err := mgr.ValidateSession(req) if err == nil { t.Error("ValidateSession() should fail with tampered cookie") } if !errors.Is(err, session.ErrInvalidSession) { t.Errorf("ValidateSession() error = %v, want %v", err, session.ErrInvalidSession) } } func TestManager_ValidateSession_WrongKey(t *testing.T) { t.Parallel() mgr1, _ := session.NewManager("signing-key-1") mgr2, _ := session.NewManager("signing-key-2") // Create session with mgr1 w := httptest.NewRecorder() _ = mgr1.CreateSession(w) resp := w.Result() var sessionCookie *http.Cookie for _, c := range resp.Cookies() { if c.Name == session.CookieName { sessionCookie = c break } } // Try to validate with mgr2 (different key) req := httptest.NewRequestWithContext(t.Context(), http.MethodGet, "/", nil) req.AddCookie(sessionCookie) _, err := mgr2.ValidateSession(req) if err == nil { t.Error("ValidateSession() should fail with different signing key") } } func TestManager_ClearSession(t *testing.T) { t.Parallel() mgr, _ := session.NewManager("test-signing-key-12345") w := httptest.NewRecorder() mgr.ClearSession(w) resp := w.Result() cookies := resp.Cookies() var sessionCookie *http.Cookie for _, c := range cookies { if c.Name == session.CookieName { sessionCookie = c break } } if sessionCookie == nil { t.Fatal("ClearSession() did not set a cookie") } if sessionCookie.MaxAge != -1 { t.Errorf("ClearSession() cookie MaxAge = %d, want -1", sessionCookie.MaxAge) } } func TestManager_IsAuthenticated(t *testing.T) { t.Parallel() mgr, _ := session.NewManager("test-signing-key-12345") // No session - should return false req := httptest.NewRequestWithContext(t.Context(), http.MethodGet, "/", nil) if mgr.IsAuthenticated(req) { t.Error("IsAuthenticated() should return false with no session") } // Create session w := httptest.NewRecorder() _ = mgr.CreateSession(w) resp := w.Result() var sessionCookie *http.Cookie for _, c := range resp.Cookies() { if c.Name == session.CookieName { sessionCookie = c break } } // With valid session - should return true req = httptest.NewRequestWithContext(t.Context(), http.MethodGet, "/", nil) req.AddCookie(sessionCookie) if !mgr.IsAuthenticated(req) { t.Error("IsAuthenticated() should return true with valid session") } } func TestManager_CookieAttributes(t *testing.T) { t.Parallel() mgr, _ := session.NewManager("test-key") w := httptest.NewRecorder() _ = mgr.CreateSession(w) resp := w.Result() var sessionCookie *http.Cookie for _, c := range resp.Cookies() { if c.Name == session.CookieName { sessionCookie = c break } } if !sessionCookie.HttpOnly { t.Error("Cookie should have HttpOnly flag") } if !sessionCookie.Secure { t.Error("Cookie should have Secure flag when manager created with secure=true") } if sessionCookie.SameSite != http.SameSiteStrictMode { t.Errorf("Cookie SameSite = %v, want %v", sessionCookie.SameSite, http.SameSiteStrictMode) } }