8.9 KiB
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:
run(id, kind, startedAt, finishedAt, rowsWritten, rowsRemoved, error)
Named fixture sets:
all_success_recent_firstrate_limit_unresolvedfailed_then_success_same_kindpartial_backfill_errorin_progress_stalemixed_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.dartserver/lib/trading/market_history_admin_query.dart(or equivalent helper)server/test/integration/market_history_admin_handler_test.dartserver/test/trading/market_history_admin_logic_test.dart
4.1 RED: pin/severity/status logic unit tests
Write tests before implementation:
- Pinned unresolved failure
- failed
backfillwith no later success => pinned
- failed
- Unpin after retry success
- failed
backfillthen later successfulbackfill=> old failure not pinned
- failed
- Pinned is per-kind
cleanupsuccess does not clearbackfillfailure pin
- Rate limit classification
errorcontaining429orrate limited=>severity=rate_limit
- Partial success classification
rows_written>0 && error!=null=>status=partial
- In-progress classification
finished_at==nullrecent =>status=in_progress- stale threshold exceeded =>
severity=warning/error(per chosen policy)
- Newest-first ordering
- runs sorted by
started_at DESC
- runs sorted by
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:
- 403 for non-admin token
- 200 + shape for admin
runsnewest-firstpinnedcontains unresolved rate-limit rowpinnedclears when later success seededlimitrespected- pagination via
before kindfilter- 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.dartlib/admin/services/market_history_admin_api.dartlib/admin/repositories/sync_run_log_repository.dartlib/admin/screens/market_history_log_screen.dartlib/admin/widgets/sync_run_expansion_tile.dart
Tests:
test/admin/models/sync_run_event_test.darttest/admin/repositories/sync_run_log_repository_test.darttest/admin/services/market_history_admin_api_test.darttest/admin/widgets/sync_run_expansion_tile_test.darttest/admin/screens/market_history_log_screen_test.dart
5.1 RED: model + repository unit tests
- Parse API response into domain model
- Severity/status mapping from API and fallback parsing
- Merge pinned + history sections
- Keep pinned visible above chronological list
- Append paged results without reordering newer rows
- Deduplicate by
idduring refresh - 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:
- sends bearer token header
- sends
limit,before,kindquery params - handles non-200 responses
- 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
- Pinned section shown when pinned rows exist
- Pinned section hidden when none exist
- Newest row at top in history list
- Expansion tile details
- shows full message text
- does not show raw data fields
- Severity styling (icon/chip text present)
- Pull-to-refresh triggers repository refresh
- Scroll-to-end triggers
loadMore() - 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
ExpansionTilerows 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/resyncreturns202and creates run rowsPOST /v1/admin/market-data/cleanupwitharchiveflag 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
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
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:
cd server && dart test --coverage=coverage
Flutter:
flutter test --coverage
Add CI thresholds for the targets in Section 2.
8. Risk-driven test checklist
High-priority cases to lock down early:
- Pin logic regression when multiple kinds interleave
- Rate-limit text variants (
429,rate limit, capitalization differences) - Very long error message expansion rendering
- Pagination duplicates/holes on refresh + load-more race
- Timezone display mismatch (
started_atUTC vs local display) - Empty payload safety (no crashes)
9. Definition of done
The admin portal feature is considered validated when:
- All MVP tests listed above pass.
- Coverage targets in Section 2 are met.
- 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.
- 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
- Server pure logic tests + implementation ✅
- Server handler integration tests + endpoint ✅
- Flutter models/repository/service tests + implementation ✅
- Flutter widget/screen tests + UI implementation ✅
- Optional trigger actions phase ✅
- Risk checklist (§8) + acceptance tests (§9) ✅
This order keeps risky logic proven first and gives UI teams a stable API contract early.