Skip to main content

Regression Suite — SAPOT Mobile App

Revised: 2026-06-20

This document defines the must-pass regression tests for every release. These tests cover the highest-risk paths: critical user journeys, security boundaries, data integrity, and transport-mode behavior.

Execution types:

  • AUTOMATED — runs in CI (Jest / RNTL / Pytest); safe to gate merges
  • HYBRID — scripted Maestro flow; requires emulator or device in CI with device farm
  • MANUAL — physical device only; see manual-testing-addendum.md; blocks release, not PRs

Release Gate Criteria

Before merging to main or tagging a release:

  • All REG-P0 tests pass
  • pnpm test passes for affected features
  • pnpm run typecheck passes
  • pnpm run lint is clean
  • No new CRITICAL or HIGH security findings

REG-P0 — Must Pass Every Release

Authentication & Sessions

IDNameWhat It VerifiesTest TypeTarget File
REG-001Server login happy pathPOST /auth/token returns valid tokens; MainContainer initializes; Chats tab loadsMaestro E2Eflows/auth/server-login.yaml
REG-002Guest login happy pathGuest name entry; Chats tab loads in LAN mode; no server call madeMaestro E2Eflows/auth/guest-login.yaml
REG-003Access token refreshExpired access token auto-renewed by interceptor; request succeeds transparentlyJest unitfeatures/shared/core/api/__tests__/client-interceptor.test.ts
REG-004Logout clears sessionPOST /auth/logout called; tokens removed; redirected to loginMaestro E2Eflows/auth/logout.yaml
REG-005Expired session redirects to loginSession > 60 days; app reopenedJest unitfeatures/auth/context/__tests__/auth-context.test.tsx
REG-006Banned account blockedLogin with banned account shows BannedBannerRNTLfeatures/auth/context/__tests__/auth-context.test.tsx
REG-007Locked account blockedLogin on locked account shows LockoutBanner; form disabledRNTLfeatures/auth/context/__tests__/auth-context.test.tsx

Transport Mode Guards

IDNameWhat It VerifiesTest TypeTarget File
REG-010Guest locked to LANAppModeStore.getEffectiveMode(isGuest=true) always returns "lan"Jest unitfeatures/shared/stores/__tests__/app-mode-store.test.ts
REG-011LAN mode blocks WS chat fallbackConnectionService.sendChatMessage() throws when no data channel in LAN modeJest unitfeatures/shared/connection/services/__tests__/connection-service.test.ts
REG-012Guest Public Chat tab hiddenTab href is null for guestRNTLapp/(drawer)/(tabs)/__tests__/tab-layout.test.tsx
REG-013Guest Map tab hiddenTab href is null for non-rescuersRNTLapp/(drawer)/(tabs)/__tests__/tab-layout.test.tsx
REG-014Rescuer Map tab visibleTab renders for rescuer usersRNTLapp/(drawer)/(tabs)/__tests__/tab-layout.test.tsx

Messaging

IDNameWhat It VerifiesTest TypeTarget File
REG-020Message SENDING → SENT → DELIVEREDACK received within 12 s; status transitionsJest unitfeatures/chat/services/__tests__/chat-service.test.ts
REG-021ACK timeout marks NOT_SENTNo ACK after 12 s → NOT_SENTJest unitfeatures/chat/services/__tests__/chat-service.test.ts
REG-022Messages encrypted before DB writeMessageRepository.save() stores ciphertextJest unitfeatures/chat/repositories/__tests__/message-repository.test.ts
REG-023Messages decrypted on readMessageRepository.query() returns plaintextJest unitfeatures/chat/repositories/__tests__/message-repository.test.ts
REG-024Message deduplication by messageIdDuplicate insert rejected silentlyJest unitfeatures/chat/repositories/__tests__/message-repository.test.ts
REG-025ConversationKeyStore max 5 keys6th key evicts oldestJest unitfeatures/chat/repositories/__tests__/conversation-key-store.test.ts
REG-026Historical key decrypts old messages5th key decrypts message encrypted before rotationJest unitfeatures/chat/repositories/__tests__/conversation-key-store.test.ts

