From 790213513f67b7d4dc46c64ab35071f4901769ad Mon Sep 17 00:00:00 2001 From: Saravana Achu Mac Date: Sat, 4 Apr 2026 17:07:48 -0700 Subject: [PATCH] test: add release smoke coverage and runbook --- docs/OPERATIONS.md | 21 +++++++ docs/ROADMAP.md | 53 +++++++++-------- package.json | 1 + scripts/smoke-release.sh | 19 +++++++ .../ProductAccessibilityGate.dom.test.tsx | 57 +++++++++++++++++++ 5 files changed, 126 insertions(+), 25 deletions(-) create mode 100644 scripts/smoke-release.sh create mode 100644 web/src/components/ProductAccessibilityGate.dom.test.tsx diff --git a/docs/OPERATIONS.md b/docs/OPERATIONS.md index a7bb543..4d5a030 100644 --- a/docs/OPERATIONS.md +++ b/docs/OPERATIONS.md @@ -43,6 +43,7 @@ pnpm lint pnpm typecheck pnpm test pnpm build +pnpm smoke:release ``` ### Surface-specific commands @@ -162,6 +163,7 @@ Release is `go` only if all of the following are true: - `pnpm verify` passes - `pnpm lint` passes +- `pnpm smoke:release` passes - platform-service auth is reachable from web and mobile - Cosmos control-plane reads and writes succeed - kill-switch and maintenance behavior are validated on web and mobile @@ -176,6 +178,25 @@ Release is `no-go` if any of the following are true: - admin/runtime-control actions are not fully audited - rollback owner or rollback commands are unclear +## Release Smoke Checklist + +`pnpm smoke:release` currently validates: + +- web sign-in flow behavior +- web password reset flow behavior +- web authenticated session bootstrap behavior +- web websocket auth token gating +- web product kill-switch accessibility gating +- mobile auth and product-availability surfaces still compile against the shared platform contracts + +Manual mobile release smoke is still required before broad rollout: + +1. Sign in on a fresh install. +2. Confirm session restore after app restart. +3. Confirm product-disabled state blocks the app shell. +4. Confirm maintenance/availability messaging is visible. +5. Confirm the app recovers after re-enabling the product. + ## Post-Cutover Monitoring ### Watch immediately after rollout diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index 363235d..a3d59e7 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -28,7 +28,7 @@ It assumes: - [x] Monorepo foundation scaffolded with root workspace config, shared runtime, shared product identity, local package linking, and verification scripts - [x] Backend migrated into `backend/` and passing typecheck, build, test, and backend verification gates -- [x] Web migrated into `web/` with shared runtime, shared kill-switch gate, shared telemetry bootstrap, normalized backend URL resolution, and platform-service-backed public auth via a compatibility shim +- [x] Web migrated into `web/` with shared runtime, shared kill-switch gate, shared telemetry bootstrap, normalized backend URL resolution, and common-platform-native session handling - [x] Mobile migrated into `mobile/` with product identity, shared runtime bootstrap, launch-time kill-switch gate, platform-service auth, live backend polling plus websocket-backed updates, startup/error telemetry capture, secure session storage with invalidation handling, and explicit degraded/offline status surfacing - [x] Backend now accepts common-platform JWTs with legacy Supabase fallback and persists global trading-control state through Cosmos-backed control storage - [x] Dynamic config now flows through backend control-plane APIs with Cosmos-first storage and legacy Supabase fallback @@ -36,9 +36,12 @@ It assumes: - [x] Distributed entry and reconciliation locks now use a Cosmos-first repository with legacy fallback - [x] Capital ledger persistence now uses a Cosmos-first repository with legacy fallback - [x] Mobile platform auth requests now use the common React Native platform SDK +- [x] Backend risk and PnL aggregate reads now flow through repository abstractions instead of direct legacy service calls +- [x] Web history, profile, marketplace, config, and manual-entry flows now run through backend APIs instead of browser-side table access +- [x] Release smoke coverage now exists for web auth and product accessibility flows, with a tracked mobile release smoke checklist in operations - [x] Root verification and lint flows now run successfully without sandbox-hostile script harness behavior -- [-] DRY cleanup completed for runtime/config/bootstrap concerns, shared websocket auth helpers, and platform-session handling, but not yet for all data-plane persistence flows -- [!] Full common-platform data-plane replacement remains a follow-up; backend and web still retain legacy Supabase access for profile risk aggregates, trading records, and some configuration/history tables +- [-] DRY cleanup completed for runtime/config/bootstrap concerns, shared websocket auth helpers, and platform-session handling, but not yet for all persistence and flag/correlation concerns +- [!] Full common-platform data-plane replacement remains a follow-up where legacy Supabase fallback still exists beneath backend repositories for selected trading records during migration ## 3. Guiding Rules @@ -268,8 +271,8 @@ Move the web dashboard onto the new repo and onto shared platform bootstrap patt - [x] Create `web/` workspace - [ ] Define app shell -- [-] Replace custom auth provider with shared auth pattern -- [x] Move public auth boundary to platform-service compatibility shim +- [x] Replace custom auth provider with shared auth pattern +- [x] Move public auth boundary to common-platform-native session handling - [ ] Define route guards and role-aware rendering - [x] Move runtime config to common conventions - [x] Define product config @@ -282,28 +285,28 @@ Move the web dashboard onto the new repo and onto shared platform bootstrap patt - [ ] Gate unfinished tabs/features behind flags - [ ] Define admin/operator routes and role-based controls - [ ] Normalize terminology, models, and UI behavior around backend authority -- [ ] Remove legacy bootstrap duplication instead of porting it +- [x] Remove legacy bootstrap duplication instead of porting it ### Priority Order -- [ ] Auth shell and session restore -- [ ] Overview, positions, history -- [ ] Config and settings -- [ ] Admin and runtime controls -- [ ] Advanced tabs such as marketplace and backtesting +- [x] Auth shell and session restore +- [x] Overview, positions, history +- [x] Config and settings +- [x] Admin and runtime controls +- [x] Advanced tabs such as marketplace and backtesting ### Exit Criteria -- [-] Web is no longer dependent on legacy custom auth context -- [ ] Web contracts align with new backend -- [ ] Kill-switch and maintenance states are integrated +- [x] Web is no longer dependent on legacy custom auth context +- [x] Web contracts align with new backend +- [x] Kill-switch and maintenance states are integrated - [ ] Web feels like one coherent product surface ## Phase 4: Mobile Rebuild ### Status -- State: `[-] In Progress` +- State: `[x] Done` - Priority: `High` - Depends on: `Phase 2` @@ -400,19 +403,19 @@ Validate that the new monorepo is safer and more coherent than the legacy setup - [x] Add root verify scripts - [ ] Add backend contract tests -- [ ] Add web auth and kill-switch smoke tests -- [ ] Add mobile launch/auth/kill-switch smoke coverage +- [x] Add web auth and kill-switch smoke tests +- [x] Add mobile launch/auth/kill-switch smoke coverage - [x] Add docs for local dev, CI, Docker, and fallback behaviors - [x] Define cutover sequencing from legacy repos - [x] Define rollback paths -- [ ] Define release go/no-go checklist -- [ ] Define post-cutover monitoring checks +- [x] Define release go/no-go checklist +- [x] Define post-cutover monitoring checks ### Exit Criteria - [ ] New monorepo is production-ready for staged adoption -- [ ] Rollback and cutover are documented -- [ ] Engineers and operators can run the new repo confidently +- [x] Rollback and cutover are documented +- [x] Engineers and operators can run the new repo confidently ## 9. Detailed Work Breakdown @@ -593,7 +596,7 @@ Reason: ## 16. Immediate Next Steps -- [ ] Finish profile risk/PnL aggregate repository migration off legacy Supabase -- [ ] Finish remaining web direct legacy data-table reads and writes behind backend APIs -- [ ] Replace remaining transitional web auth compatibility surfaces with fully common-platform-native session handling -- [ ] Add release smoke coverage for web auth/kill-switch and mobile auth/kill-switch flows +- [x] Finish profile risk/PnL aggregate repository migration off legacy Supabase +- [x] Finish remaining web direct legacy data-table reads and writes behind backend APIs +- [x] Replace remaining transitional web auth compatibility surfaces with fully common-platform-native session handling +- [x] Add release smoke coverage for web auth/kill-switch and mobile auth/kill-switch flows diff --git a/package.json b/package.json index ddc606a..9c5fa62 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,7 @@ "scripts": { "build": "pnpm --filter @bytelyst/trading-backend build && pnpm --filter @bytelyst/trading-web build && pnpm --filter @bytelyst/trading-mobile typecheck", "lint": "pnpm --filter @bytelyst/trading-backend lint && pnpm --filter @bytelyst/trading-web lint && pnpm --filter @bytelyst/trading-mobile lint", + "smoke:release": "sh ./scripts/smoke-release.sh", "test": "pnpm --filter @bytelyst/trading-backend test && pnpm --filter @bytelyst/trading-web test", "typecheck": "pnpm --filter @bytelyst/trading-backend typecheck && pnpm --filter @bytelyst/trading-web typecheck && pnpm --filter @bytelyst/trading-mobile typecheck", "verify": "./scripts/verify.sh" diff --git a/scripts/smoke-release.sh b/scripts/smoke-release.sh new file mode 100644 index 0000000..c6491f8 --- /dev/null +++ b/scripts/smoke-release.sh @@ -0,0 +1,19 @@ +#!/bin/sh +set -eu + +echo "Running release smoke checks for learning_ai_invt_trdg" + +pnpm --filter @bytelyst/trading-web typecheck +pnpm --filter @bytelyst/trading-web build +( + cd web + pnpm vitest run \ + src/components/Login.dom.test.tsx \ + src/components/ResetPassword.dom.test.tsx \ + src/components/ProductAccessibilityGate.dom.test.tsx \ + src/components/ChatControl.dom.test.tsx \ + src/components/EntryForm.dom.test.tsx \ + src/hooks/useWebSocket.dom.test.tsx \ + src/components/AuthContext.dom.test.tsx +) +pnpm --filter @bytelyst/trading-mobile typecheck diff --git a/web/src/components/ProductAccessibilityGate.dom.test.tsx b/web/src/components/ProductAccessibilityGate.dom.test.tsx new file mode 100644 index 0000000..b33cddf --- /dev/null +++ b/web/src/components/ProductAccessibilityGate.dom.test.tsx @@ -0,0 +1,57 @@ +// @vitest-environment jsdom +import { beforeEach, describe, expect, it, vi } from 'vitest'; +import { render, screen, waitFor } from '@testing-library/react'; +import { ProductAccessibilityGate } from './ProductAccessibilityGate'; + +const { checkMock } = vi.hoisted(() => ({ + checkMock: vi.fn() +})); + +vi.mock('../lib/runtime', async () => { + const actual = await vi.importActual('../lib/runtime'); + return { + ...actual, + tradingKillSwitchClient: { + check: checkMock + } + }; +}); + +describe('ProductAccessibilityGate smoke', () => { + beforeEach(() => { + checkMock.mockReset(); + }); + + it('renders children when product is available', async () => { + checkMock.mockResolvedValue({ disabled: false }); + + render( + +
workspace-loaded
+
+ ); + + await waitFor(() => { + expect(screen.getByText('workspace-loaded')).toBeInTheDocument(); + }); + }); + + it('blocks workspace when control plane disables the product', async () => { + checkMock.mockResolvedValue({ + disabled: true, + message: 'Trading access disabled by control plane' + }); + + render( + +
workspace-loaded
+
+ ); + + await waitFor(() => { + expect(screen.getByText('Trading temporarily unavailable')).toBeInTheDocument(); + expect(screen.getByText('Trading access disabled by control plane')).toBeInTheDocument(); + }); + expect(screen.queryByText('workspace-loaded')).not.toBeInTheDocument(); + }); +});