Phase 3.3: topic sensitivity + Phase 3.4: auth foundation

Phase 3.3 — Session Topic Sensitivity (complete):
- Backend: topic_sensitivity column + session_sensitivity_log, sensitivity service
  (flip with one-way-latch + audit), PATCH /api/shared/chat/sessions/:id/topic,
  topic carried in pairing + extension WS payloads, CC filter + sensitive stats
  + per-mitra sensitive columns on activity page
- client_app: TopicSelectionBottomSheet before pricing, topic flows through
  pairing request, silent WS handler for session_topic_updated
- mitra_app: SensitivityBadge + SensitivityTheme + sensitivityConfigProvider,
  overlay badge + yellow accent, chat screen app-bar toggle with configurable
  confirmation + latch, extension card shows current flag, history + transcript
  yellow theme
- control_center: Sensitivitas Topik settings section, topic filter + column
  with inline audit log, sensitive stats dashboard card, mitra activity
  sensitive columns with QC flag

Phase 3.4 — Self-Managed Auth (foundation only):
- Migration: auth_sessions + otp_requests tables, social identity columns on
  customers, password_hash + lockout on control_center_users, OTP + CC lockout
  app_config keys
- New services: password (bcrypt + complexity), token (JWT HS256 + refresh
  rotation, session_id claim pre-wires future Valkey revocation),
  social-identity (Google + Apple JWKS), OTP (Fazpass stub — real API TBD)
- Constants: AuthProvider + OtpChannel
- Middleware, auth route rewrites, WS auth update, Firebase → FCM isolation
  still pending (next chunk); Fazpass docs + Apple Developer setup still
  required before E2E testing

Docs:
- requirement/phase3.3.md, phase3.3-plan.md, phase3.3-testing.md
- requirement/phase3.4.md, phase3.4-plan.md

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-24 10:15:12 +08:00
parent 97d50a8e08
commit 780cade3db
44 changed files with 3834 additions and 103 deletions

View File

