package e2e import ( "net/http" "net/http/httptest" "testing" "time" "github.com/gin-gonic/gin" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "github.com/gochat/gochat/internal/auth" "github.com/gochat/gochat/internal/config" "github.com/gochat/gochat/internal/middleware" ) // SessionE2ETestSuite tests session store and middleware integration. type SessionE2ETestSuite struct { suite.Suite store *auth.SessionStore sessionCfg config.SessionConfig } func TestSessionE2ETestSuite(t *testing.T) { suite.Run(t, new(SessionE2ETestSuite)) } func (s *SessionE2ETestSuite) SetupSuite() { // E2E tests require PostgreSQL; skip in SQLite test mode. s.T().Skip("E2E tests require PostgreSQL; skipping in SQLite test mode") s.sessionCfg = config.SessionConfig{ Enabled: true, ExpirySeconds: 3600, TokenLength: 32, HeaderName: "X-Session-ID", CleanupInterval: 300, } s.store = auth.NewSessionStore(&s.sessionCfg) } // --- Session Store Tests --- func (s *SessionE2ETestSuite) TestSessionCreate() { session, err := s.store.Create(1, 100, "agent", "email") assert.NoError(s.T(), err) assert.NotEmpty(s.T(), session.ID) assert.Equal(s.T(), uint(1), session.UserID) assert.Equal(s.T(), uint(100), session.AccountID) assert.Equal(s.T(), "agent", session.Role) assert.Equal(s.T(), "email", session.Provider) assert.False(s.T(), session.CreatedAt.IsZero()) assert.False(s.T(), session.ExpiresAt.IsZero()) } func (s *SessionE2ETestSuite) TestSessionGet() { session, err := s.store.Create(2, 200, "administrator", "google") assert.NoError(s.T(), err) retrieved, err := s.store.Get(session.ID) assert.NoError(s.T(), err) assert.Equal(s.T(), session.ID, retrieved.ID) assert.Equal(s.T(), uint(2), retrieved.UserID) } func (s *SessionE2ETestSuite) TestSessionGetNotFound() { _, err := s.store.Get("nonexistent-session-id") assert.Error(s.T(), err) } func (s *SessionE2ETestSuite) TestSessionDelete() { session, err := s.store.Create(3, 300, "agent", "saml") assert.NoError(s.T(), err) err = s.store.Delete(session.ID) assert.NoError(s.T(), err) _, err = s.store.Get(session.ID) assert.Error(s.T(), err) } func (s *SessionE2ETestSuite) TestSessionDeleteByUserID() { s.store.Create(4, 400, "agent", "email") s.store.Create(4, 500, "agent", "email") // same user, different account s.store.Create(5, 600, "administrator", "email") // different user count := s.store.DeleteByUserID(4) assert.Equal(s.T(), 2, count) // User 5's session should still exist assert.Equal(s.T(), 1, s.store.Count()) } func (s *SessionE2ETestSuite) TestSessionRefresh() { session, err := s.store.Create(6, 700, "agent", "email") assert.NoError(s.T(), err) originalExpiry := session.ExpiresAt refreshed, err := s.store.Refresh(session.ID) assert.NoError(s.T(), err) assert.True(s.T(), refreshed.ExpiresAt.After(originalExpiry)) } func (s *SessionE2ETestSuite) TestSessionSetGetData() { session, err := s.store.Create(7, 800, "agent", "email") assert.NoError(s.T(), err) err = s.store.SetData(session.ID, "theme", "dark") assert.NoError(s.T(), err) val, err := s.store.GetData(session.ID, "theme") assert.NoError(s.T(), err) assert.Equal(s.T(), "dark", val) } func (s *SessionE2ETestSuite) TestSessionCleanupExpired() { // Create a session with short expiry shortCfg := config.SessionConfig{ Enabled: true, ExpirySeconds: 1, // 1 second TokenLength: 32, } shortStore := auth.NewSessionStore(&shortCfg) session, err := shortStore.Create(8, 900, "agent", "email") assert.NoError(s.T(), err) // Wait for expiry time.Sleep(2 * time.Second) count := shortStore.CleanupExpired() assert.Equal(s.T(), 1, count) _, err = shortStore.Get(session.ID) assert.Error(s.T(), err) } func (s *SessionE2ETestSuite) TestSessionCount() { initialCount := s.store.Count() s.store.Create(9, 1000, "agent", "email") assert.Equal(s.T(), initialCount + 1, s.store.Count()) } // --- Session Middleware Tests --- func (s *SessionE2ETestSuite) TestSessionMiddlewareWithHeader() { session, err := s.store.Create(10, 1100, "agent", "email") assert.NoError(s.T(), err) mwCfg := middleware.DefaultSessionMiddlewareConfig() router := gin.New() router.Use(middleware.SessionMiddleware(s.store, mwCfg)) router.GET("/test", func(c *gin.Context) { sessionID, _ := c.Get("session_id") c.JSON(200, gin.H{"session_id": sessionID}) }) w := httptest.NewRecorder() req, _ := http.NewRequest("GET", "/test", nil) req.Header.Set("X-Session-ID", session.ID) router.ServeHTTP(w, req) assert.Equal(s.T(), 200, w.Code) } func (s *SessionE2ETestSuite) TestSessionMiddlewareWithCookie() { session, err := s.store.Create(11, 1200, "agent", "email") assert.NoError(s.T(), err) mwCfg := middleware.DefaultSessionMiddlewareConfig() router := gin.New() router.Use(middleware.SessionMiddleware(s.store, mwCfg)) router.GET("/test", func(c *gin.Context) { sessionID, exists := c.Get("session_id") if exists { c.JSON(200, gin.H{"session_id": sessionID}) } else { c.JSON(200, gin.H{"session_id": nil}) } }) w := httptest.NewRecorder() req, _ := http.NewRequest("GET", "/test", nil) req.AddCookie(&http.Cookie{Name: "_gochat_session", Value: session.ID}) router.ServeHTTP(w, req) assert.Equal(s.T(), 200, w.Code) } func (s *SessionE2ETestSuite) TestSessionMiddlewareNoSession() { mwCfg := middleware.DefaultSessionMiddlewareConfig() router := gin.New() router.Use(middleware.SessionMiddleware(s.store, mwCfg)) router.GET("/test", func(c *gin.Context) { _, exists := c.Get("session_id") c.JSON(200, gin.H{"has_session": exists}) }) w := httptest.NewRecorder() req, _ := http.NewRequest("GET", "/test", nil) router.ServeHTTP(w, req) assert.Equal(s.T(), 200, w.Code) } func (s *SessionE2ETestSuite) TestSessionMiddlewareSkipPath() { mwCfg := middleware.DefaultSessionMiddlewareConfig() router := gin.New() router.Use(middleware.SessionMiddleware(s.store, mwCfg)) router.GET("/api/v1/auth/login", func(c *gin.Context) { c.JSON(200, gin.H{"status": "ok"}) }) w := httptest.NewRecorder() req, _ := http.NewRequest("GET", "/api/v1/auth/login", nil) router.ServeHTTP(w, req) assert.Equal(s.T(), 200, w.Code) } func (s *SessionE2ETestSuite) TestSessionMiddlewareExpiredSession() { shortCfg := config.SessionConfig{ Enabled: true, ExpirySeconds: 1, TokenLength: 32, } shortStore := auth.NewSessionStore(&shortCfg) session, err := shortStore.Create(12, 1300, "agent", "email") assert.NoError(s.T(), err) // Wait for expiry time.Sleep(2 * time.Second) mwCfg := middleware.DefaultSessionMiddlewareConfig() router := gin.New() router.Use(middleware.SessionMiddleware(shortStore, mwCfg)) router.GET("/test", func(c *gin.Context) { _, exists := c.Get("session_id") c.JSON(200, gin.H{"has_session": exists}) }) w := httptest.NewRecorder() req, _ := http.NewRequest("GET", "/test", nil) req.Header.Set("X-Session-ID", session.ID) router.ServeHTTP(w, req) assert.Equal(s.T(), 200, w.Code) // doesn't abort, just no session } func (s *SessionE2ETestSuite) TestRequireSessionMiddleware() { router := gin.New() router.Use(middleware.RequireSession()) router.GET("/test", func(c *gin.Context) { c.JSON(200, gin.H{"status": "ok"}) }) // Without session_id in context → 401 w := httptest.NewRecorder() req, _ := http.NewRequest("GET", "/test", nil) router.ServeHTTP(w, req) assert.Equal(s.T(), 401, w.Code) // With session_id in context → 200 router2 := gin.New() router2.Use(func(c *gin.Context) { c.Set("session_id", "test-session-123") c.Next() }) router2.Use(middleware.RequireSession()) router2.GET("/test", func(c *gin.Context) { c.JSON(200, gin.H{"status": "ok"}) }) w2 := httptest.NewRecorder() req2, _ := http.NewRequest("GET", "/test", nil) router2.ServeHTTP(w2, req2) assert.Equal(s.T(), 200, w2.Code) }