test: add release smoke coverage and runbook

This commit is contained in:
Saravana Achu Mac 2026-04-04 17:07:48 -07:00
parent 6c39b9b185
commit 790213513f
5 changed files with 126 additions and 25 deletions

View File

@ -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

View File

@ -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

View File

@ -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"

19
scripts/smoke-release.sh Normal file
View File

@ -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

View File

@ -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(
<ProductAccessibilityGate>
<div>workspace-loaded</div>
</ProductAccessibilityGate>
);
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(
<ProductAccessibilityGate>
<div>workspace-loaded</div>
</ProductAccessibilityGate>
);
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();
});
});