gohorsejobs/docs/TEST_USERS.md
Tiago Yamamoto 222006fb6b docs: rewrite TEST_USERS.md with current credentials and bug post-mortems
- Correct all passwords (were wrong/outdated)
- Add full table of current seeded users (superadmin, admins, candidates)
- Document 3 bugs fixed 2026-02-24:
  - superadmin placeholder hash (seeder fix)
  - users.status VARCHAR(20) crash loop (migration fix)
  - PASSWORD_PEPPER alignment requirement
- Add bash ! expansion gotcha for curl testing

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-24 05:55:10 -06:00

5.4 KiB

🧪 Test Users & Credentials — GoHorseJobs

All accounts below are provisioned by seeder-api against the DEV environment (local.gohorsejobs.com / api-local.gohorsejobs.com).

Last verified: 2026-02-24 Environment: Coolify DEV (Redbull VPS) Auth endpoint: POST https://api-local.gohorsejobs.com/api/v1/auth/login


Login Payload Format

{ "email": "<identifier OR email>", "password": "<password>" }

The "email" JSON key accepts both the username (identifier) and the actual e-mail address. The backend resolves WHERE email = $1 OR identifier = $1.


👑 SuperAdmin

Identifier Email Password Role
lol lol@gohorsejobs.com Admin@2025! superadmin
superadmin admin@gohorsejobs.com Admin@2025! superadmin

Both identifiers grant full system access. lol is the primary dev account; superadmin is the alias created by migration 010.


🏢 Admins / Recruiters

Identifier Email Password Role Company
takeshi_yamamoto takeshi@techcorp.com Takeshi@2025 admin TechCorp
kenji kenji@appmakers.mobile Takeshi@2025 admin AppMakers
maria_santos maria@designhub.com User@2025 recruiter DesignHub
wile_e_coyote wile@acme.corp MeepMeep@123 admin ACME Corporation

🙋 Candidates

Unified Users (identifier-based login)

Identifier Email Password Name
paulo_santos paulo@email.com User@2025 Paulo Santos
maria_email maria@email.com User@2025 Maria Reyes

Legacy Candidates (email-based login)

Email Password Name
ana.silva@example.com User@2025 Ana Silva
carlos.santos@example.com User@2025 Carlos Santos
maria.oliveira@example.com User@2025 Maria Oliveira
pedro.costa@example.com User@2025 Pedro Costa
juliana.ferreira@example.com User@2025 Juliana Ferreira

🌱 Seeding & Reset

# Reset all data (drops and recreates all public tables)
curl -X POST https://s-local.gohorsejobs.com/reset

# Re-seed everything (locations, users, companies, jobs, applications)
curl https://s-local.gohorsejobs.com/seed/stream

# After reset+seed, the backend must be reachable so migrations have already run.
# The seeder does NOT apply Go migrations — they run on backend startup.

Order matters after a full DB wipe:

  1. Wait for the backend container to finish running migrations (check GET /api/v1/jobs returns [])
  2. Then run POST /resetGET /seed/stream

⚠️ Known Issues / Gotchas

1. superadmin login broken after DB reset (FIXED 2026-02-24)

Bug: Migration 010_seed_super_admin.sql creates the superadmin user with a hardcoded placeholder hash ($invalid-placeholder-run-seeder$). The seeder previously only updated lol's hash, leaving superadmin permanently unable to log in after a DB reset.

Root cause: ON CONFLICT DO NOTHING in migration 010 means the user exists in the DB but with an unusable hash. The seeder's upsert only covered the lol identifier.

Fix (commit 6b1c058): seeder-api/src/seeders/users.js now also upserts superadmin with the same runtime-generated hash as lol:

// Uses the same bcrypt(pass + PASSWORD_PEPPER) hash already computed for lol
await pool.query(`
    INSERT INTO users (identifier, password_hash, ...) VALUES ('superadmin', $1, ...)
    ON CONFLICT (identifier) DO UPDATE SET password_hash = EXCLUDED.password_hash, status = 'active'
`, [superAdminHash]);

Affected versions: All environments before 6b1c058. Triggered on every fresh DB seed.


2. users.status column too short — backend crash loop on startup (FIXED 2026-02-24)

Bug: Migration 009_unify_schema.sql defined status VARCHAR(20). Migration 010_seed_super_admin.sql tried to insert 'force_change_password' (21 chars), crashing the backend with pq: value too long for type character varying(20) on every restart.

Fix (commit e5e4397):

  • 009: VARCHAR(20)VARCHAR(30)
  • 010: initial status changed from 'force_change_password''pending'
  • New migration 046_fix_user_status_varchar.sql: ALTER TABLE users ALTER COLUMN status TYPE VARCHAR(30) for existing deployments

3. PASSWORD_PEPPER must match between seeder and backend

All password hashes are stored as bcrypt(password + PASSWORD_PEPPER). Both the seeder-api and backend must have the same PASSWORD_PEPPER env var, or all logins return 401 invalid credentials.

DEV value: gohorse-pepper Confirmed in: Coolify backend env + Coolify seeder env

If logins break after a deploy: check docker inspect <backend_container> --format '{{.Config.Env}}' for PASSWORD_PEPPER, then re-run the seeder.


4. Bash ! expansion breaks login tests via curl

When testing logins from the shell, passwords containing ! (e.g. Admin@2025!) must be in a file or use $'...' syntax. Using "Admin@2025!" in double-quotes causes bash history expansion (\!), producing invalid JSON and a 400 Invalid Request.

# WRONG (bash expands !)
curl ... -d '{"email":"lol","password":"Admin@2025!"}'  # OK in single quotes
curl ... -d "{\"email\":\"lol\",\"password\":\"Admin@2025!\"}"  # BROKEN

# CORRECT — use a file
cat > /tmp/login.json << 'EOF'
{"email":"lol","password":"Admin@2025!"}
EOF
curl -X POST .../auth/login -d @/tmp/login.json