Test Strategy
Per-phase test gates. No phase exits without these green.
Test pyramid (per repo)
| Layer | Curva | Pasukuru BE | Pasukuru FE |
|---|---|---|---|
| Unit | Pest unit (Services, Builders) | Jest unit (Services) | Vitest unit (utils) |
| Integration | Pest feature (HTTP) | Jest e2e (HTTP+DB) | Playwright (cmp+API) |
| End-to-end | Curva → Pasukuru via real HTTP | Same | Cypress/Playwright LIFF (mock SDK) |
| Manual | Admin flows in browser | Postman / curl scripts | LINE app on real device |
Phase 0 test gates
| Test | Layer | Required for | Status |
|---|---|---|---|
| Migration up + down | unit | 0.P-1 | — |
| Identify endpoint mocked LINE response | jest e2e | (Phase 1, but stub Phase 0) | — |
| Order event emit on status change | jest unit | 0.P-4 | — |
| Order builder per event | jest unit | 0.P-5 | — |
| HMAC signing canonical string | jest unit | 0.P-7 | — |
| HMAC verify rejects bad sig | pest feature | 0.C-2 | — |
| Webhook receiver persists log | pest feature | 0.C-1 | — |
| Replay nonce rejected | pest feature | 0.C-2 | — |
| Smoke: pasukuru → curva order roundtrip | manual | Phase 0 exit | — |
Phase 1 test gates
| Test | Layer | Required for |
|---|---|---|
| LIFF init mocked in jsdom | vitest | 1.F-2 |
| useLiff returns expected shape | vitest | 1.F-3 |
| Identify returns JWT | jest e2e | 1.P-2 |
| JWT shop_token resolves tenant | jest e2e | 1.P-9 |
| Auto-identify happens once | playwright | 1.F-5 |
| Stripe checkout works in LIFF | manual | 1.F-7 |
| Smoke: LINE friend → LIFF → buy | manual | Phase 1 exit |
Phase 2 test gates
| Test | Layer |
|---|---|
| Product cache event handlers | pest unit |
| Sync command idempotent | pest feature |
| Picker generates valid Flex JSON | playwright |
| RichMenu LIFF dropdown saves correct URI | pest |
| Receipt Flex builds per event type | pest unit |
| Abandoned cart trigger fires + cancels | pest with time mock |
Phase 3 test gates
| Test | Layer |
|---|---|
| LINE Pay sandbox confirm | jest e2e |
| Agent QR scan → attribution | manual |
| Order history page renders past orders | playwright |
| LIFF chrome polish on iOS+Android | manual device |
Continuous tests (every phase)
- Lint clean (eslint, phpcs, eslint-fe)
- Type-check clean (tsc, larastan)
- Existing test suites pass (no regression)
- Coverage doesn’t drop below current baseline
CI gates (HARD RULE — global policy)
All CI jobs MUST be green before merge. If even ONE job red → blocked.
Specifically:
- Curva: PHP 8.2 unit + feature, larastan, php-cs-fixer, prettier (vue), eslint
- Pasukuru BE: Jest, eslint, tsc
- Pasukuru FE: Vitest, eslint, tsc, build
Per concepts/ci-cd-must-pass-before-merge.md.
Smoke test catalog
SMOKE-0 (Phase 0 exit)
“Order webhook reaches Curva from Pasukuru”
Steps in 02-phase-0-foundation.md smoke test section.
SMOKE-1 (Phase 1 exit)
“User completes purchase inside LINE LIFF”
Steps in 03-phase-1-liff-wrap.md.
SMOKE-2 (Phase 2 exit)
“Admin builds Flex Msg → user buys → receipt arrives”
Steps in 04-phase-2-ux-glue.md.
SMOKE-3 (Phase 3 exit)
“Agent QR drives attributed purchase via LINE Pay”
Steps in 05-phase-3-payment-agent.md.
SMOKE-PROD (Pre-launch)
All SMOKE-0 through SMOKE-3 against prod-staging tenant. Plus:
- 1000-user load
- Webhook DLQ < 5 across 1hr observation
- No 5xx in nginx access log over 1hr
Test environment matrix
| Env | Purpose | Auto-deployed? |
|---|---|---|
| local (dev VPS) | per-dev hacking | manual |
| staging | smoke tests, integration | on dev branch merge |
| canary | first-touch tenants | promotion from staging |
| prod | live | promotion + manual approval |
Test data fixtures
Maintained in vault pages/concepts/curva-pasukuru-test-fixtures.md:
- 5 test products
- 3 test members (one with LINE-linked, one without, one as agent)
- 1 test PartnerConnection
- Sample order in each status