@@ -0,0 +1,273 @@
# Phase 3.3 Testing & Outstanding Regression Checklist
This is a **reminder document** — consolidated testing work for Phase 3.3 plus every outstanding test item carried over from earlier Phase 3 iterations.
Tick boxes as you verify.
---
## Part 1 — Phase 3.3: Session Topic Sensitivity
### 1.1 Database / Migration
- [ ] Migration runs cleanly on an existing dev DB (no errors, all `IF NOT EXISTS` / `ON CONFLICT` paths hit)
- [ ] `chat_sessions.topic_sensitivity` column exists with default `'regular'` and NOT NULL
- [ ] Existing sessions (created before the migration) have `topic_sensitivity = 'regular'` after migration
- [ ] `session_sensitivity_log` table exists with correct FKs (sessions, mitras)
- [ ] `idx_chat_sessions_topic_sensitivity` index created
- [ ] `idx_session_sensitivity_log_session` index created
- [ ] `app_config` has `sensitive_flip_confirmation_enabled = true` by default
- [ ] `app_config` has `sensitive_flag_one_way_latch = false` by default
### 1.2 Customer Flow (client_app)
**Topic selection bottom sheet**
- [ ] Tap "Mulai Curhat" → topic selection bottom sheet appears
- [ ] Sheet **cannot** be dismissed by tapping outside
- [ ] Sheet **cannot** be dismissed by swiping down
- [ ] System back button cancels entire "Mulai Curhat" flow (does NOT open pricing)
- [ ] Copy matches PRD (title, body, sub-question, helper line)
- [ ] "Topik umum" button styling is primary
- [ ] "Topik sensitif" button styling is secondary but equal weight (not de-emphasized)
**Request submission**
- [ ] Tap "Topik umum" → pricing sheet opens with topic pre-selected as regular
- [ ] Tap "Topik sensitif" → pricing sheet opens with topic pre-selected as sensitive
- [ ] After pricing confirm → `POST /api/client/chat/request` body includes `topic_sensitivity: regular|sensitive`
- [ ] Backend rejects request with missing `topic_sensitivity` (400 BAD_REQUEST)
- [ ] Backend rejects request with invalid `topic_sensitivity` value (e.g., `"other"`)
- [ ] Created `chat_sessions` row has correct `topic_sensitivity` value
**Customer UI after request**
- [ ] Chat screen stays pink (no yellow), regardless of flag
- [ ] Customer history screen: no badge on any row regardless of flag
- [ ] Customer transcript screen: stays pink
- [ ] Customer receives `session_topic_updated` WS message after mitra flip → **no UI change**, no error, no crash
### 1.3 Mitra Flow — Incoming Request (mitra_app)
- [ ] Regular request: overlay has no badge, no yellow accent
- [ ] Sensitive request: overlay shows "Topik sensitif" badge + yellow accent
- [ ] Overlay payload from WS includes `topic_sensitivity`
- [ ] Overlay payload from FCM fallback includes `topic_sensitivity` (or app fetches on open)
- [ ] Overlay payload from `getPendingRequestsForMitra` (app-resume path) includes `topic_sensitivity`
- [ ] Mitra accepts sensitive request → lands in active chat screen with correct flag
### 1.4 Mitra Flow — Active Chat Screen
- [ ] Sensitive active session: yellow doodle background
- [ ] Regular active session: pink doodle (unchanged behavior)
- [ ] Header banner shows "Topik sensitif" label only when sensitive
- [ ] App-bar toggle icon visible (flag / flag_outlined depending on state)
**Flip toggle — confirmation enabled (default)**
- [ ] Tap toggle regular → sensitive → dialog appears: "Tandai sesi ini sebagai sensitif?"
- [ ] "Batal" cancels, no state change, no audit log entry, no WS broadcast
- [ ] "Tandai" flips, background turns yellow instantly, log entry created
- [ ] Tap toggle sensitive → regular → dialog: "Tandai sesi ini sebagai topik umum?"
**Flip toggle — confirmation disabled (via CC config)**
- [ ] Toggle `sensitive_flip_confirmation_enabled` to `false` in CC
- [ ] Flip happens immediately, toast "Sesi ditandai sensitif" / "Sesi ditandai topik umum"
- [ ] No dialog appears
**One-way latch — disabled (default)**
- [ ] Can flip regular → sensitive → regular → sensitive freely
**One-way latch — enabled (via CC config)**
- [ ] Toggle `sensitive_flag_one_way_latch` to `true` in CC
- [ ] Session that was regular: can flip to sensitive; toggle then disabled
- [ ] Session that was already sensitive at latch-enable time: toggle disabled with tooltip
- [ ] Attempt to flip sensitive → regular with latch on: API returns 409 `SENSITIVITY_LATCHED`
- [ ] Error dialog shown: "Sesi sudah ditandai sensitif dan tidak bisa diubah kembali."
**Audit trail**
- [ ] Every successful flip creates a `session_sensitivity_log` row with correct `from_value`, `to_value`, `changed_by_mitra_id`
- [ ] No-op flip (e.g., tap confirm but value didn't actually change) does NOT create a log row
- [ ] Log entries ordered correctly (ascending `created_at`)
### 1.5 Mitra Flow — Extension
- [ ] Customer requests extension on regular session → mitra extension card has no badge
- [ ] Customer requests extension on sensitive session → mitra extension card shows "Topik sensitif" badge + yellow accent
- [ ] Mitra flipped session mid-chat regular → sensitive, then customer requests extension → extension card reflects **current** sensitive flag
- [ ] Extension accepted → flag carries over unchanged to extended session
### 1.6 Mitra Flow — History & Transcript
- [ ] Mitra history list row shows "Topik sensitif" badge for sensitive sessions
- [ ] Mitra history list row has no badge for regular sessions
- [ ] Mitra transcript view: sensitive session → yellow doodle background
- [ ] Mitra transcript view: regular session → pink doodle
- [ ] Customer-side history and transcript: always pink, no badge
### 1.7 Mitra Flow — Edge Cases
- [ ] Mitra tries to flip flag on a session they don't own → 403 FORBIDDEN
- [ ] Mitra tries to flip flag on a `CLOSING` session → 409 SESSION_NOT_ACTIVE
- [ ] Mitra tries to flip flag on a `COMPLETED` session → 409 SESSION_NOT_ACTIVE
- [ ] Mitra tries to flip flag on an `EXPIRED` session → 409 SESSION_NOT_ACTIVE
- [ ] Invalid `topic_sensitivity` value sent to PATCH endpoint → 400 BAD_REQUEST
- [ ] Customer tries to call PATCH endpoint → 403 FORBIDDEN (only mitra allowed)
### 1.8 Control Center — Settings
- [ ] Settings page has new "Sensitivitas Topik" section
- [ ] `sensitive_flip_confirmation_enabled` checkbox reflects current backend value
- [ ] `sensitive_flag_one_way_latch` checkbox reflects current backend value
- [ ] PATCH `/internal/config/sensitivity` persists changes
- [ ] Changes take effect immediately on next mitra flip (no app restart needed on mitra side, if mitra fetches config dynamically)
### 1.9 Control Center — Sessions Page
- [ ] Sessions list has new filter dropdown (All / Umum / Sensitif)
- [ ] Filter "Sensitif" returns only sessions with `topic_sensitivity = 'sensitive'`
- [ ] Filter "Umum" returns only `regular`
- [ ] Filter "All" returns everything (backward-compatible)
- [ ] Filter works combined with existing status filter
- [ ] Session list has new "Topik" column showing badge (green "Umum" / yellow "Sensitif")
### 1.10 Control Center — Session Detail
- [ ] Session detail page shows current `topic_sensitivity`
- [ ] Session detail shows sensitivity audit trail timeline: "Mitra {name} menandai topik sebagai {from→to} pada {timestamp}"
- [ ] Timeline ordered ascending
- [ ] Sessions with no flips show empty timeline (no error)
### 1.11 Control Center — Dashboard
- [ ] Dashboard shows "Sesi Sensitif" card with total count + 30-day % breakdown
- [ ] Percentage math correct (sensitive / total × 100, rounded to 1 decimal)
- [ ] Edge case: 0 sessions in last 30 days → shows `0%` not `NaN`
### 1.12 Control Center — Mitra Activity
- [ ] Summary table has new columns: Sensitive Total, Sensitive Accepted, Sensitive Rate (%)
- [ ] Mitra with 0 sensitive requests shows `—` (not `0%`)
- [ ] Sensitive rate computed correctly (sensitive_accepted / sensitive_total × 100)
- [ ] Detail log table: optional new "Topik" column with badge
- [ ] Date range filter still works with new columns
### 1.13 Control Center — Integration Regression
- [ ] Existing settings (anonymity, free-trial, extension-timeout, early-end, mitra-ping, price-tiers) still work
- [ ] Existing sessions filter (by status) still works
- [ ] Existing dashboard cards still render
---
## Part 2 — Outstanding Items From Phase 3.2
Carried over from `project_phase3_testing_status.md` (2026-04-15):
### 2.1 Chat Request Overlay
- [ ] **Multiple concurrent chat requests** — verify queue behavior (one shown at a time, next appears when current resolved)
- [ ] Stale request: "cancelled by customer" message shown + requires acknowledge (no auto-dismiss)
- [ ] Stale request: "accepted by other bestie" message shown + requires acknowledge
- [ ] Stale request: "expired" message shown + requires acknowledge
- [ ] Swipe-to-dismiss (ignore) does NOT send reject to backend
- [ ] Ignored request eventually logs as `ignored` in `chat_request_notifications` after 60s timeout
- [ ] Request `missed` (another mitra accepted first) logs correctly
- [ ] `active_session_count` captured correctly at notification creation
### 2.2 End-to-End Flows
- [ ] Full chat flow: pair → chat → extension → closure (customer + mitra)
- [ ] Goodbye flow: session expires → closing → both submit goodbye → completed
- [ ] Extension accepted mid-flow → session resumes, timer extends, no grace timer lingering
- [ ] Extension rejected → session moves to closing, both see closure UI
- [ ] Extension timeout (no mitra response) → closing
### 2.3 iOS Coverage (still partially untested)
- [ ] OTP login on iOS (customer)
- [ ] OTP login on iOS (mitra)
- [ ] Push notifications on iOS (customer + mitra)
- [ ] FCM token registration on iOS
- [ ] Chat screen rendering on iOS
- [ ] Back button behavior on iOS (deep-link pop fallback)
- [ ] Overlay on iOS (from `project_phase3_testing_status`: iOS setup started but incomplete)
- [ ] Splash screen on iOS
- [ ] Onboarding carousel on iOS
- [ ] Keyboard handling on iOS (chat input, goodbye form)
---
## Part 3 — Outstanding Items From Phase 3 / 3.1
### 3.1 Session Lifecycle
- [ ] Server restart mid-session: session timer is restored from DB (`restoreActiveTimers`)
- [ ] Stale active sessions auto-complete on restart
- [ ] Closing sessions with stale grace timers auto-complete on restart
- [ ] Session expired from customer side (5-min countdown display)
- [ ] Abandoned session during closure grace period → auto-completes
- [ ] **Known limitation**: multi-instance backend sessions not supported until Valkey keyspace notifications implemented (out of scope, just confirm single-instance behavior)
### 3.2 Chat Mechanics
- [ ] Message status transitions (sent → delivered → read) work correctly
- [ ] Typing indicator shows/hides correctly on both sides
- [ ] Messages received while backgrounded are marked `delivered` on foreground resume
- [ ] Messages viewed are marked `read` and the read receipt propagates back to sender
- [ ] Unread badge on home screen updates correctly (client_app + mitra_app)
### 3.3 Navigation / UI
- [ ] All navigation uses `GoRouter.context.push/go` (no leftover `Navigator.pushNamed`)
- [ ] Deep-linked screens work with `canPop` fallback + `PopScope`
- [ ] `notification_service` uses `go` (not `push`) for terminal states
- [ ] Splash screen hides auth loading flash on both apps
- [ ] Goodbye views use `SingleChildScrollView` (no keyboard overflow)
### 3.4 Control Center Settings
- [ ] Free trial config: toggle + duration edit
- [ ] Extension timeout: edit seconds
- [ ] Early end: toggle mitra / customer independently
- [ ] Mitra ping: toggle require + interval
- [ ] Price tiers: add / edit / remove tiers and verify client_app pricing sheet reflects changes
---
## Part 4 — Cross-Cutting / Pre-Release
### 4.1 Regression Checks (do after Phase 3.3 merge)
- [ ] Existing customer auth flow still works (welcome → OTP → register → home)
- [ ] Existing mitra auth flow still works
- [ ] Existing control center login still works (admin@halobestie.com)
- [ ] Pairing flow (mulai curhat → matched) still works end-to-end
- [ ] All existing WS messages still processed (no regressions from new `session_topic_updated` handler)
### 4.2 Platform Coverage
- [ ] Android: customer app on emulator (Medium_Phone_API_36.1)
- [ ] Android: mitra app on physical device (SM-A530F, 52002a5db8e0c46b)
- [ ] iOS: customer app (Mac + simulator / physical)
- [ ] iOS: mitra app (Mac + simulator / physical)
- [ ] Control center: Chrome latest
- [ ] Control center: Firefox / Safari (if required)
### 4.3 Load / Concurrency (sanity)
- [ ] 2 concurrent customers requesting chat at the same time — both find a mitra (or one waits)
- [ ] 1 customer, 5 mitras online — blast notification reaches all 5
- [ ] Mitra accepts after another mitra already accepted → receives `missed` with `accepted_by_other`
- [ ] Backend restart with active sessions → timers restored, no data loss
### 4.4 Config Flag Interactions
- [ ] `sensitive_flag_one_way_latch = true` + existing sensitive session → toggle disabled
- [ ] `sensitive_flip_confirmation_enabled = false` + rapid flips → no race, all logged in order
- [ ] Both config flags toggled together → no conflict
### 4.5 Known Blockers / Deferred
Not tests — tracked here so they don't get forgotten:
- [ ] **Valkey keyspace notifications** — required for multi-instance session timers (noted in memory as future work)
- [ ] **Mitra QC auto-flag** — auto-flagging high-rejection mitras on CC (future phase)
- [ ] **Merge-on-link** for social login (currently reject-on-existing)
- [ ] **Phase 3.4 auth migration** — separate phase, not blocking 3.3 testing