progress/testing.md

Tổng hợp Test Inventory

Snapshot lần ăn khớp với code thực tế. Cập nhật mỗi khi test counts thay đổi đáng kể. Xem features.md cho phạm vi feature, changelog.md cho lịch sử release.

Cập nhật lần cuối: 2026-05-05 (coverage audit lần đầu + branding.resolver.spec.ts lấp 0% gap)

Coverage snapshot 2026-05-05 (yarn test:cov từ root booking-api):

  • Lines 83.66 % · Statements 82.33 % · Functions 78.32 % · Branches 68.81 %
  • 0 % files (top gaps): core/email/{branding,booking-payload,branded-email,recipient}.{resolver,builder,processor}.ts, core/booking/on-{booking,payment}-email.listener.ts, auth-customer/auth-customer.{controller,service}.ts, common/filters/http-exception.filter.ts, core/loyalty/.../prisma-loyalty-redemption.repository.ts.
  • Đã đóng: core/email/branding.resolver.ts (0 % → 100 % L · 89 % B · 100 % F) bằng branding.resolver.spec.ts (+21 tests covering tenant-not-found, ConfigService default, salonUrl trailing-slash strip, logo signing, branding fallbacks, address formatting, locale inference). Suite: 1577 → 1596 pass.
  • Web: Vitest 4 chưa cấu hình @vitest/coverage-v8 → cần yarn add -D @vitest/coverage-v8 + coverage: { provider: 'v8' } trong vitest.config.ts mới đọc được số. Files mới ship 2026-05-02 (BrandingSection, FocalPointPicker, page.tsx, HomeContent.tsx, InvoiceClient.tsx) đều chưa có spec.

Tổng quan

Repo Framework Files Tests Pass Skip Fail Wall time (local)
booking-api Jest (unit + integration) 112 1332 1332 0 0 ~6 s
booking-api Jest (e2e, real DB + Redis) 3 59 59 0 0 ~45 s
booking-web Vitest (unit + component) 18 137 137 0 0 ~4 s
booking-web Playwright (E2E smoke + regression) 4 25 24 0 1 fixme ~34 s
booking-web Playwright (@integration, live PSP) 1 1 1 ‡
booking-mobile 0 0 0 0 0
Tổng (repo-wide, non-integration) 138 1553 1552 1 0

payment-settings-integration.spec.ts: hit live Bambora sandbox, gate bằng env E2E_BAMBORA_*, mặc định test.skip() ở local.


Lệnh chạy

Chạy từ root của mỗi repo (yarn-only).

# booking-api — Jest
yarn test                     # unit + integration, ~6s
yarn test:e2e                 # e2e against docker-compose DB + Redis
yarn test:cov                 # coverage report

# booking-web — Vitest + Playwright
yarn test                     # vitest (unit/component), ~4s
yarn test:watch               # vitest watch mode
yarn test:e2e                 # playwright, exclude @integration
yarn test:e2e:integration     # playwright, chỉ @integration (cần live PSP)
yarn test:e2e:ui              # playwright UI mode

booking-api — Jest

Unit + integration (yarn test)

