package auth import ( "context" "path/filepath" "strings" "testing" "polar/internal/db" ) func TestPasswordHashAndVerify(t *testing.T) { if _, err := HashPassword("short"); err == nil { t.Fatalf("expected error for short password") } hash, err := HashPassword("correct-horse-battery-staple") if err != nil { t.Fatalf("HashPassword failed: %v", err) } if !strings.HasPrefix(hash, "$argon2id$") { t.Fatalf("expected argon2id PHC format, got %q", hash) } ok, err := VerifyPassword("correct-horse-battery-staple", hash) if err != nil || !ok { t.Fatalf("VerifyPassword failed for valid password: %v", err) } ok, err = VerifyPassword("wrong-password", hash) if err != nil || ok { t.Fatalf("VerifyPassword should return false for wrong password") } } func TestSessionsAndTokens(t *testing.T) { ctx := context.Background() database, err := db.Open(ctx, filepath.Join(t.TempDir(), "auth.db"), true) if err != nil { t.Fatalf("open db: %v", err) } defer database.Close() _, err = database.ExecContext(ctx, `INSERT INTO users (id, username, email, password_hash, is_admin) VALUES (1, 'alice', 'alice@example.com', 'hash', 1)`) if err != nil { t.Fatalf("insert test user: %v", err) } svc := NewService(database) sess, err := svc.CreateSession(ctx, 1) if err != nil { t.Fatalf("CreateSession failed: %v", err) } if len(sess.ID) != 64 || len(sess.CSRFToken) < 32 { t.Fatalf("unexpected session ID or CSRF token length") } loaded, err := svc.GetSession(ctx, sess.ID) if err != nil || loaded.UserID != 1 { t.Fatalf("GetSession failed: %v", err) } if err := svc.DeleteSession(ctx, sess.ID); err != nil { t.Fatalf("DeleteSession failed: %v", err) } if _, err := svc.GetSession(ctx, sess.ID); err == nil { t.Fatalf("expected error after session deletion") } rawToken, tok, err := svc.CreateToken(ctx, 1, "Laptop CLI") if err != nil { t.Fatalf("CreateToken failed: %v", err) } if !strings.HasPrefix(rawToken, "polar_") { t.Fatalf("expected polar_ prefix, got %q", rawToken) } if tok.Name != "Laptop CLI" { t.Fatalf("unexpected token name: %q", tok.Name) } uid, err := svc.VerifyToken(ctx, rawToken) if err != nil || uid != 1 { t.Fatalf("VerifyToken failed: uid=%d err=%v", uid, err) } tokens, err := svc.ListTokens(ctx, 1) if err != nil || len(tokens) != 1 { t.Fatalf("ListTokens failed: len=%d err=%v", len(tokens), err) } if tokens[0].LastUsedAt == nil { t.Fatalf("expected LastUsedAt to be updated after VerifyToken") } if err := svc.RevokeToken(ctx, 1, tok.ID); err != nil { t.Fatalf("RevokeToken failed: %v", err) } if _, err := svc.VerifyToken(ctx, rawToken); err == nil { t.Fatalf("expected error for revoked token") } }