# Flutter Admin Portal TDD Plan **Scope:** Validate the functionality defined in `FLUTTER-ADMIN-PORTAL.md` using test-first development across server API + Flutter UI/repository layers. **Primary objective:** Ship an admin portal that reliably shows retention/cleanup/backfill history, prioritizes unresolved failures/rate limits, and presents concise expandable details. --- ## 1. Test strategy at a glance Use a layered pyramid so core behavior is proven at the cheapest layer first: | Layer | Purpose | Tools | |---|---|---| | Unit (server) | pin/severity/status logic, query mapping | `dart test` | | Integration (server) | auth, DB query shape/order, endpoint behavior | `dart test` + Postgres test DB | | Unit (Flutter) | DTO mapping, repository merge, parsing, formatting | `flutter test` | | Widget (Flutter) | pinned section, newest-first list, expansion details, pagination interactions | `flutter test` | | Optional e2e/manual | confidence pass with real API + seeded rows | local runbook | --- ## 2. Coverage targets (reasonable) Aim for practical coverage without over-testing visual polish: - **Server admin handler package:** >= **85% line coverage** - **Server pin/severity helper logic:** >= **95% line coverage** - **Flutter admin repository/models:** >= **90% line coverage** - **Flutter admin widgets/screens:** >= **75% line coverage** - **Critical behavior cases:** **100% scenario coverage** for: - unresolved errors pinned - unpin after successful retry by same `kind` - newest-first ordering - expanded full error text display - no raw data fields rendered --- ## 3. Test data model fixtures Create reusable fixture builders for `market_data_sync_runs` rows: ```text run(id, kind, startedAt, finishedAt, rowsWritten, rowsRemoved, error) ``` Named fixture sets: 1. `all_success_recent_first` 2. `rate_limit_unresolved` 3. `failed_then_success_same_kind` 4. `partial_backfill_error` 5. `in_progress_stale` 6. `mixed_kinds_mixed_outcomes` Keep fixtures concise; avoid raw bar payloads by design. --- ## 4. Server TDD plan Target files (planned): - `server/lib/handlers/market_history_admin_handler.dart` - `server/lib/trading/market_history_admin_query.dart` (or equivalent helper) - `server/test/integration/market_history_admin_handler_test.dart` - `server/test/trading/market_history_admin_logic_test.dart` ### 4.1 RED: pin/severity/status logic unit tests Write tests before implementation: 1. **Pinned unresolved failure** - failed `backfill` with no later success => pinned 2. **Unpin after retry success** - failed `backfill` then later successful `backfill` => old failure not pinned 3. **Pinned is per-kind** - `cleanup` success does not clear `backfill` failure pin 4. **Rate limit classification** - `error` containing `429` or `rate limited` => `severity=rate_limit` 5. **Partial success classification** - `rows_written>0 && error!=null` => `status=partial` 6. **In-progress classification** - `finished_at==null` recent => `status=in_progress` - stale threshold exceeded => `severity=warning/error` (per chosen policy) 7. **Newest-first ordering** - runs sorted by `started_at DESC` ### 4.2 GREEN: minimal implementation Implement pure helper functions first: - `deriveSeverity(error, finishedAt, startedAt, now)` - `deriveStatus(error, finishedAt, rowsWritten, rowsRemoved)` - `computePinned(runs)` - `toSummary(run)` Keep these deterministic and injectable with `clock` for testability. ### 4.3 RED: handler integration tests `market_history_admin_handler_test.dart`: 1. **403 for non-admin token** 2. **200 + shape for admin** 3. **`runs` newest-first** 4. **`pinned` contains unresolved rate-limit row** 5. **`pinned` clears when later success seeded** 6. **`limit` respected** 7. **pagination via `before`** 8. **`kind` filter** 9. **response excludes raw fields** (no `raw`, no snapshot payload) ### 4.4 GREEN: handler + routing Implement route: - `GET /v1/admin/market-history/sync-runs` Wire auth middleware with admin allowlist/claim gate. ### 4.5 REFACTOR - Move query + transform logic out of handler into service/query class. - Ensure all SQL is parameterized. - Add response DTO class to reduce map-shape drift. --- ## 5. Flutter TDD plan Planned files: - `lib/admin/models/sync_run_event.dart` - `lib/admin/services/market_history_admin_api.dart` - `lib/admin/repositories/sync_run_log_repository.dart` - `lib/admin/screens/market_history_log_screen.dart` - `lib/admin/widgets/sync_run_expansion_tile.dart` Tests: - `test/admin/models/sync_run_event_test.dart` - `test/admin/repositories/sync_run_log_repository_test.dart` - `test/admin/services/market_history_admin_api_test.dart` - `test/admin/widgets/sync_run_expansion_tile_test.dart` - `test/admin/screens/market_history_log_screen_test.dart` ### 5.1 RED: model + repository unit tests 1. Parse API response into domain model 2. Severity/status mapping from API and fallback parsing 3. Merge pinned + history sections 4. Keep pinned visible above chronological list 5. Append paged results without reordering newer rows 6. Deduplicate by `id` during refresh 7. Error state handling (`401/403/500/network`) ### 5.2 GREEN: implement model/repository - Implement immutable model objects - Implement repository APIs: - `loadInitial()` - `refresh()` - `loadMore()` - Include simple in-memory cache for current session ### 5.3 RED: service HTTP tests Using mocked HTTP client: 1. sends bearer token header 2. sends `limit`, `before`, `kind` query params 3. handles non-200 responses 4. parse malformed payload guardrails ### 5.4 GREEN: service implementation Mirror existing app API style (`apiBaseUrl`, auth token retrieval, robust parse). ### 5.5 RED: widget/screen tests 1. **Pinned section shown** when pinned rows exist 2. **Pinned section hidden** when none exist 3. **Newest row at top** in history list 4. **Expansion tile details** - shows full message text - does not show raw data fields 5. **Severity styling** (icon/chip text present) 6. **Pull-to-refresh triggers repository refresh** 7. **Scroll-to-end triggers `loadMore()`** 8. **Loading, empty, and error states render correctly** ### 5.6 GREEN: implement UI - Build screen with two sections: - `Needs attention` (pinned) - `History` (paged, newest-first) - Use `ExpansionTile` rows with concise collapsed summary. ### 5.7 REFACTOR - Extract common chips/icons/date formatters - Keep widget tree shallow for testability - Avoid business logic in widget build methods --- ## 6. API-trigger actions (optional phase C) TDD If adding on-demand actions: Server tests: - `POST /v1/admin/market-data/resync` returns `202` and creates run rows - `POST /v1/admin/market-data/cleanup` with `archive` flag routes correctly - auth + invalid input tests Flutter tests: - retry button visible for pinned failures - confirm dialog for cleanup - action triggers refresh and shows new in-progress/success rows This phase can be deferred without affecting MVP log validation. --- ## 7. Quality gates and CI commands ### Server ```bash cd server && dart test test/trading/market_history_admin_logic_test.dart cd server && dart test test/integration/market_history_admin_handler_test.dart ``` ### Flutter ```bash flutter test test/admin/models/ flutter test test/admin/repositories/ flutter test test/admin/services/ flutter test test/admin/widgets/ flutter test test/admin/screens/ ``` ### Coverage checks (recommended) Server: ```bash cd server && dart test --coverage=coverage ``` Flutter: ```bash flutter test --coverage ``` Add CI thresholds for the targets in Section 2. --- ## 8. Risk-driven test checklist High-priority cases to lock down early: - [x] Pin logic regression when multiple kinds interleave - [x] Rate-limit text variants (`429`, `rate limit`, capitalization differences) - [x] Very long error message expansion rendering - [x] Pagination duplicates/holes on refresh + load-more race - [x] Timezone display mismatch (`started_at` UTC vs local display) - [x] Empty payload safety (no crashes) --- ## 9. Definition of done The admin portal feature is considered validated when: 1. All MVP tests listed above pass. 2. Coverage targets in Section 2 are met. 3. Automated acceptance tests (`test/admin/acceptance/`) cover: - newest-first order, - pinned unresolved issues, - expandable full error detail, - no raw payload details in UI, - cleanup archive toggle when enabled. 4. CI includes these test suites and fails on regression. Manual sanity pass (live server + seeded rows) remains recommended before production rollout. --- ## 10. Suggested implementation order 1. Server pure logic tests + implementation ✅ 2. Server handler integration tests + endpoint ✅ 3. Flutter models/repository/service tests + implementation ✅ 4. Flutter widget/screen tests + UI implementation ✅ 5. Optional trigger actions phase ✅ 6. Risk checklist (§8) + acceptance tests (§9) ✅ This order keeps risky logic proven first and gives UI teams a stable API contract early.