package infra import ( "database/sql" "testing" "time" _ "github.com/mattn/go-sqlite3" ) func openSessionTestDB(t *testing.T) *sql.DB { t.Helper() db, err := sql.Open("sqlite3", ":memory:") if err != nil { t.Fatalf("sql.Open: %v", err) } t.Cleanup(func() { db.Close() }) return db } func TestSessionCreate_PersistsSession(t *testing.T) { db := openSessionTestDB(t) s, err := SessionCreate(db, "user-1", time.Hour, map[string]any{"role": "admin"}) if err != nil { t.Fatalf("SessionCreate: %v", err) } if s.Token == "" || len(s.Token) != 64 { t.Errorf("token de largo incorrecto: len=%d", len(s.Token)) } if s.UserID != "user-1" { t.Errorf("UserID = %q", s.UserID) } if s.ExpiresAt <= s.CreatedAt { t.Errorf("ExpiresAt %d <= CreatedAt %d", s.ExpiresAt, s.CreatedAt) } // Verificar persistencia var count int if err := db.QueryRow("SELECT COUNT(*) FROM sessions").Scan(&count); err != nil { t.Fatalf("query: %v", err) } if count != 1 { t.Errorf("esperaba 1 fila, got %d", count) } } func TestSessionCreate_GeneratesDistinctTokens(t *testing.T) { db := openSessionTestDB(t) s1, _ := SessionCreate(db, "u", time.Hour, nil) s2, _ := SessionCreate(db, "u", time.Hour, nil) if s1.Token == s2.Token { t.Fatal("dos sesiones no pueden tener el mismo token") } } func TestSessionCreate_RejectsEmptyUserID(t *testing.T) { db := openSessionTestDB(t) if _, err := SessionCreate(db, "", time.Hour, nil); err == nil { t.Fatal("esperaba error con user_id vacio") } } func TestSessionValidate_ValidSession(t *testing.T) { db := openSessionTestDB(t) s, _ := SessionCreate(db, "user-7", time.Hour, map[string]any{"k": "v"}) got, err := SessionValidate(db, s.Token) if err != nil { t.Fatalf("SessionValidate: %v", err) } if got.UserID != "user-7" { t.Errorf("UserID = %q", got.UserID) } if got.Metadata["k"] != "v" { t.Errorf("metadata[k] = %v", got.Metadata["k"]) } } func TestSessionValidate_MissingToken(t *testing.T) { db := openSessionTestDB(t) _ = sessionEnsureTable(db) if _, err := SessionValidate(db, "nope"); err == nil { t.Fatal("esperaba error con token inexistente") } } func TestSessionValidate_ExpiredSession(t *testing.T) { db := openSessionTestDB(t) _ = sessionEnsureTable(db) // Insertar manualmente una sesion ya expirada past := time.Now().Unix() - 60 _, err := db.Exec( "INSERT INTO sessions (token, user_id, expires_at, created_at, metadata) VALUES (?, ?, ?, ?, ?)", "expired-tok", "u", past, past-100, "{}", ) if err != nil { t.Fatalf("insert: %v", err) } if _, err := SessionValidate(db, "expired-tok"); err == nil { t.Fatal("esperaba error con sesion expirada") } } func TestSessionCleanup_RemovesOnlyExpired(t *testing.T) { db := openSessionTestDB(t) active, _ := SessionCreate(db, "active-user", time.Hour, nil) _ = sessionEnsureTable(db) past := time.Now().Unix() - 60 _, _ = db.Exec( "INSERT INTO sessions (token, user_id, expires_at, created_at, metadata) VALUES (?, ?, ?, ?, ?)", "expired", "u", past, past, "{}", ) n, err := SessionCleanup(db) if err != nil { t.Fatalf("SessionCleanup: %v", err) } if n != 1 { t.Errorf("esperaba 1 eliminada, got %d", n) } // La activa sigue ahi if _, err := SessionValidate(db, active.Token); err != nil { t.Errorf("sesion activa no deberia haberse borrado: %v", err) } }