Files
baya-monorepo/dev/post-phase/manual-testing-plan.md
T
2026-08-02 17:18:36 +03:30

24 KiB
Raw Blame History

Balinyaar — Manual Testing Plan

SUPERSEDED (2026-08-02) by docs/flows/. This file was written on 2026-07-26 and parts of it no longer work: it tells you to set crypto keys with dotnet user-secrets (removed in 5885280), calls the API https://localhost:5002 (it is plain HTTP), and promises the OTP code appears in the server console (it never does). The verified replacements are docs/flows/testing-setup.md for bring-up and docs/flows/index.md for the per-flow walkthroughs. Kept as a record.

The flow-by-flow walkthrough for testing the whole product against the seeded lifecycle demo world. Written after the UI/backend/refinement phase chains completed (2026-07-26). Companion docs:

  • Bring-up: refinement/RUNBOOK.md — how to boot API + client, OTP login mechanics.
  • Business rules with numbers: product/business/ — what "correct" means; the load-bearing numbers are repeated inline below.

How to use this doc: pick a flow section, log in as the named account, follow the steps, compare against Expected. Anything marked ⚠ known gap is pre-existing and filed (REQ-xxx) — record it as confirmation, not as a new bug. Anything else that deviates is a genuine finding.


1. Environment

Piece Where Notes
API http://localhost:5002 (cd server && dotnet run --project src/API/Baya.Web.Api/Baya.Web.Api.csproj) Boots in Development: migrates + seeds (reference + demo world + lifecycle world) against the DB in appsettings.Development.json — currently the remote dev SQL Server (87.107.152.16). Swagger at /swagger.
Client http://localhost:3000 (cd client && npm run dev) .env.development already points at http://localhost:5002.
Crypto keys dotnet user-secrets (from server/src/API/Baya.Web.Api) The committed config carries SET_VIA_USER_SECRETS_OR_ENV placeholders. The remote DB's data is encrypted under the dev keys — any machine booting against it must set the same four user-secrets: IdentitySettings:SecretKey = dev-only-jwe-signing-key-not-for-production-0123456789abcdef, IdentitySettings:Encryptkey = dev-only-16bytes, Seams:FieldEncryption:Key = local-dev-field-encryption-key-not-for-production, Seams:FieldEncryption:HashKey = local-dev-field-hash-key-not-for-production. Booting with different field keys makes every phone lookup miss and every PII read throw Padding is invalid.
OTP codes server console (MOCK SMS — OTP code …) or GET /api/v1/dev/last_otp/{phone} Resend window 120 s, 5 wrong attempts per code, OTP valid 60 s. The OTP endpoints are rate-limited per IP — hammering logins in scripts will 429.
Reset drop the Baya DB (dotnet ef database drop --force --project src/Infrastructure/Baya.Infrastructure.Persistence --startup-project src/API/Baya.Web.Api), reboot Everything reseeds. Needed when the time-relative scenarios age out (see §3 note).

2. Test accounts

All log in via phone-OTP at /login. Any listed account works immediately.

Phone Actor Who Carries
09120000001 nurse (verified) زهرا عزیزی 4 variants (incl. the 5-session package), whole-city+2-district coverage, ★5.00 (1 published review), 3 completed bookings, a pending request in the inbox, the in-progress visit today, earnings incl. a paid payout and a pending clawback
09120000002 nurse (verified) علی کریمی Partner-sponsored (مرکز پرستاری آرامش), 2 completed bookings (one BNPL), a draft payout waiting, a hidden 1★ review, an expired request
09120000003 nurse (unverified) مریم احمدی Mid-pipeline verification (KYC+Shahkar passed, MoH license in review w/ document, criminal record pending) — never searchable
09120000010 customer سارا محمدی 2 patients, 1 address, 6 bookings across all states, pending/accepted/rejected requests, tickets, notifications, care records
09120000011 customer رضا حسینی 1 infant patient, 2 completed bookings (one BNPL w/ processing refund), expired + cancelled requests, a closed support ticket
09120000020 admin (super_admin) نگار مدیری Full /admin console incl. RBAC
09120000021 admin (finance) کامران مالی /admin scoped to money consoles (useAdminCapabilities)
09120000030 partner owner بهنام رستگار Owns the merchant-of-record center; navigate to /partner manually after login (⚠ REQ-038 — no /me partner signal)
any new 09xxxxxxxxx fresh user Registers on first OTP verify → /select-role → onboarding. Use for first-run testing.

