308 lines
8.9 KiB
Markdown
308 lines
8.9 KiB
Markdown
# 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.
|
|
|