Call Lifecycle

IDNameWhat It VerifiesTest TypeTarget File
REG-030Accept incoming call → Call RoomAccept → permissions → navigates to Call RoomMaestro E2Eflows/call/accept-call.yaml
REG-031Reject incoming call → ChatsReject → rejectIncomingCall() → Chats tabMaestro E2Eflows/call/reject-call.yaml
REG-032Auto-dismiss after 30 s (missed)No action for 30 s → missed call savedJest unitfeatures/call/services/__tests__/call-service.test.ts
REG-033Simultaneous call tie-breakerDeterministic resolution; no deadlockJest unitfeatures/shared/connection/services/__tests__/connection-service.test.ts
REG-034Call log saved on endMessageType.CALL_LOG in DB with durationJest unitfeatures/call/services/__tests__/call-service.test.ts
REG-035End call from Call RoomBoth disconnected; navigate backMaestro E2Eflows/call/end-call.yaml

Discovery & Connection

IDNameWhat It VerifiesTest TypeTarget File
REG-040mDNS peer discovery registers peerZeroconfAdapter → peerService.register()Jest unitfeatures/shared/connection/services/__tests__/discovery-service.test.ts
REG-041Peer rediscovery triggers reconnectAddress change → resendToPeer()Jest unitfeatures/shared/connection/services/__tests__/discovery-service.test.ts
REG-042TCP handshake + ECDH encryptionTcpClientAdapter completes NaCl-encrypted handshakeJest unitfeatures/shared/connection/adapters/__tests__/tcp-client-adapter.test.ts
REG-043Max 5 retry attemptsAuto-reconnect stops after 5 failuresJest unitfeatures/chat/hooks/__tests__/use-chat-connection.test.ts
REG-044Exponential backoff timingDelays: 1s, 1.8s, 3.2s, 5.8s, 10.4s (±20% jitter)Jest unitfeatures/chat/hooks/__tests__/use-chat-connection.test.ts

Background Connectivity & Notifications

IDNameWhat It VerifiesTest TypeTarget File
REG-050Foreground service starts on backgroundBackground app state starts react-native-background-actionsJest unitfeatures/shared/hooks/__tests__/use-foreground-service.test.ts
REG-051Foreground service stops on activeActive app state stops react-native-background-actionsJest unitfeatures/shared/hooks/__tests__/use-foreground-service.test.ts
REG-052Background call notification fires while process is aliveaudio-call produces an incoming-call notification with ringtoneMaestro E2Eflows/notifications/background-call.yaml
REG-053Cold-start from notificationgetLastNotificationResponseAsync() → Incoming Call screenMaestro E2Eflows/notifications/cold-start-call.yaml
REG-054Notification deduplicationTwo identical notifications → one navigation in 30 sJest unitfeatures/shared/connection/services/__tests__/notification-service.test.ts

Sync

IDNameWhat It VerifiesTest TypeTarget File
REG-060Pull sync scoped to own dataUser A cannot see user B's private conversationsPytesttests/test_sync.py
REG-061Push conflict detectionStale updated_at → 409Pytesttests/test_sync.py
REG-062Sync retry backoff (max 5, max 30 s)Retries with exponential delays; stops after 5Jest unitfeatures/sync/services/__tests__/sync-service.test.ts

Security Boundaries

