From e16cbda46447fd3a350c446a4d4ffbfb02ea0c05 Mon Sep 17 00:00:00 2001 From: Dzarlax Date: Sat, 6 Jun 2026 14:40:05 +0200 Subject: [PATCH] Add focused test coverage roadmap --- CONTRIBUTING.md | 2 + docs/TEST_COVERAGE.md | 35 ++++ ...sue-149-focused-test-coverage-roadmap.html | 162 ++++++++++++++++++ internal/applehealth/parse_test.go | 24 +++ .../testdata/focused_edge_export.xml | 9 + 5 files changed, 232 insertions(+) create mode 100644 docs/TEST_COVERAGE.md create mode 100644 docs/ai-plans/2026-06-06-issue-149-focused-test-coverage-roadmap.html create mode 100644 internal/applehealth/testdata/focused_edge_export.xml diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a81df28..d5274dc 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -25,6 +25,8 @@ Dependencies are managed through normal Go module resolution. Review dependency DB-backed integration tests skip when no Postgres connection is configured. To run them, provide libpq environment variables or `READINESS_TEST_DSN`. Tests create throwaway schemas and drop them during cleanup. +See `docs/TEST_COVERAGE.md` for the current focused coverage roadmap and fixture rules. + ### CI and race detector policy The default required-looking CI job is `Build, Vet & Test`. Keep that job name stable unless repository branch protection is updated at the same time. diff --git a/docs/TEST_COVERAGE.md b/docs/TEST_COVERAGE.md new file mode 100644 index 0000000..50e4dd3 --- /dev/null +++ b/docs/TEST_COVERAGE.md @@ -0,0 +1,35 @@ +# Focused Test Coverage Roadmap + +This roadmap tracks high-value Health Dashboard test coverage without turning the suite into a slow infrastructure exercise. + +## Constraints + +- Default CI must stay pure and fast under `go test ./...`. +- Fixtures must be synthetic or anonymized. Do not commit personal health exports, screenshots, logs, or raw metric dumps. +- DB-backed tests should skip cleanly unless a Postgres connection is configured. +- Coverage thresholds are intentionally deferred until the active product contracts have a useful baseline. + +## Current Coverage Map + +| Area | Current state | Next useful coverage | +|---|---|---| +| `internal/health` formulas | Strong coverage for readiness, evidence payloads, EnergyBank v2, stress, i18n labels, and anomaly helpers. | Add tests only when formula or evidence contracts change. | +| Apple Health import | Synthetic XML fixtures cover mapped records, empty input, malformed input, sleep-stage mapping, and focused parser edge cases. | Add fixtures for real bug classes after reducing them to minimal synthetic XML. | +| UI and admin APIs | Contract tests cover admin pages, auth/session behavior, webhook dispatch, dashboard sections, and tenant scope. | Pin response-shape changes before frontend code starts depending on them. | +| Storage writers | Readiness redesign, EnergyBank, sleep gates, freshness, calibration, and tenant helpers have focused tests, with DB-backed tests gated. | Prefer bug-driven storage contract tests over broad repository-level sweeps. | +| Notifications | Morning/evening report, freshness banners, smart retry, Telegram webhook, and proactive framework tests cover key behavior. | Add regression tests when notification timing or skip conditions change. | +| CI policy | Default CI runs build, vet, and pure tests; race detector is manual and documented. | Keep race detector separate from default PR checks unless the runtime cost becomes acceptable. | + +## Delivered First Batch + +Issue #149 starts with a small Apple Health import safety batch: + +- `internal/applehealth/testdata/focused_edge_export.xml` is a synthetic fixture with no personal data. +- `TestParseXMLFocusedEdgeFixturePinsImportSafety` checks percent normalization boundaries, duration-derived category metrics, stand-hour mapping, unknown quantity fallback, invalid duration rejection, and unsupported correlation skipping. + +## Candidate Follow-Ups + +- Dashboard API response stability tests for fields consumed by `static/app.js` and `static/charts.js`. +- Import ZIP wrapper tests around missing `export.xml`, progress status, and parse error propagation. +- Storage contract tests for freshness/readiness behavior when a bug or product change proves the need. +- One informational `go test -cover` baseline after the focused lanes are less sparse, without enforcing a threshold. diff --git a/docs/ai-plans/2026-06-06-issue-149-focused-test-coverage-roadmap.html b/docs/ai-plans/2026-06-06-issue-149-focused-test-coverage-roadmap.html new file mode 100644 index 0000000..b258841 --- /dev/null +++ b/docs/ai-plans/2026-06-06-issue-149-focused-test-coverage-roadmap.html @@ -0,0 +1,162 @@ + + + + + Implementation Plan: Issue #149 Focused Test Coverage Roadmap + + + + +
+

