diff --git a/docs/TEST_USERS.md b/docs/TEST_USERS.md index 1502c57..5b0b92e 100644 --- a/docs/TEST_USERS.md +++ b/docs/TEST_USERS.md @@ -1,55 +1,140 @@ -# ๐Ÿงช Test Users & Data Scenarios - GoHorseJobs +# ๐Ÿงช Test Users & Credentials โ€” GoHorseJobs -When running the platform locally (via `start.sh`) or on the DEV server (`local.gohorsejobs.com`), the `seeder-api` provisions a rich set of test accounts representing distinct personas. +All accounts below are provisioned by `seeder-api` against the DEV environment (`local.gohorsejobs.com` / `api-local.gohorsejobs.com`). -All accounts use the exact same password to simplify development. - -**Universal Test Password:** `Admin@2025!` +> **Last verified:** 2026-02-24 +> **Environment:** Coolify DEV (Redbull VPS) +> **Auth endpoint:** `POST https://api-local.gohorsejobs.com/api/v1/auth/login` --- -## ๐Ÿ‘ฅ Core Personas (The "Golden" Accounts) +## Login Payload Format -These are the primary accounts you should use for daily development and E2E testing. - -| Role | Identifier (Username) | Email | Purpose | -| :--- | :--- | :--- | :--- | -| **Superadmin** | `lol` | `lol@gohorsejobs.com` | Complete system access. Bypass company walls. Manage features. | -| **Superadmin** | `superadmin` | `admin@gohorsejobs.com` | Secondary global admin. | -| **Admin** | `admin` | `moderator@gohorsejobs.com` | Reviews pending companies/jobs in backoffice. | -| **Recruiter** | `recruiter` | `hr@techcorp.com` | Posts jobs, manages applicants for "TechCorp" (Company ID: 1). | -| **Recruiter** | `jane_hr` | `jane.doe@startup.io` | Recruiter for a pending/unapproved company to test gating. | -| **Candidate** | `candidate` | `carlos.dev@gmail.com` | Standard job seeker. Pre-filled resume and skills. Has 3 active applications. | -| **Candidate** | `newbie` | `new.user@hotmail.com` | Fresh account with 0 applications and empty profile to test onboarding flows. | - ---- - -## ๐Ÿข Auto-Generated Companies & Jobs - -The DB seeder simulates a healthy marketplace. -If you run `npm run seed:lite`, the DB receives: -* **50 Companies**: Ranging from "TechCorp" (Active) to "SuspiciousLTDA" (Pending/Rejected). -* **100 Jobs**: Distributed among the active companies. Various statuses (`published`, `draft`, `closed`). -* **200 Applications**: Dummy candidates applying to random jobs, in varied pipelines (`pending`, `reviewing`, `accepted`). - ---- - -## ๐Ÿšจ Login & Auth "Gotchas" - -### 1. `Invalid Credentials` Right After Fresh Seed -If you completely reset the DB using `start.sh` (Option 6) or manually clear Postgres, and your first login returns "Invalid credentials", **do not panic and do not manually change the DB hash**. -* **Cause**: The Node.js seeder calculates the bcrypt hash using an environment variable called `PASSWORD_PEPPER` (`gohorse-pepper`). If this variable was empty or mismatched with the Backend API, the hash is physically wrong. -* **Fix**: Ensure your `.env` files in both `backend` and `seeder-api` contain `PASSWORD_PEPPER=gohorse-pepper`. Then simply run `cd seeder-api && npm run seed` again to fix the hashes. - -### 2. Login Payload Fields -The frontend sends: ```json -{ - "email": "lol", - "password": "..." -} +{ "email": "", "password": "" } ``` -**Important:** Even though the frontend passes the username `lol`, the JSON key **must** be `"email"`. The Go backend natively handles resolving `"email"` against the `identifier` OR `email` database columns. -### 3. Account Status Flags -The `users` table has an `is_active` boolean. If a user is manually deactivated by an admin, the login endpoint will return a generic `401 Unauthorized` (or specific Account Locked error depending on the exact route version) to prevent user enumeration. +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 + +```bash +# 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 /reset` โ†’ `GET /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`: +```js +// 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 --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`. + +```bash +# 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 +```