3. The seeded world at a glance

Booking scenarios (all Tehran; seed-time-relative — T0 = the boot that first seeded):

Key Who State Money / attachments
pending request محمدی → عزیزی pending_nurse_response, 24 h countdown nurse-inbox + C5 tracker fodder
accepted request محمدی → عزیزی accepted_awaiting_payment (24 h demo window, not the real 30 min) checkout entry; 1 failed card attempt (wallet history)
rejected / expired / cancelled requests mixed terminal request states C5 terminal cards + inbox tabs
B1 upcoming محمدی × عزیزی confirmed, scheduled T0+3d, care instructions written paid (card), invoice issued, coordination ticket
B2 in_progress محمدی × عزیزی 5-session package: 2 done, session 3 checked-in now, 2 scheduled paid, emergency ticket logged
B3 completed_in_window محمدی × عزیزی completed T01d, dispute window open until T0+2d review pending moderation, care record
B4 completed_paid محمدی × عزیزی completed T010d, paid out ★5 published review (punctual/professional), care record
B5 completed_eligible حسینی × کریمی completed T05d, in the draft batch hidden 1★ review, out-of-range EVV (1220 m)
B6 bnpl_settled حسینی × کریمی completed T06d, paid via SnappPay (net-of-fee) processing bnpl_revert 50% refund (ETA ~10 business days), excluded from payouts, care record
B7 cancelled_refunded محمدی × عزیزی cancelled ≥24 h ahead (standard_24h, 100%) succeeded card refund
C8 clawback محمدی × عزیزی completed T011d, paid out, then fully refunded pending nurse clawback, support alert