Coverage chia theo bounded context / module. Số đếm dưới đây gom từ *.spec.ts thực tế trong src/**.

Khu vực Suites Tests Notes
Auth (login, refresh, guards, password reset) 4 38 Token versioning, tenant isolation, multi-tenant AUTH_TENANT_REQUIRED, rate-limit guard
Tenant + Onboarding 6 26 Settings merge, slug uniqueness, industry resolution, validateSettingsCombination (P1-5/P1-11)
Resource management 9 32 Schedule, time-off, skill assignment, soft-delete, STAFF self time-off (P3 audit)
Service catalog 7 26 Categories, VAT, active-flag, skill links
Booking engine (multi-service + status) 14 67 Conflict detection, cross-item availability, snapshot on create, P1-1/P1-3/P1-8 cancel-preview, deposit-required guard P0-2
Availability calculator 5 20 Business hours, buffer, time-off overlap, weekday edge cases
Public booking API 6 25 Slug resolution, guest path, resource filter, skill scoping, P1-1 self-cancel, P1-4 retry endpoint
TenantCustomer 4 12 Dedup phone/email, per-tenant scope, STAFF whitelist (notes/tags)
Loyalty (stamps + points) 8 28 Discount compute, redemption ledger, idempotency
Loyalty Track L2–L3 (DDD layered) 5 58 computeLoyaltyDiscount pure helper + DDD reservations + backfill guards
Payment — domain + application (Track A–C) 18 280 State machine, intent creation, dual-write outbox, refund/capture/void
Payment — provider adapters 12 155 Bambora Classic (adapter, MD5, mapper, http-client, retry) — Worldline Direct kept cho enterprise migration
Payment — expiry cron + metrics 5 25 15-min sweep job, findExpirable, Prometheus labels
Booking → Outbox listeners (Phase 6B) 4 27 OnBookingCreated/Confirmed/Cancelled/Completed/NoShow payload builders + listeners
Payment lifecycle listeners (P1-9 + P1-10) 5 39 OnPaymentStateProjectionListener (8 events → 8 depositStatus), OnPaymentNotificationListener (refund/void SMS), OnPaymentFailedRetryNotificationListener (P1-4)
Authorization expiry cron contract 1 27 Cron sweep + PaymentExpired listener invariant test (audit P1-11)
Status-matrix shipped — P0/P1 series 8 110 P0-1 force cancel, P0-2 deposit guard, P0-3 walk-in IN_PERSON, P1-1 customer cancel, P1-3 phone IN_PERSON, P1-4 retry classification, P1-5 combo guard, P1-8 cancel preview, P1-9 projection, P1-10 notifications, P1-11 7-day cap + auto-cancel
Role-based audit Phase 2–4 6 24 STAFF scoping (booking/resource/tenant-customer/payment), web STAFF support + /auth/me resourceId, multi-tenant cache leak fix
Shared primitives (src/shared/) 6 30 Clock, UUIDv7, event-bus, outbox port + Prisma impl
Infrastructure (Prisma adapter, logger, …) 4 25 PrismaService constructor DI, request-id interceptor
Tổng 112 1332 Coverage ≥ 80% trên mọi module touched bởi PR (verify yarn test:cov)

End-to-end (yarn test:e2e)

Boot Nest app trên Postgres + Redis sạch từ docker-compose.yml, seed fixtures per spec, assert HTTP responses.

Spec Scenarios Pass Mục đích
booking-outbox.e2e-spec.ts 6 6 BookingCreated → outbox row shape, publish marks published, Completed triggers TC+Loyalty DB effects, idempotent re-publish, guest booking flags false
customer-auth.e2e-spec.ts 19 19 Google OAuth callback, token version, profile update, refresh cookie, password reset
public-booking.e2e-spec.ts 34 34 Guest flow, deposit branch, skill enforcement, conflict, P1-1 cancel, multi-day time-off (weekday-flake-fixed)
Tổng 59 59

booking-web — Vitest

Unit + component test chạy với jsdom + @testing-library/react. Colocated src/**/*.test.{ts,tsx}.

File Tests Concern
lib/timezone.test.ts 22 Salon-tz formatters (formatDateTimeInZone), DST edges, month/day boundaries, single-digit pad
lib/booking-display.test.ts 12 Booking detail formatting, status labels
lib/payment/bambora-schema.test.ts 17 Zod validation Bambora Classic credentials (T/P prefix → test/prod)
lib/payment/deposit-calc.test.ts 8 Percentage vs fixed, zero-price guard, rounding
lib/payment/provider-metadata.test.ts 4 Provider label lookup
lib/payment/public-payment-api.test.ts 6 redirectToCheckout polling, timeout, failure mapping
components/settings/payment/BamboraConfigForm.test.tsx 5 Merchant-mode badge gating, required fields
components/settings/payment/ProviderCard.test.tsx 7 Active / inactive / Coming-soon, mode-badge gated by health-check OK
components/settings/payment/HealthCheckBadge.test.tsx 3 OK / FAILED / not-yet với hover timestamp
components/payments/refund-schema.test.ts 8 Refund amount bounds, partial vs full
components/payments/capture-schema.test.ts 5 Capture ≤ authorized amount
components/payments/collect-remaining-schema.test.ts 7 Remaining > 0, ≤ max, integer minor-units
components/staff/StaffFormModal.test.tsx 8 Tạo staff kèm login (email/phone + password + role), unique-per-tenant validate
components/auth/__tests__/SignInForm.test.tsx 7 Multi-tenant AUTH_TENANT_REQUIRED payload, tenant picker (avatar + role + name)
components/auth/__tests__/SignUpForm.test.tsx 5 Owner register flow, validation rules
context/__tests__/AuthContext.test.tsx 6 clear() query cache trên login/logout/register, refreshUser → resolved user
hooks/usePayments.test.tsx 2 Query terminal-status polling stop
hooks/usePaymentConfigs.test.tsx 8 Active config resolution + invalidation
Tổng 137

booking-web — Playwright

Headless Chromium mặc định (playwright.config.ts). Serial execution (workers: 1, fullyParallel: false) vì nhiều test PATCH shared studio-nordic tenant; finally block restore.

ownerApi / staffApi fixtures worker-scoped; loggedInPage / loggedInAsStaffPage load pre-saved storageState (login 1 lần per worker per role) — tránh trip 10-req/min login rate-limiter.

Smoke (admin)