Implementation Plan: Issue #149 Focused Test Coverage Roadmap

+

Approved and implemented

+ +
+

Task Summary

+

Create a current, focused test coverage roadmap for active Health Dashboard behavior. This should update the issue/doc roadmap and add only high-value missing tests, not introduce broad coverage thresholds or slow infrastructure.

+
+ +
+

Current Behavior

+
    +
  • The repository currently has 78 Go test files under internal and related packages.
  • +
  • Existing tests already cover major active areas: health formulas, readiness evidence, EnergyBank v2, stress validation, Apple Health XML parser fixtures, UI admin contracts, tenant scope, webhook dispatch, and storage writers.
  • +
  • internal/applehealth/testdata already has synthetic, malformed, and empty XML fixtures.
  • +
  • CONTRIBUTING.md states DB-backed tests skip when no Postgres connection is configured and warns against real personal health data in fixtures.
  • +
  • CI currently runs the full pure test suite under go test ./....
  • +
+
+ +
+

Desired Behavior

+
    +
  • The repo has a short, current roadmap that identifies remaining high-value test gaps instead of implying the test suite is empty.
  • +
  • New tests target product contracts that change often: readiness freshness, EnergyBank v1/v2 compatibility boundaries, import conflict resolution, and API response stability.
  • +
  • All tests added in this lane run under the existing default go test ./... path unless explicitly DB-gated and justified.
  • +
  • No real personal health data is committed.
  • +
+
+ +
+

Assumptions And Unknowns

+
    +
  • Assumption: this issue is best handled as a roadmap plus a small first batch of tests, not a large test sweep.
  • +
  • Assumption: issue #166 should own readiness freshness contract tests if implemented first.
  • +
  • Unknown: whether the user wants child GitHub issues created for each coverage lane or a single living doc section.
  • +
  • Unknown: whether coverage measurement should be introduced now; the issue says threshold should wait until there is a meaningful baseline.
  • +
+
+ +
+

Files Likely To Change

