@@ -441,6 +441,133 @@ func TestNewStore_WithURL(t *testing.T) {
441441 })
442442}
443443
444+ // TestSessionCookieMaxAgeZero tests that MaxAge == 0 creates a session cookie
445+ // (no Max-Age attribute) and saves to Redis with DefaultMaxAge TTL.
446+ // This is a regression test for issue #53.
447+ func TestSessionCookieMaxAgeZero (t * testing.T ) {
448+ addr := setup ()
449+ store := createTestStore (t , addr )
450+ defer func () {
451+ if err := store .Close (); err != nil {
452+ fmt .Printf ("Error closing store: %v\n " , err )
453+ }
454+ }()
455+
456+ var cookies []string
457+
458+ // Round 1: Create a session with MaxAge = 0 (session cookie)
459+ t .Run ("Create session cookie with MaxAge=0" , func (t * testing.T ) {
460+ req , _ := http .NewRequestWithContext (
461+ context .Background (), "GET" , "http://localhost:8080/" , nil )
462+ rsp := NewRecorder ()
463+ session := getSession (t , store , req )
464+
465+ // Set MaxAge to 0 to create a session cookie
466+ session .Options .MaxAge = 0
467+ session .Values ["user" ] = "testuser"
468+ session .Values ["authenticated" ] = true
469+
470+ saveSession (t , req , rsp )
471+ cookies = getCookies (t , rsp )
472+
473+ // Verify cookie is set
474+ if len (cookies ) == 0 {
475+ t .Fatal ("Expected cookie to be set" )
476+ }
477+
478+ // Verify the cookie doesn't contain an explicit Max-Age=0
479+ // (session cookies should not have Max-Age attribute set to 0)
480+ cookieStr := cookies [0 ]
481+ if bytes .Contains ([]byte (cookieStr ), []byte ("Max-Age=0" )) {
482+ t .Errorf ("Session cookie should not have Max-Age=0, got: %s" , cookieStr )
483+ }
484+
485+ // Verify session was saved to Redis by checking if we can retrieve it
486+ conn := store .Pool .Get ()
487+ defer conn .Close ()
488+
489+ // Get the session ID from the session
490+ if session .ID == "" {
491+ t .Fatal ("Session ID should not be empty after save" )
492+ }
493+
494+ // Check if the key exists in Redis
495+ exists , err := conn .Do ("EXISTS" , store .keyPrefix + session .ID )
496+ if err != nil {
497+ t .Fatalf ("Error checking Redis key existence: %v" , err )
498+ }
499+ if exists == int64 (0 ) {
500+ t .Error ("Session should be saved to Redis when MaxAge=0" )
501+ }
502+
503+ // Verify the TTL is set to DefaultMaxAge (not 0)
504+ ttl , err := conn .Do ("TTL" , store .keyPrefix + session .ID )
505+ if err != nil {
506+ t .Fatalf ("Error getting TTL from Redis: %v" , err )
507+ }
508+ ttlInt := ttl .(int64 )
509+ if ttlInt <= 0 {
510+ t .Errorf ("Expected positive TTL (DefaultMaxAge), got %d" , ttlInt )
511+ }
512+ // TTL should be close to DefaultMaxAge (1200 seconds / 20 minutes)
513+ // Allow some margin for test execution time
514+ if ttlInt < 1190 || ttlInt > 1200 {
515+ t .Errorf ("Expected TTL close to DefaultMaxAge (1200), got %d" , ttlInt )
516+ }
517+ })
518+
519+ // Round 2: Verify the session can be retrieved
520+ t .Run ("Retrieve session cookie" , func (t * testing.T ) {
521+ req , _ := http .NewRequestWithContext (
522+ context .Background (), "GET" , "http://localhost:8080/" , nil )
523+ req .Header .Add ("Cookie" , cookies [0 ])
524+ session := getSession (t , store , req )
525+
526+ // Verify session is not new (it was loaded from Redis)
527+ if session .IsNew {
528+ t .Error ("Session should not be new, it should be loaded from Redis" )
529+ }
530+
531+ // Verify session values are preserved
532+ user , ok := session .Values ["user" ]
533+ if ! ok || user != "testuser" {
534+ t .Errorf ("Expected user='testuser', got %v" , user )
535+ }
536+
537+ authenticated , ok := session .Values ["authenticated" ]
538+ if ! ok || authenticated != true {
539+ t .Errorf ("Expected authenticated=true, got %v" , authenticated )
540+ }
541+ })
542+
543+ // Round 3: Verify MaxAge < 0 deletes the session (existing behavior)
544+ t .Run ("Delete session with MaxAge=-1" , func (t * testing.T ) {
545+ req , _ := http .NewRequestWithContext (
546+ context .Background (), "GET" , "http://localhost:8080/" , nil )
547+ req .Header .Add ("Cookie" , cookies [0 ])
548+ rsp := NewRecorder ()
549+ session := getSession (t , store , req )
550+
551+ sessionID := session .ID
552+
553+ // Set MaxAge to -1 to delete the session
554+ session .Options .MaxAge = - 1
555+ saveSession (t , req , rsp )
556+
557+ // Verify session was deleted from Redis
558+ conn := store .Pool .Get ()
559+ defer conn .Close ()
560+
561+ exists , err := conn .Do ("EXISTS" , store .keyPrefix + sessionID )
562+ if err != nil {
563+ t .Fatalf ("Error checking Redis key existence: %v" , err )
564+ }
565+ if exists != int64 (0 ) {
566+ t .Error ("Session should be deleted from Redis when MaxAge=-1" )
567+ }
568+ })
569+ }
570+
444571func ExampleRediStore () {
445572 // RedisStore
446573 store , err := NewStore (
0 commit comments