Spec Scenarios Phạm vi
payment-settings.spec.ts 3 Provider list render, Vipps disabled, Bambora drawer mở
payments.spec.ts 2 Payments index render + xuất hiện trong sidebar
staff-role.spec.ts 7 STAFF signin form + sidebar filter (4 visible / 4 hidden) + OwnerOnlyGuard bounce (/admin/staff, /admin/settings, /admin/services) + /admin/bookings accessible + OWNER→STAFF role switch giữ tenantId

Regression — Public booking (public-booking.spec.ts)

13 tests, chia 4 tranches (P1-3 đã thêm 2 case DateStrip clamp).

Tranche 0 — DateStrip clamp maxBookingDaysInAdvance

# Scenario
0.1 Cap ≥ 28 → strip saturate đúng 28 cells
0.2 Cap = 5 → strip render đúng 6 cells (today → today+5), boundary check

Tranche 1 — Service & staff selection

# Scenario
1.1 Happy path: 1 service → confirmed booking (deposit off)
1.2 Multi-service: 2 services → totals aggregate trong summary
1.3 Unassigned booking (bookingMode=allow_unassigned) — test.fixme pending API fix: first slot 09:00 land trên BOOKING_OUTSIDE_BUSINESS_HOURS ở unassigned path (1.1 same slot + assigned passes)
1.4 Assigned-only: submit không staff → inline error trên staff field

Tranche 2 — Settings enforcement

# Scenario
2.3 Closed day (Sunday seed) render disabled cell trong DateStrip
2.4 Time slots bound bởi business hours (no post-21:00 leakage)
2.5 depositEnabled=true → deposit notice visible + submit redirect tới PSP (Bambora checkout URL)

Tranche 3 — Validation & error states

# Scenario
3.1 Submit với blank customerName → inline error, no redirect
3.2 Staff dropdown count match /resources?serviceId=… API (skill filter correctness)
3.3 Invalid serviceId → "no-service-selected" state, booking form không mount

Tranche 4 — Book again

# Scenario
4.1 ?from=<bookingId> pre-fill service item + notes từ seeded booking

data-testid catalogue

TestID File Used by
booking-form BookingPage.tsx Reach form root; assert it didn't mount on invalid deep-link
booking-submit BookingPage.tsx Click submit
booking-error BookingPage.tsx Top-level submit banner
booking-summary BookingPage.tsx Side panel — assert totals appear
booking-success BookingPage.tsx Render khi requiresPayment=false; data-status mirror booking status
deposit-notice BookingPage.tsx Amber banner; data-deposit-amount cho numeric assertion
no-service-selected BookingPage.tsx Empty-state khi deep-link → 0 items
service-item ServiceItem.tsx Per-service row (data-service-id cùng node)
service-item-remove ServiceItem.tsx Remove button (chỉ khi canRemove=true)
service-item-staff ServiceItem.tsx Staff SearchSelect wrapper (scope option queries)
service-item-time ServiceItem.tsx Time-slot SearchSelect wrapper
service-picker-add ServicePicker.tsx Add-service CTA
service-picker-search ServicePicker.tsx Expanded search mode
date-strip DateStrip.tsx Scrollable day container
date-strip-day DateStrip.tsx Each day button; data-date=YYYY-MM-DD

booking-mobile

Chưa có automated tests. Khi mobile scaffold start (sau Role-audit Phase 5), section này sẽ mirror layout của web.


Conventions

  • TDD bắt buộc — viết failing test trước, rồi implement. Xem development-rules.md.
  • E2E luôn restore settings trong finally blockstudio-nordic tenant share giữa specs, serial execution depend trên known state.
  • Worker-scoped fixtures cho auth — tránh rate-limiter false positive khi run > 5 tests / worker.
  • Scope dropdown locators tới SearchSelect wrapper (qua data-testid) — dropdown render fixed-position <div> trong component root, page.locator('li') không scope sẽ leak sang lists khác.
  • Playwright tạo bookings thật trên studio-nordic — intentional để dev spot-check trong admin sau run. Để discoverable marker trong customerName / notes (e.g. E2E Happy Path, E2E rebook seed <ts>).

Known Gaps (target tranche tiếp)

  • Auth flows: chưa Playwright cover customer login / Google OAuth, admin login form, forgot-password round-trip.
  • Admin calendar: drag-and-drop, conflict warning, booking drawer — vẫn manual-test only.
  • Loyalty: unit coverage tốt nhưng chưa E2E redeem-on-checkout flow.
  • Customer portal: booking list pagination + "my bookings" cancel flow chưa có E2E.
  • Settings: chỉ Payments tab có E2E touch.
  • Mobile: zero automated tests (xem trên).
  • Status-matrix P1-4 retry: 26 unit tests cover backend path, FE chưa có Playwright assert UI hiển thị "Retry payment" button.

Track trong features.md ở mỗi module tương ứng.