Plus: 9+ tickets (coordination per booking, 3 refund tickets, open support w/ an internal admin note, closed support, emergency), notifications for every persona (mixed read/unread), 4 support alerts (low-rating / EVV-mismatch / clawback / emergency), a paid + a draft payout batch, invoices for all 8 bookings (کریمی's resolve to the partner center as merchant-of-record).

Time decay: the world is date-relative and ages. After ~24 h the pending request expires (the sweep runs every minute); after ~2 days B3's dispute window closes and the "review-eligible + in-window" state is gone; the accepted request's payment window lasts 24 h. To refresh the timeline: drop the DB and reboot (seeder guards make an in-place re-run a no-op, not a re-date).


4. Mock-vs-real map (critical for honest testing)

15 of 22 client domains hit the real API. 7 still run on in-browser mocks (USE_*_MOCK = true in client/src/services/{domain}/constants.ts) because a blocking backend REQ is deferred. On those screens the UI shows mock data regardless of the seeded DB — test the UI/UX there, and verify the server data through Swagger instead.

Mocked domain Screens affected Server data still verifiable via
verification nurse /nurse/verification/* journey, admin /admin/verification queue GET nurse_verification/status (nurse token), Swagger
refunds customer cancel flow + /bookings/[id]/refund_status, wallet «استردادها» GET /api/v1/refunds/by_booking/{id}, GET /api/v1/admin_refunds
payouts nurse /nurse/earnings*, admin /admin/payouts `GET /api/v1/nurse_payouts/earnings_balance
admin `/admin/config holidays
bnpl the whole D1D5 BNPL checkout + wallet «اقساط» GET /api/v1/admin_bnpl/{id}
partnerCenter /partner/* portal + /admin/partners GET /api/v1/admin/partner-centers
patientRecords E2 family care record editor + nurse visit-note panel GET /api/v1/patients/{id}/care_records

Real domains (UI = server truth): auth, geography, patients, profiles, nurse-bank, addresses, serviceAreas, catalog, search, bookingRequests, bookings, payment, reviews (incl. admin moderation), tickets (incl. admin ticket console), notifications.


5. Flow walkthroughs

5.1 Auth & first-run (real)

# Steps Expected
A1 /login → fresh phone → request code → wrong code ×5 Refused after 5 attempts until a fresh code; resend locked 120 s; no reveal whether the phone exists
A2 Fresh phone, correct code /select-role (no roles yet) → pick خانواده → /onboarding wizard (relation → first patient) → customer home
A3 Same phone again later Straight to customer home (role remembered)
A4 Nurse 09120000001 login Routed to /nurse dashboard (never a flash of the customer shell)
A5 Admin 09120000020 vs 09120000021 Both land /admin; finance sees only money consoles in the sidebar
A6 Deep link while logged out (e.g. /bookings) /login?next=/bookings → after login lands back on /bookings
A7 Logged out, open / Public landing (/welcome content, URL stays /) — ⚠ verify via npm run build && npm start (dev-server root-path quirk, ui-phase-13)

5.2 Customer — discovery & booking request (real)

Login: 09120000010.

# Steps Expected
C1 Home → search entry → category سالمند, region Tehran/whole city Live count CTA > 0; unverified مریم احمدی never appears in any result
C2 Results عزیزی shows ★5.00 (1 نظر), 3 visits, tappable verified badge; district-3 search still shows her whole-city rows
C3 Gender filter مرد Only کریمی remains
C4 Open عزیزی's profile Trust dossier: badge → verification panel (credential types only, never numbers); services/prices tab; «نظرات» tab shows exactly 1 published review with tags — the pending + hidden ones never
C5 «درخواست رزرو» → C4 form: patient حسن, address, tomorrow, morning window → submit Request created; C5 tracker shows a 24 h countdown ring
C6 As عزیزی (second browser/profile): /nurse/requests New request at top with urgency countdown; detail shows only customer notes + coarse city/district (⚠ stage-1 masked address)
C7 Accept it Customer C5 flips to accepted + 30-minute payment window countdown (real window — the seeded one is a 24 h demo exception)
C8 Reject another (with reason) C5 terminal card + «درخواست دوباره» / «پرستاران مشابه» off-ramps

5.3 Customer — checkout & payment (real contract, mock PSP)

# Steps Expected
P1 C5 of the seeded accepted request → «پرداخت» → C6 Nurse identity + <Money> total (variant price), reconciling breakdown, escrow explainer. ⚠ REQ-046: avatar/badge enrichment may be absent on the real path
P2 «ادامه پرداخت» (card) Initiate succeeds → browser redirects to https://mock-psp.local/... — a dead page. Expected: the mock PSP has no UI (real E2E completion needs the Phase-8 real PSP config).
P3 To actually complete a payment: Swagger → POST /api/v1/bookings/convert body {"bookingRequestId": <id>} with the customer's bearer token (Dev-only capture simulator) Booking appears under «رزروها» as confirmed; coordination ticket auto-opens; invoice issued; nurse gets a notification
P4 Wallet «پرداخت‌ها» ⚠ REQ-047: empty on the real path (no transactions-list endpoint). The seeded failed+succeeded transactions are visible in DB/Swagger only
P5 BNPL branch (D1D5) ⚠ Whole flow is client-mock (REQ-022/024); /bookings/checkout/bnpl/gateway only exists under npm run dev. Server-side BNPL truth: admin_bnpl via Swagger (B6 is reverted)

5.4 Customer — bookings lifecycle (real)

# Steps Expected
L1 /bookings tabs «در انتظار پاسخ»: pending request w/ countdown · «فعال»: B1, B2 · «گذشته»: B3, B4, B7, C8
L2 B1 (upcoming) detail Status timeline at تایید شده; next-session headline T0+3d; .ics download; address card
L3 B2 (package) detail 5 sessions listed: 2 completed, 1 در حال ویزیت (presence headline), 2 scheduled
L4 B3 review CTA Star strip on the completed row → review form (context recap + moderation note) — but B3 already carries a pending review, so expect the "under review" state, never a second form
L5 B4 «مشاهده نظر» shows the published ★5 review state
L6 B1 → لغو رزرو Off-ramps first; tier disclosure: ≥48 h → 100%، ≥24 h → 50%، <24 h → 0% (server tiers) — ⚠ the UI copy states a single 24 h boundary (REQ-065 divergence; record the exact numbers shown). ⚠ the actual cancel + refund-status screens run on the mock refunds domain — server truth via Swagger
L7 B7 → وضعیت استرداد ⚠ mock domain: UI shows mock stages. Server truth: refund succeeded, channel psp_card (GET refunds/by_booking/{B7})
L8 /bookings/[B1]/invoice Real invoice: sequential number, VAT = 10% on the commission line only, مودیان state pending. ⚠ REQ-049: payment-method/reference/seller-fiscal block may be absent

5.5 Customer — account, care circle, tickets, notifications

# Steps Expected
E1 /patients («حلقهٔ مراقبت») حسن + فاطمه cards; add/edit/archive (real); tap → E2 record
E2 /patients/[حسن]/record ⚠ patientRecords domain is mock: meds/routine/tasks + the nurse visit-note history shown are mock data (the 2 real seeded notes are server-side only). Access-denied state testable with the mock's foreign-patient id 8888
E3 /addresses Real CRUD; map is the canvas stand-in unless NEXT_PUBLIC_NESHAN_KEY is set
E4 /profile Identity header (server-masked phone), personal-info edit, language row, emergency-contact card; avatar upload round-trips
E5 /support/tickets Real inbox: refund tickets (TKT-DEMORF0x), open support TKT-DEMOSUP1, coordination tickets; thread of TKT-DEMOSUP1 shows customer msg + admin reply + follow-up — the internal admin note must never appear
E6 Send a message; send another containing /fail Normal send lands; ⚠ /fail sentinel only drives the mock — on the real path it just sends
E7 /notifications Seeded rows, unread-first, day-grouped; mark-read flips the bell count. ⚠ Notification titles/bodies are English — server-rendered copy from the real handlers (finding F-4 §7)

5.6 Nurse — daily ops (real except earnings)

Login: 09120000001.

# Steps Expected
N1 /nurse dashboard Next-visit card (B2 today), requests strip (pending w/ countdown), earnings snapshot (⚠ mock numbers — payouts domain), activation checklist collapsed to «فعال در جستجو»
N2 /nurse/visits Today's feed shows B2 session 3 checked-in → the check-out CTA is the hero action
N3 Check out (confirm step) Session completes; EVV panel updates. GPS: set NEXT_PUBLIC_EVV_MOCK_GPS=in_range in client/.env.local to force a passing location, else real geolocation runs (out-of-tolerance is advisory — banner + staff alert, never a block). Completing the last session would complete the booking and start its 72 h dispute clock
N4 /nurse/visits/[B2] Address card + map link (⚠ REQ-051: address may stay masked w/ quiet fallback note), gated care card (care instructions readable post-confirmation), visit-note composer (⚠ mock — patientRecords)
N5 /nurse/requests Tabs: pending (live countdown) / answered / expired. Accept behind confirm dialog → opens the payment window
N6 /nurse/earnings + /payouts Mock domain — UI shows mock buckets. Server truth (Swagger, nurse token): earnings_balance = signed ledger sum (عزیزی: B1+B2+B3 payouts pending — B7 reversed, B4/C8 paid out), history shows the paid payout w/ masked IBAN
N7 /nurse/services 4 offerings incl. the package variant; edit/deactivate; the accepting-bookings toggle is real (pausing → disappears from search)
N8 /nurse/coverage Whole-city + districts 1&3; duplicate add → 409 message
N9 /nurse/bank Verified account, masked IBAN, ownership state «تأیید شده»
N10 As کریمی (09120000002) Draft-batch earnings waiting; B6's payout share reversed by the 50% refund; hidden review does not appear on his public profile (0 reviews)
N11 As احمدی (09120000003) Activation checklist far from complete; /nurse/verification — ⚠ mock domain: the journey UI shows mock state; the real mid-pipeline case (KYC ✓, Shahkar ✓, license in-review, criminal pending) is server-side (nurse_verification/status via Swagger). She must never appear in search

5.7 Admin console

Login: 09120000020 (all consoles) and 09120000021 (money only).

# Console Real/Mock What the seeded world provides (server-side)
AD1 /admin/reviews moderation queue Real B3's pending review — publish it → عزیزی's public aggregate recomputes (★ count 2); hide/reject paths; the hidden 1★ shows the hide flow already happened
AD2 /admin/tickets Real Full queue incl. emergency (red) + refund tickets; thread of TKT-DEMOSUP1 shows the internal note distinctly; composer's internal-note mode goes amber
AD3 /admin/verification ⚠ Mock Real queue data (احمدی in review + document metadata) via Swagger admin_verifications; the document's signed-URL fetch 404s (no real bytes — expected)
AD4 /admin/payouts ⚠ Mock Real: admin_payouts/batches shows the completed batch (2 links) + the draft batch; batches/{id}/process on the draft is the demoable money-out action; eligible preview
AD5 Refunds (via ticket RefundPanel) ⚠ Mock Real: admin_refunds lists 3 refunds; {id}/confirm_settlement on the processing BNPL refund is the demoable action (posts the clearing leg)
AD6 /admin/alerts ⚠ Mock Real: 4 seeded alerts (low_rating / evv_location_mismatch / nurse_clawback / emergency) via support_alerts endpoints
AD7 /admin/config, /admin/holidays, /admin/audit, /admin/users, /admin/roles ⚠ Mock Real: b1 endpoints (config list incl. the §6 numbers, audit trail rows for every seeded money/trust entity — actor null = system)
AD8 /admin/partners ⚠ Mock Real: admin/partner-centers shows مرکز پرستاری آرامش (MoR, verified, masked IBAN), کریمی sponsored

5.8 Partner portal (⚠ mock — REQ-032/033/038)

Login 09120000030 → navigate to /partner manually. UI runs on mock center data; the real center + MoR invoice resolution (کریمی's two invoices carry issuing_entity_type = partner_center) is verifiable via Swagger. Test the portal chrome, the MoR settlement view, and the CSV export against mock data.


6. The numbers the UI must respect

From seeded platform_configs (admins can change them; rates are snapshotted — a change never re-prices an existing booking/invoice/deadline):

Rule Value
Nurse response deadline 24 h (frozen per request)
Payment window after accept 30 min (frozen per request)
Dispute window after completion 72 h → then payout-eligible
Platform commission 15% of gross (gross = commission + payout, always)
VAT 10%, on the commission line only
Cancellation tiers ≥48 h → 100% · ≥24 h → 50% · <24 h → 0% (⚠ client copy says a single 24 h boundary — REQ-065)
BNPL provider fee 7% — a platform expense; the nurse payout is payment-method-invariant
BNPL refund ETA ~10 business days (Fridays skipped); UI copy says 710
EVV tolerance 200 m, advisory only
No-show threshold 60 min after scheduled start
Payout cadence weekly generation (auto, draft); processing stays a manual admin action
Review alert rating ≤ 2 raises a staff alert
OTP resend 120 s · 5 attempts · code lives 60 s · session 30 d · access token 60 min

7. Findings from this bring-up (decide / track)

Recorded while building the seed — each needs an owner decision:

# Finding State
F-1 Committed real DB credential: appsettings.Development.json ships the remote server's SQL login in git (the exact thing refinement-phase-5 removed; the pre-commit hook that blocks it isn't enabled on this clone). Recommendation: move the connection strings to user-secrets like the crypto keys and restore placeholders. Open — decision needed
F-2 /me hardcoded hasCustomerProfile/hasNurseProfile/nurseVerificationStatus to false/not_started for everyone (broke the customer home profile nudge + profile-hook invalidations). Fixed this session (snapshot projection + ToMeResult)
F-3 Stored Persian text is normalized destructively: an ApplicationDbContext save hook runs Fa2En().FixPersianChars() over every string property — Persian digits become ASCII and ZWNJ becomes a space in everything persisted (e.g. «شبانه‌روزی» → «شبانه روزی»), contradicting the client's STYLE.md ZWNJ rules. Any Persian-literal equality against stored data silently misses. Open — decision needed (legacy starter behavior; removing it changes stored data shape)
F-4 Notification copy is English on the real path — the b-phase handlers write "Booking confirmed"-style titles/bodies straight into ops.Notifications; the Persian UI renders them verbatim. Open — decision needed (server-side i18n or client-side type→copy mapping)
F-5 Mock PSP redirect goes to a dead host (mock-psp.local) — in-browser card completion impossible until a real PSP is configured (Payments:Provider=zarinpal); the Dev bookings/convert affordance is the workaround (§5.3 P3). By design (refinement-phase-8 rails exist behind config)
F-6 7 client domains still mocked (§4) — the biggest lever for "test everything for real" is flipping verification, refunds, payouts (user halves are contract-ready; the blocking REQs are admin-side). Flipping a shared flag turns the admin half's screens into real-API screens against missing endpoints. Open — decision needed (split flags per half, or deliver the admin REQs)
F-7 Seeded object-storage keys (verification document, invoice PDFs) have no real bytes — signed-URL fetches 404 in admin viewers. Accepted for dev (never fake bytes)

8. Suggested test order

  1. Auth & routing (§5.1) — everything depends on it.
  2. Customer discovery → request → accept → convert → booking (§5.25.4) — the core funnel, all real.
  3. Nurse ops (§5.6) — inbox/EVV against the same bookings.
  4. Reviews + tickets + notifications (§5.4 L4/L5, §5.5 E5E7) — real, seeded both sides.
  5. Admin real consoles (§5.7 AD1/AD2) — moderation + tickets.
  6. Mock-domain UI passes (§5.3 P5, §5.5 E2, §5.6 N6/N11, §5.7 AD3AD8, §5.8) — UX only, verify server truth via Swagger as listed.
  7. Sweep §7's decisions.