All checks were successful
check / check (push) Successful in 1m48s
Per review on PR #48: pre-1.0 there is no installed base to keep compiling against, so remove the dead parameter in one pass instead of deferring. NewManager now takes only the signing key. Update the handlers.go call site and its stale comment, mechanically update the NewManager call shapes in the session tests (assertions unchanged), collapse the now-meaningless constructor-argument loop in the cookie attributes test, and drop the moot P1 Future Step from TODO.md. Reviewer-directed test call-site updates; cookie behavior is unchanged from the previous commit (always Secure/HttpOnly/SameSite=Strict).
204 lines
4.5 KiB
Go
204 lines
4.5 KiB
Go
package session
|
|
|
|
import (
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestManager_CreateAndValidate(t *testing.T) {
|
|
mgr, err := NewManager("test-signing-key-12345")
|
|
if err != nil {
|
|
t.Fatalf("NewManager() error = %v", err)
|
|
}
|
|
|
|
// Create a session
|
|
w := httptest.NewRecorder()
|
|
if err := mgr.CreateSession(w); 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 == CookieName {
|
|
sessionCookie = c
|
|
break
|
|
}
|
|
}
|
|
|
|
if sessionCookie == nil {
|
|
t.Fatalf("CreateSession() did not set cookie named %q", CookieName)
|
|
}
|
|
|
|
// Validate the session
|
|
req := httptest.NewRequest(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) {
|
|
mgr, _ := NewManager("test-signing-key-12345")
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "/", nil)
|
|
|
|
_, err := mgr.ValidateSession(req)
|
|
if err == nil {
|
|
t.Error("ValidateSession() should fail with no cookie")
|
|
}
|
|
|
|
if err != ErrNoSession {
|
|
t.Errorf("ValidateSession() error = %v, want %v", err, ErrNoSession)
|
|
}
|
|
}
|
|
|
|
func TestManager_ValidateSession_TamperedCookie(t *testing.T) {
|
|
mgr, _ := NewManager("test-signing-key-12345")
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "/", nil)
|
|
req.AddCookie(&http.Cookie{
|
|
Name: CookieName,
|
|
Value: "tampered-invalid-cookie-value",
|
|
})
|
|
|
|
_, err := mgr.ValidateSession(req)
|
|
if err == nil {
|
|
t.Error("ValidateSession() should fail with tampered cookie")
|
|
}
|
|
|
|
if err != ErrInvalidSession {
|
|
t.Errorf("ValidateSession() error = %v, want %v", err, ErrInvalidSession)
|
|
}
|
|
}
|
|
|
|
func TestManager_ValidateSession_WrongKey(t *testing.T) {
|
|
mgr1, _ := NewManager("signing-key-1")
|
|
mgr2, _ := 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 == CookieName {
|
|
sessionCookie = c
|
|
break
|
|
}
|
|
}
|
|
|
|
// Try to validate with mgr2 (different key)
|
|
req := httptest.NewRequest(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) {
|
|
mgr, _ := 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 == 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) {
|
|
mgr, _ := NewManager("test-signing-key-12345")
|
|
|
|
// No session - should return false
|
|
req := httptest.NewRequest(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 == CookieName {
|
|
sessionCookie = c
|
|
break
|
|
}
|
|
}
|
|
|
|
// With valid session - should return true
|
|
req = httptest.NewRequest(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) {
|
|
mgr, _ := NewManager("test-key")
|
|
|
|
w := httptest.NewRecorder()
|
|
_ = mgr.CreateSession(w)
|
|
|
|
resp := w.Result()
|
|
var sessionCookie *http.Cookie
|
|
for _, c := range resp.Cookies() {
|
|
if c.Name == 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)
|
|
}
|
|
}
|