IDNameWhat It VerifiesTest TypeTarget File
REG-070Testing endpoints blocked in prodPOST /testing/* → 404 or 401Pytesttests/test_security.py
REG-071GPS monitor WS requires authNo-token connect → 1008 closePytesttests/test_security.py
REG-072/auth/exists rate-limited>N req/min → 429Pytesttests/test_security.py
REG-073Captive portal disconnect bug fixedPATCH …/disconnect → 200 (not 500 NameError)Pytesttests/test_captive_portal.py
REG-074JWT uses env secretJWT_SECRET_KEY set; no hardcoded fallback usedManual / CICI env check
REG-075Messages encrypted at restDB content is ciphertextJest unitfeatures/chat/repositories/__tests__/message-repository.test.ts
REG-076TCP traffic encryptedTcpClientAdapter sends NaCl box bytesJest unitfeatures/shared/connection/adapters/__tests__/tcp-client-adapter.test.ts
REG-077WS relay payloads E2E encryptedWS message body is opaque blobJest unitfeatures/shared/services/__tests__/ws-encryption.test.ts

Guest Migration

IDNameWhat It VerifiesTest TypeTarget File
REG-080Messages re-encrypted after migrationAll messages use new ECDH keys post-authenticateJest unitfeatures/auth/services/__tests__/guest-migration-service.test.ts
REG-081skipEncryptedMessageUpdatesOnNextSync calledPrevents double-uploadJest unitfeatures/auth/services/__tests__/guest-migration-service.test.ts
REG-082Conversations preserved after migrationAll pre-migration data visible post-loginMaestro E2Eflows/auth/guest-migration.yaml

REG-P1 — High Priority (Run Before Feature Releases)

IDNameWhat It VerifiesTest Type
REG-100Pull-to-refresh triggers syncsyncService.syncNow() on pullJest unit
REG-101QR scan creates peer + navigatesValid QR → peer upserted → Chat RoomMaestro E2E
REG-102QR missing ip/port in LAN rejected"Invalid QR" badge 4 sRNTL
REG-103Announcements role-filteredRescuer > user countPytest
REG-104Expired announcements excludedOnly active shownPytest
REG-105GPS streaming starts on enableGpsLocationService WS opensJest unit
REG-106GPS streaming stops on disableWS closed on toggle offJest unit
REG-107GPS reconnects after 3 sRe-connects on WS dropJest unit
REG-108Map tab redirects non-rescuersNon-rescuer → Chats tabMaestro E2E
REG-109Search debounced — single API callOne call after rapid typingJest unit
REG-110Unknown search result creates peerpeerService.createUser() before navigateJest unit
REG-111Safe-area insets on all screensNo clipping on notched deviceMaestro E2E

REG-P2 — Known Bug-Prone Area Checks

IDNameWhat It VerifiesTest Type
REG-200Self-chat always connectedOwn peer → DELIVERED immediately; no TCP/WebRTCJest unit
REG-201Seen receipt only when focusedNot sent when screen is backgroundJest unit
REG-202NetworkConfig debounced IP callbackMultiple events → one callbackJest unit
REG-203POST /update/profile skips email fieldEmail not actually changedPytest
REG-204Recovery key cooldown enforced429 within cooldownPytest
REG-205Security question burned after useis_burned=True after correct answerPytest
REG-206WsMessageParser handles unknown typeNo crash on unknown type fieldJest unit
REG-207Server Host Override absent in prodDrawer URL field not renderedMaestro E2E

5-Minute Smoke Test (Run After Every Deploy)

1. GET / → {"state": "running"} ✓
2. POST /auth/ (register) → 200 + tokens ✓
3. POST /auth/token (login) → 200 + tokens ✓
4. WS /ws/?token=<access_token> → connected + status-update:online ✓
5. Send {type: "ping"} → {type: "pong"} ✓
6. GET /sync/pull?last_pulled_at=0 → {changes, timestamp} ✓
7. GET /user-utils/get-announcements → 200 ✓
8. POST /auth/logout → 200; JTI blacklisted ✓

Automate with Pytest + websockets library.


Stub Screens — Excluded from Regression

ScreenReason
/settings/preferences/notificationsStub only ("Notifications" text)
/settings/account/contactsStub only ("Contacts" text)
Drawer "Edit Profile" buttonNavigates nowhere
/(drawer)/(tabs)/debugHidden dev-only tab