Merge pull request #56 from cartsnitch/feat/uat-seed-user

feat: add dedicated UAT seed user with known credentials
This commit is contained in:
cartsnitch-ceo[bot]
2026-03-29 21:57:26 +00:00
committed by GitHub
3 changed files with 90 additions and 0 deletions
+28
View File
@@ -0,0 +1,28 @@
# CartSnitch Common
Shared models, schemas, and utilities for CartSnitch services.
## Test Users
The following users are seeded by `cartsnitch-seed` and can be used for local development and UAT.
| Email | Password | Display Name | Notes |
|---|---|---|---|
| `uat@cartsnitch.com` | `CartSnitch-UAT-2026!` | UAT Tester | Primary UAT account. Use for regression testing in the CartSnitch frontend. Created by the seed runner via Better-Auth's bcrypt path — credentials work against the live auth service. Idempotent; re-running the seed skips this user if it already exists. |
### Running the Seed
```bash
# Install with seed dependencies
pip install -e "cartsnitch-common[seed]"
# Run (requires CARTSNITCH_DATABASE_URL_SYNC)
CARTSNITCH_DATABASE_URL_SYNC=postgresql://user:pass@localhost:5432/cartsnitch \
cartsnitch-seed
```
### Architecture
- **Models** live in `src/cartsnitch_common/models/`
- **Alembic migrations** run via the `api` service (`api/alembic/`)
- **Seed runner** runs via `cartsnitch-seed` (installed as a package entry point)
+1
View File
@@ -27,6 +27,7 @@ dev = [
] ]
seed = [ seed = [
"faker>=33.0,<34.0", "faker>=33.0,<34.0",
"bcrypt>=4.0,<6.0",
] ]
[project.scripts] [project.scripts]
@@ -2,8 +2,10 @@
import random import random
import time import time
import uuid
from typing import Any from typing import Any
import bcrypt
from faker import Faker from faker import Faker
from sqlalchemy import text from sqlalchemy import text
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
@@ -184,6 +186,65 @@ def run_seed(
session.commit() session.commit()
_seed_uat_user(session)
elapsed = time.monotonic() - t0 elapsed = time.monotonic() - t0
_log("") _log("")
_log(f"Seed complete in {elapsed:.1f}s") _log(f"Seed complete in {elapsed:.1f}s")
# ---------------------------------------------------------------------------
# UAT seed user
# ---------------------------------------------------------------------------
UAT_EMAIL = "uat@cartsnitch.com"
UAT_PASSWORD = "CartSnitch-UAT-2026!"
UAT_DISPLAY_NAME = "UAT Tester"
UAT_USER_ID = uuid.UUID("00000000-0000-0000-0000-000000000001")
def _seed_uat_user(session: Session) -> None:
"""Insert or verify the dedicated UAT test user.
The user is created via Better-Auth's bcrypt hashing path so credentials
work against the live auth service. Idempotent — skips if the user already
exists.
"""
existing = session.execute(
text("SELECT id FROM users WHERE email = :email"),
{"email": UAT_EMAIL},
).fetchone()
if existing is not None:
_log(f"UAT user {UAT_EMAIL} already exists — skipping")
return
password_hash = bcrypt.hashpw(UAT_PASSWORD.encode(), bcrypt.gensalt()).decode()
session.execute(
text(
"INSERT INTO users (id, email, hashed_password, display_name, email_verified, created_at, updated_at) "
"VALUES (:id, :email, :hashed_password, :display_name, true, now(), now())"
),
{
"id": str(UAT_USER_ID),
"email": UAT_EMAIL,
"hashed_password": password_hash,
"display_name": UAT_DISPLAY_NAME,
},
)
session.execute(
text(
"INSERT INTO accounts (id, user_id, account_id, provider_id, password, created_at, updated_at) "
"VALUES (gen_random_uuid()::text, :user_id, :account_id, 'credential', :password, now(), now())"
),
{
"user_id": str(UAT_USER_ID),
"account_id": str(UAT_USER_ID),
"password": password_hash,
},
)
session.commit()
_log(f"UAT user {UAT_EMAIL} created")