198 lines
5.1 KiB
Go
198 lines
5.1 KiB
Go
// SPDX-License-Identifier: EUPL-1.2
|
|
|
|
package api_test
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
|
|
"github.com/gin-contrib/sessions"
|
|
"github.com/gin-gonic/gin"
|
|
|
|
api "forge.lthn.ai/core/go-api"
|
|
)
|
|
|
|
// ── Helpers ─────────────────────────────────────────────────────────────
|
|
|
|
// sessionTestGroup provides /sess/set and /sess/get endpoints for session tests.
|
|
type sessionTestGroup struct{}
|
|
|
|
func (s *sessionTestGroup) Name() string { return "sess" }
|
|
func (s *sessionTestGroup) BasePath() string { return "/sess" }
|
|
func (s *sessionTestGroup) RegisterRoutes(rg *gin.RouterGroup) {
|
|
rg.POST("/set", func(c *gin.Context) {
|
|
session := sessions.Default(c)
|
|
session.Set("key", "value")
|
|
session.Save()
|
|
c.JSON(http.StatusOK, api.OK("saved"))
|
|
})
|
|
rg.GET("/get", func(c *gin.Context) {
|
|
session := sessions.Default(c)
|
|
val := session.Get("key")
|
|
c.JSON(http.StatusOK, api.OK(val))
|
|
})
|
|
}
|
|
|
|
// ── WithSessions ────────────────────────────────────────────────────────
|
|
|
|
func TestWithSessions_Good_SetsSessionCookie(t *testing.T) {
|
|
gin.SetMode(gin.TestMode)
|
|
e, _ := api.New(api.WithSessions("session", []byte("test-secret-key!")))
|
|
e.Register(&sessionTestGroup{})
|
|
|
|
h := e.Handler()
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequest(http.MethodPost, "/sess/set", nil)
|
|
h.ServeHTTP(w, req)
|
|
|
|
if w.Code != http.StatusOK {
|
|
t.Fatalf("expected 200, got %d", w.Code)
|
|
}
|
|
|
|
cookies := w.Result().Cookies()
|
|
found := false
|
|
for _, c := range cookies {
|
|
if c.Name == "session" {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
if !found {
|
|
t.Fatal("expected Set-Cookie header with name 'session'")
|
|
}
|
|
}
|
|
|
|
func TestWithSessions_Good_SessionPersistsAcrossRequests(t *testing.T) {
|
|
gin.SetMode(gin.TestMode)
|
|
e, _ := api.New(api.WithSessions("session", []byte("test-secret-key!")))
|
|
e.Register(&sessionTestGroup{})
|
|
|
|
h := e.Handler()
|
|
|
|
// First request: set session value.
|
|
w1 := httptest.NewRecorder()
|
|
req1, _ := http.NewRequest(http.MethodPost, "/sess/set", nil)
|
|
h.ServeHTTP(w1, req1)
|
|
|
|
if w1.Code != http.StatusOK {
|
|
t.Fatalf("set: expected 200, got %d", w1.Code)
|
|
}
|
|
|
|
// Extract the session cookie from the response.
|
|
var sessionCookie *http.Cookie
|
|
for _, c := range w1.Result().Cookies() {
|
|
if c.Name == "session" {
|
|
sessionCookie = c
|
|
break
|
|
}
|
|
}
|
|
if sessionCookie == nil {
|
|
t.Fatal("set: expected session cookie in response")
|
|
}
|
|
|
|
// Second request: get session value, sending the cookie back.
|
|
w2 := httptest.NewRecorder()
|
|
req2, _ := http.NewRequest(http.MethodGet, "/sess/get", nil)
|
|
req2.AddCookie(sessionCookie)
|
|
h.ServeHTTP(w2, req2)
|
|
|
|
if w2.Code != http.StatusOK {
|
|
t.Fatalf("get: expected 200, got %d", w2.Code)
|
|
}
|
|
|
|
var resp api.Response[any]
|
|
if err := json.Unmarshal(w2.Body.Bytes(), &resp); err != nil {
|
|
t.Fatalf("unmarshal error: %v", err)
|
|
}
|
|
|
|
data, ok := resp.Data.(string)
|
|
if !ok || data != "value" {
|
|
t.Fatalf("expected Data=%q, got %v", "value", resp.Data)
|
|
}
|
|
}
|
|
|
|
func TestWithSessions_Good_EmptySessionReturnsNil(t *testing.T) {
|
|
gin.SetMode(gin.TestMode)
|
|
e, _ := api.New(api.WithSessions("session", []byte("test-secret-key!")))
|
|
e.Register(&sessionTestGroup{})
|
|
|
|
h := e.Handler()
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequest(http.MethodGet, "/sess/get", nil)
|
|
h.ServeHTTP(w, req)
|
|
|
|
if w.Code != http.StatusOK {
|
|
t.Fatalf("expected 200, got %d", w.Code)
|
|
}
|
|
|
|
var resp api.Response[any]
|
|
if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil {
|
|
t.Fatalf("unmarshal error: %v", err)
|
|
}
|
|
|
|
if resp.Data != nil {
|
|
t.Fatalf("expected nil Data for empty session, got %v", resp.Data)
|
|
}
|
|
}
|
|
|
|
func TestWithSessions_Good_CombinesWithOtherMiddleware(t *testing.T) {
|
|
gin.SetMode(gin.TestMode)
|
|
e, _ := api.New(
|
|
api.WithSessions("session", []byte("test-secret-key!")),
|
|
api.WithRequestID(),
|
|
)
|
|
e.Register(&sessionTestGroup{})
|
|
|
|
h := e.Handler()
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequest(http.MethodPost, "/sess/set", nil)
|
|
h.ServeHTTP(w, req)
|
|
|
|
if w.Code != http.StatusOK {
|
|
t.Fatalf("expected 200, got %d", w.Code)
|
|
}
|
|
|
|
// Session cookie should be present.
|
|
found := false
|
|
for _, c := range w.Result().Cookies() {
|
|
if c.Name == "session" {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
if !found {
|
|
t.Fatal("expected session cookie")
|
|
}
|
|
|
|
// Request ID should also be present.
|
|
rid := w.Header().Get("X-Request-ID")
|
|
if rid == "" {
|
|
t.Fatal("expected X-Request-ID header from WithRequestID")
|
|
}
|
|
}
|
|
|
|
func TestWithSessions_Ugly_DoubleSessionsDoesNotPanic(t *testing.T) {
|
|
gin.SetMode(gin.TestMode)
|
|
|
|
// Applying WithSessions twice should not panic.
|
|
e, err := api.New(
|
|
api.WithSessions("session", []byte("secret-one-here!")),
|
|
api.WithSessions("session", []byte("secret-two-here!")),
|
|
)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
|
|
e.Register(&sessionTestGroup{})
|
|
|
|
h := e.Handler()
|
|
w := httptest.NewRecorder()
|
|
req, _ := http.NewRequest(http.MethodPost, "/sess/set", nil)
|
|
h.ServeHTTP(w, req)
|
|
|
|
if w.Code != http.StatusOK {
|
|
t.Fatalf("expected 200, got %d", w.Code)
|
|
}
|
|
}
|