+
    +
  • docs/ARCHITECTURE.md, README.md, or CONTRIBUTING.md - add the current test coverage roadmap.
  • +
  • internal/health/*_test.go - add focused invariant tests only where gaps remain.
  • +
  • internal/applehealth/testdata/* and internal/applehealth/parse_test.go - add a small anonymized fixture only if current fixtures do not cover a real parser risk.
  • +
  • internal/ui/*_test.go - add API response contract tests if readiness/AI/dashboard shapes are changed.
  • +
  • GitHub issue comments or child issues - optional, if the roadmap should live in GitHub rather than docs.
  • +
+
+ +
+

Implementation Steps

+
    +
  1. Produce a compact test inventory grouped by package and behavior.
  2. +
  3. Map the issue's candidate lanes to current coverage and mark each as covered, partial, or missing.
  4. +
  5. Choose a first small batch of tests that protects active product behavior without DB/network requirements.
  6. +
  7. Add or update docs with the roadmap and "do not commit personal health data" fixture rule.
  8. +
  9. If GitHub child issues are desired, create them after user approval with clear scopes and dependencies.
  10. +
  11. Run the default test suite and vet.
  12. +
+
+ +
+

Impact

+
    +
  • CI: should remain fast; no coverage threshold or race job in this issue.
  • +
  • Data: no production DB changes; fixtures must stay synthetic/anonymized.
  • +
  • Product behavior: tests should pin existing behavior unless paired with an approved product change.
  • +
  • GitHub process: optional child issues can make the roadmap easier to execute incrementally.
  • +
+
+ +
+

Test Plan

+
    +
  • go test ./internal/health ./internal/applehealth ./internal/ui ./internal/storage -count=1
  • +
  • go test ./...
  • +
  • go vet ./...
  • +
  • If fixtures are added: manually inspect fixture content for personal data before commit.
  • +
+
+ +
+

Risks And Edge Cases

+
    +
  • A generic coverage push can create brittle tests that slow useful development.
  • +
  • DB-backed tests can become flaky if they assume a developer has a local database; keep them opt-in unless necessary.
  • +
  • Fixtures can accidentally leak personal data; prefer minimal synthetic XML.
  • +
  • This issue overlaps with #166 and #152; keep readiness contract tests and race policy in their respective issues where possible.
  • +
+
+ +
+

Rollback Plan

+

Revert roadmap docs and any added tests. If child issues are created incorrectly, close or relabel them rather than deleting historical context.

+
+ +
+

Open Questions

+
    +
  • Should the roadmap live in docs, GitHub child issues, or both?
  • +
  • Which first coverage batch is most valuable after #166: import safety, EnergyBank compatibility, or dashboard API stability?
  • +
  • Should coverage metrics be measured once as an informational baseline, without enforcing a threshold?
  • +
+
+ +
+

Approval Gate

+

Approved by the user on 2026-06-06 before production code changes.

+
+ +
+

Implementation Result

+
    +
  • Added docs/TEST_COVERAGE.md as the living focused coverage roadmap.
  • +
  • Linked the roadmap from CONTRIBUTING.md.
  • +
  • Added a synthetic Apple Health edge fixture at internal/applehealth/testdata/focused_edge_export.xml.
  • +
  • Added TestParseXMLFocusedEdgeFixturePinsImportSafety to pin percent normalization boundaries, duration-derived category metrics, stand-hour mapping, unknown quantity fallback, invalid duration rejection, and unsupported correlation skipping.
  • +
  • No real personal health data was added.
  • +
+
+ +
+

Verification Run

+
    +
  • go test ./internal/health ./internal/applehealth ./internal/ui ./internal/storage -count=1
  • +
  • go test ./...
  • +
  • go vet ./...
  • +
  • git diff --check
  • +
+
+ +
+

Known Limitations And Follow-Up

+
    +
  • This PR intentionally does not add a coverage threshold.
  • +
  • DB-backed coverage remains opt-in unless a product bug requires a focused storage contract test.
  • +
  • Candidate follow-ups are tracked in docs/TEST_COVERAGE.md rather than created as child issues in this first pass.
  • +
+
+
+ + diff --git a/internal/applehealth/parse_test.go b/internal/applehealth/parse_test.go index dc256eb..6ea2331 100644 --- a/internal/applehealth/parse_test.go +++ b/internal/applehealth/parse_test.go @@ -26,6 +26,21 @@ func TestParseXMLSyntheticFixtureMapsHealthKitRecords(t *testing.T) { assertNoPoint(t, points, "sleep_total", "2026-01-02 02:00:00 +0000") } +func TestParseXMLFocusedEdgeFixturePinsImportSafety(t *testing.T) { + points := collectXMLFixturePoints(t, "testdata/focused_edge_export.xml") + + if got, want := len(points), 4; got != want { + t.Fatalf("point count = %d, want %d: %+v", got, want, points) + } + assertPoint(t, points, "blood_oxygen_saturation", "%", "2026-02-01 06:00:00 +0100", "Synthetic Watch", 98) + assertPoint(t, points, "mindful_minutes", "min", "2026-02-01 09:00:00 +0100", "Synthetic Phone", 15) + assertPoint(t, points, "apple_stand_hour", "count", "2026-02-01 10:00:00 +0100", "Synthetic Watch", 1) + assertPoint(t, points, "some_new_metric", "count", "2026-02-01 13:00:00 +0100", "Synthetic Device", 12.5) + + assertMetricCount(t, points, "mindful_minutes", 1) + assertNoMetric(t, points, "blood_pressure") +} + func TestParseXMLEmptyAndMalformedInputs(t *testing.T) { var emitted bool if err := parseXMLFixture("testdata/empty_export.xml", func(points []storage.MetricPoint) { @@ -87,6 +102,15 @@ func assertNoPoint(t *testing.T, points []storage.MetricPoint, metric, date stri } } +func assertNoMetric(t *testing.T, points []storage.MetricPoint, metric string) { + t.Helper() + for _, p := range points { + if p.MetricName == metric { + t.Fatalf("unexpected metric=%s point: %+v", metric, p) + } + } +} + func assertMetricCount(t *testing.T, points []storage.MetricPoint, metric string, want int) { t.Helper() got := 0 diff --git a/internal/applehealth/testdata/focused_edge_export.xml b/internal/applehealth/testdata/focused_edge_export.xml new file mode 100644 index 0000000..b8b75e0 --- /dev/null +++ b/internal/applehealth/testdata/focused_edge_export.xml @@ -0,0 +1,9 @@ + + + + + + + + +