Commit ec7e85c
Experimental touch (#80)
* test(web): cover + harden src/features/dashboard/widgets/SleepEfficiencyWidget.tsx
Fix: forward the useSleepEfficiency error to WidgetShell so a failed request surfaces a QueryError instead of the misleading 'No sleep efficiency data' empty state (matching RouteEfficiencyWidget/RegenEfficiencyWidget). Add a comprehensive Vitest + RTL suite covering vehicle resolution, loading/empty/error states, the populated gauge + stats, the asleep/offline sleep-total filter, gauge colour thresholds, null-safety, the compact layout, the help-tooltip a11y label, and the freshness refresh interaction.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/features/onboarding/tours/alertsTour.ts
Add co-located Vitest coverage for ALERTS_TOUR (definition metadata, step shape, routeMatch regex + registry integration, and onShow navigation incl. same-page no-op and pushState-throw resilience). Harden navigate() so a history.pushState SecurityError inside onShow can no longer bubble out of the useTour effect and crash the walkthrough.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/features/dashboard/widgets/WeatherAtCarWidget.tsx
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/lib/activityIcons.ts
Register the real past-tense automation audit actions the Go backend writes to audit_logs (internal/automation/audit.go: created/updated/deleted/enabled/disabled/re_enabled/test_run/undo/imported/exported/executed/failed/auto_disabled). The registry only had imperative keys (automation.create/update/delete) that never match a real feed entry, so every automation event collapsed onto the generic fallback. Additive change: existing entries and tests are untouched; adds matching i18n keys in en.json and tests asserting each action resolves to its own visual, failures flag distinctly, and unlisted verbs still degrade to the domain entry.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/features/dashboard/widgets/WeeklyDigestWidget.tsx
Fix two real unit-conversion bugs surfaced by the new tests:
- Distance: distanceKm (km) was fed straight into convertDistanceFromSI
(which expects SI metres), skewing values ~1609x (62 mi rendered as 0.0).
Now lifts km -> SI metres -> display unit via the lib.
- Efficiency: Wh/km was double-converted (MI_TO_KM then *1.609344), wrong in
both unit branches. Now scaled once by the lib-derived km-per-display-unit
span (250 Wh/km -> 402 Wh/mi / 250 Wh/km).
Harden: keep cached metrics on background-refetch error (error && !data),
stable useCallback refresh handler, drop unused UNITS import.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/features/onboarding/tours/debuggerTour.ts
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/features/dashboard/widgets/TripSummaryWidget.tsx
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/features/dashboard/widgets/TirePressureVisualWidget.tsx
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/features/onboarding/tours/automationsTour.ts
Add a comprehensive co-located Vitest suite covering AUTOMATIONS_TOUR metadata, registry wiring, step ordering/well-formedness, routeMatch regex, and the first step's onShow navigation (off-route push, already-on-route no-op, and history-failure paths).
Harden the internal navigate() helper: wrap history.pushState + popstate dispatch in try/catch so a SecurityError or throwing listener cannot escape the React effect that runs onShow and tear down the tour overlay. Mirrors the defensive style in @/lib/tourRegistry.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/lib/fsm.ts
Add getStateConfig(map, state, fallbackLabel?) — a null-safe,
prototype-pollution-safe resolver returning a neutral fallback
StateConfig (never undefined) so callers can read .label/.color/.variant
without a guard. Mirrors the enums.ts / FSMBadge / getActivityVisual
fallback idiom; reuses a module-level singleton on the hot path.
Add comprehensive co-located Vitest coverage for every export: data
integrity across all six state maps (valid hex color, valid variant,
non-empty label), a backend-FSM drift guard on vehicleStates, and full
getStateConfig behaviour — exact hits, unknown/null/undefined/empty
fallback, custom fallback label, prototype-key regression, purity and
the StateConfig shape contract.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/features/dashboard/widgets/VehicleHeroWidget.tsx
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/features/onboarding/tours/chargingTour.ts
Add comprehensive Vitest coverage for the charging onboarding tour: identity/shape, launcher-only (no autoStart), registry resolution, step well-formedness + unique ordered spotlight targets, routeMatch RegExp accept/reject, and the onShow imperative-navigation side effects (pushState + popstate), the no-op guard when already on-route, and the invariant that every navigating step lands on a path its own routeMatch covers.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/features/dashboard/widgets/TelemetryErrorsWidget.tsx
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/features/telemetry/hooks/useSignalGapAnalysis.ts
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/features/dashboard/widgets/types.ts
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/features/system/commands.ts
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/features/dashboard/widgets/WallConnectorWidget.tsx
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/features/system/hooks/useStatusLiveSSE.ts
Add a comprehensive Vitest suite (19 cases) covering every export and branch of useStatusLiveSSE: connect/credentials/endpoint, disabled short-circuit, open/status/heartbeat/error transitions, exponential backoff with the 30s cap, manual reconnect, unmount cleanup, and visibility-driven recovery.
Fix a real bug the tests surface: the status handler only guarded JSON.parse against throws, so a valid-JSON-but-non-object frame (e.g. 'null', a number, an array) overwrote the last good snapshot and blanked the status page. Extract an exported, testable parseStatusSnapshot guard that rejects non-object frames, mirroring parseSignalChangeEvent.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/features/dashboard/widgets/VersionInfoWidget.tsx
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/lib/report.ts
Harden the (previously untested) print-report utilities:
- Escape HTML in interpolated vehicle/user data to prevent injection into
the document.write print window (XSS fix).
- Null-guard the whole drive/vehicle/stats inputs so null args no longer throw.
- Guard the average-speed divide-by-zero (zero duration -> em dash instead of
a bogus distance*60 value).
- Show a consistent em-dash placeholder for a missing distance in the details
table (was showing 0.0 km).
- Return a boolean so callers can detect a blocked popup.
Add a co-located Vitest suite covering both exports: full document output,
print/close calls, popup-blocked path, HTML escaping, null-safety, and the
divide-by-zero guard.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/lib/colors.ts
- Add comprehensive Vitest suite (51 cases) covering every export:
static palettes, charger/status/battery maps, all threshold color
helpers with branch + boundary coverage, buildChartPalette purity/
determinism/degenerate-input handling, and the useThemeChartPalette hook.
- Fix statusHexColor null crash: guard (status ?? '').toLowerCase() and
widen the signature to string | undefined | null (mirrors stateHexColor);
API status fields can be absent and previously threw.
- Harden activityColor for nullish input via �ctivity ?? ''.
- Memoise useThemeChartPalette so chart widgets receive a referentially
stable palette across re-renders instead of a fresh object each time.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/features/telemetry/hooks/useLiveSignalStream.ts
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/features/dashboard/widgets/VehicleSpecsWidget.tsx
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/lib/broadcastTopics.ts
Add a comprehensive co-located vitest suite (16 cases, 46 assertions) locking the broadcast-topic registry contract: value hygiene (non-empty/trimmed/unique/wire-format), SCREAMING_SNAKE keys, canonical wire-string pins, a 1:1 golden-list mirror of every BroadcastMessage discriminator, and FORMATTER_AFFECTING_TOPICS membership/shape.
Fix the registry gap the mirror test surfaces: BroadcastMessage has a 'font.changed' variant with no TOPICS constant, forcing FontProvider to inline the raw string in two places -- exactly the typo-prone anti-pattern this registry exists to prevent. Register TOPICS.FONT_CHANGED and rewire FontProvider to use it.
Harden the source: add 'satisfies Record<string, BroadcastMessage[\ ype\]>' so a typo'd topic value becomes a tsc error instead of a silently dropped message, while 'as const' keeps Topic as the exact literal union.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/lib/chartTypography.ts
Add comprehensive Vitest coverage for every export (getChartFontFamily,
getChartFontScale, getChartFontSize) plus edge/failure paths, and harden
the source against two latent bugs the tests surfaced:
- getChartFontScale now clamps an absurd/malformed --font-scale to a
defensive ceiling, honouring its 'clamped to a sane range' contract.
- getChartFontSize coerces a NaN/0/negative base to the 11px default so
an invalid, unrenderable SVG font size is never produced.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/features/notifications/schemas/alertRule.ts
Add comprehensive Vitest coverage for the Alert Studio rule schema
(constants, signal/computed-metric branches, range operators, value-shape
contract, escalation invariants, and the AlertRuleFormData type).
Fix a latent contract bug the tests surfaced: the value-shape refinement
documented an 'exactly one typed value' rule but only rejected zero. It
now also rejects >1 (e.g. value_num AND value_text set together), which
is ambiguous for the backend comparison.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/features/dashboard/widgets/WidgetShell.tsx
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/features/notifications/lib/recommendedTriggerMode.ts
Relocate the recommendedTriggerMode unit test from the feature-root __tests__/ dir to a co-located lib/ test (where the elevation gate looks) and enhance it into a strict superset: per-operator mapping, semantic once/repeat partition, exhaustiveness/drift guard against ALERT_RULE_OPS + ALERT_RULE_TRIGGER_MODES, purity/determinism, and the defensive default-branch fallback. Source is already exhaustive + null-safe at its typed callsite, so no source churn.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/features/dashboard/widgets/WarrantyStatusWidget.tsx
Fix miles->meters conversion bug: warranty *_mi fields (miles) were fed into convertDistanceFromSI which expects SI meters, rendering a 50,000 mi limit as ~31 mi. Convert miles->meters up front, matching sibling widgets. Also export pure helpers for testing, stabilize toDistanceDisplay via useCallback, collapse duplicated compact WidgetShell props, and guard the mileage bar against a zero limit. Adds a comprehensive Vitest suite (21 cases) covering every export, all render states, coverage-badge branches, refetch wiring, and mi/km conversion.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/lib/parseSettingEnum.ts
Accept unknown and narrow via asNonEmptyString so the setting helpers never throw on non-string raw signal values (mirrors safetyEnum/parseEnums). Fix isSettingMiles/isSettingFahrenheit to detect the codec-stripped 'mi'/'f' short forms the enumMappings table already lists — without them GeneralSettings mis-synced the app to km/Celsius. Add a comprehensive co-located Vitest suite covering every export.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/lib/cn.ts
Add comprehensive Vitest coverage for the app-wide cn() className composer: clsx composition (strings/arrays/objects, deep nesting, falsy pruning, conditional idiom) and tailwind-merge last-wins conflict resolution (padding, text color, dedup, override-beats-base). Make the public return type explicit (string).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/features/onboarding/tourSteps.ts
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/lib/inlineHelpAllowlist.ts
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/lib/metricSemantics.ts
Add comprehensive Vitest coverage for the metric-semantics registry and resolveSemantic(), and fix a real crash: resolveSemantic(null|undefined) threw a TypeError at the 'id' in metric check, violating the file's documented never-crash contract. Widen the param to accept nullish input and fall back to a neutral semantic.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(api): cover + harden cmd/ai-eval
Add comprehensive table-driven tests for the ai-eval CLI covering run()
flag parsing, feature selection, fast/judge/record modes, JUnit output,
error wrapping, and the documented 0/1/2 exit-code contract (via a
subprocess re-exec of main). Harden JUnit file writing to surface close
errors so a truncated report never reports success.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/features/onboarding/tours/drivesTour.ts
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/features/onboarding/tours/vehiclesTour.ts
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/features/dashboard/widgets/YearReviewWidget.tsx
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/lib/signalObservation.ts
Add comprehensive Vitest coverage for latestNumeric/latestBool/latestText (head-row contract, 0/false/'' preservation, undefined/empty/null branches) and harden latestNumeric to coerce NaN/+/-Infinity to null so non-finite readings never leak past downstream != null guards or corrupt chart aggregations.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/lib/automations.ts
Add co-located Vitest suite covering every export (isStep, findStepByKind) across kind-match, mismatch, union narrowing, first-match, empty/no-match, and non-mutation facets. Harden both guards against nullish input (partial API payloads) so probing an absent step/list returns false/undefined instead of throwing on .kind / .find.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(api): cover + harden cmd/slo-coverage-audit
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/lib/numberFormat.ts
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/lib/appIcon.ts
Add comprehensive co-located Vitest coverage for every export (buildAppIconSvg,
svgToDataUrl, renderSvgToPngDataUrl) across all icon modes, colour-guard
branches, data-URL round-trip, btoa-absent fallback, and canvas rasterise
success/error/no-ctx/no-DOM paths.
Fix: safeHex accepted 5- and 7-digit hex strings (invalid CSS colours) which
would blank the SVG gradient — the exact corruption the guard exists to
prevent. Tighten the regex to only the legal 3/4/6/8-digit notations.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/features/onboarding/TourLauncher.tsx
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/lib/signalCatalog.ts
Add a comprehensive Vitest suite (23 cases / 100 assertions) covering every
export: signalCatalog data integrity, signalCategories derivation,
getSignalMeta lookup, and normalizeGpsState branches/edges.
Fix a real bug surfaced by the tests: normalizeGpsState did not recognise the
Tesla firmware / proto-batch wire value "GpsLocked" (persisted verbatim by
internal/tesla/codec/coercion.go) nor a bare "fix", so it returned 'unknown'
and hid a genuine GPS lock in the UI. Both now map to 'locked'. The GpsState
catalog enumValues and JSDoc are updated to match.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/lib/sseManager.ts
Add a comprehensive Vitest suite for the singleton SSE manager (22 cases)
covering the full sseManager surface: subscribe/unsubscribe auto
connect/teardown, connect/disconnect idempotency, getState transitions,
lastMessageAt freshness, hasEverConnected latch, event dispatch, and the
exponential-reconnect backoff.
Harden the source (surfaced by the tests):
- safeParse() so malformed/empty SSE frames yield null instead of throwing
an uncaught error mid-dispatch; a bad connected frame still transitions
the wire to connected.
- doConnect() cancels any pending reconnect timer so an explicit connect()
racing the backoff cannot churn a duplicate EventSource.
- disconnect() and last-unsubscribe teardown reset failCount so a later
connection lifecycle starts from the base 1s backoff.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/features/onboarding/tours/mainTour.ts
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/lib/globalShortcuts.tsx
Add a co-located Vitest+RTL suite (7 cases, 77 assertions) covering the GlobalShortcuts seed component: null render with registry side effect, the four universal action keys, one navigation entry per GOTO_SHORTCUTS with interpolated labels, a palette entry per command with a shortcut hint (and none without), the global-scope + informational (no handler/match) contract, unique ids, and full unregister on unmount.
Harden the source by replacing the unsafe \c.shortcut as string\ cast with a type-guard filter over CommandDefinition (behaviour-preserving).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(api): cover + harden cmd/export-worker
Extract behavior-preserving helpers from main() and add table-driven,
race-safe tests. Fixes real bugs surfaced while testing:
- healthcheck probe leaked resp.Body and had no timeout (could hang)
- health handler produced malformed JSON when the driver error contained
quotes (string-interpolated) and skipped Content-Type on the error path
- healthcheck probe hardcoded :8082, ignoring HEALTH_PORT
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/features/telemetry/hooks/useThroughputHistory.ts
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/api/drives.ts
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/lib/geo.ts
Add a comprehensive co-located Vitest suite for lib/geo (haversineDistance,
isValidLatLng, hasMeaningfulRoute, firstValidIndex, MIN_MEANINGFUL_ROUTE_METERS)
covering identical/known/antipodal/non-finite distances, boundary and placeholder
coordinate validation, stationary-cluster vs meaningful-route detection, and
null-safety paths.
Fix a floating-point NaN bug in haversineDistance: the squared half-chord could
round just past 1 for near-antipodal points, making sqrt(1-a) NaN and poisoning
any cumulative distance sum. Clamp to [0,1] so antipodal inputs resolve to
~half Earth circumference.
Harden hasMeaningfulRoute/firstValidIndex to tolerate null/undefined lists and
null/undefined samples (telemetry JSON may omit fields) instead of throwing.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/lib/safeArray.ts
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/lib/errorClassification.ts
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/features/settings/searchIndex.ts
Add a comprehensive co-located Vitest suite (28 cases) covering every export: fuzzyMatch subsequence semantics, getSettingsIndex structural invariants + translator contract, and the full searchSettings scoring ladder with tie-break stability and real-index smoke matches.
Harden the two exported matchers with test-driven null-safety so the settings search box never white-screens on a malformed/partial entry or nullish query/index, and fix a self-contradictory fuzzyMatch doc comment. No public behaviour change for valid input.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/features/telemetry/signalGapUtils.ts
Fix two real staleness-math bugs the single-source-of-truth util lagged
behind (SignalCatalogPanel had already patched its local copies):
- deriveSignalRows: an unparseable timestamp string produced a bogus
'active' row with NaN staleness while computeGapBuckets counted the same
row as 'stale'. Validate the parse (Number.isFinite) and normalise to
'never' (timestamp null, staleness Infinity) so category, bucket tally,
and the rendered cell stay consistent.
- formatStaleness: fmtInt rounds, so 59.98 -> '60s ago' and 7199 ->
'1h 60m ago', and future timestamps printed '-3s ago'. Floor each segment
and clamp negatives to zero.
Add a comprehensive co-located Vitest suite covering every export
(constants, colours, deriveSignalRows, computeGapBuckets,
computeFreshnessPct, formatStaleness) incl. branch boundaries, nullish
value handling, and both regressions.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/lib/signals.ts
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(api): cover + harden cmd/chaos-runner
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/api/analytics.ts
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/lib/preferredRange.ts
Add comprehensive Vitest coverage for selectPreferredRange across its full branch matrix (type selection, label metadata, null safety, no cross-fallback, zero-preservation) and harden the selector to collapse non-finite values (NaN/±Infinity) to null so consumers render an empty state instead of 'NaN km'.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(api): cover + harden cmd/notification-worker
Add table-driven -race tests for vehiclesForRule, setupLogger, workerTracer, the health endpoint, runComputedMetricTick, and dispatchComputedMetricNotification. Extract testable ports so the tick loop and health probe no longer require a live DB.
Fix real bugs surfaced by the tests: /healthz interpolated err.Error() straight into a JSON literal (malformed JSON on quotes/backslashes) and set Content-Type only on the healthy path; vehiclesForRule could nil-deref on a nil rule or nil vehicle entry.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(api): cover + harden cmd/automation-worker
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/types/admin.ts
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/lib/errorMessage.ts
Fix blank-error-UI bug: an Error with an empty/whitespace message (or an
empty string) now falls back to the default instead of returning '',
which rendered a dangling 'Failed to load: ' in every AlertBanner.
Also normalise object-shaped rejections ({ message } / { error }) and add
an optional localisable fallback param so callers can pass a translated
default. Adds a co-located Vitest suite (24 cases) covering every branch.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/lib/routeRegistry.ts
Regenerate the stale route registry from App.tsx (was missing 38 routes and carried a hand-edited label on /notifications/audit) and add a comprehensive Vitest contract suite: structural invariants, generator-derived label/i18nKey/hidden rules, alias consistency, and an App.tsx drift guard.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/lib/automationSSE.ts
Fix two real bugs in the automation SSE singleton surfaced by tests:
- unsubscribe() leaked the connecting latch when teardown happened
mid-connect, wedging all future reconnects (subscribe was a silent
no-op because !source && !connecting was false).
- unsubscribe() during the backoff window left a pending reconnect
timer scheduled (guard required a non-null source), opening a
zombie EventSource with no subscribers.
Also cancel a superseded backoff timer inside doConnect() to avoid
churning a healthy reconnect, and reset failCount on teardown so a
fresh subscribe restarts the backoff at 1s.
Adds a comprehensive Vitest suite (17 cases) with a controllable
EventSource double covering connect lifecycle, all five typed events,
malformed-JSON/throwing-listener isolation, heartbeat, capped
exponential backoff, teardown, and regression guards for both bugs.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/api/charging.ts
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/ai/spaWiring.ts
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/lib/touchTargetAllowlist.ts
Add a runtime TOUCH_TARGET_ELEMENTS tuple + TouchTargetElement type so the
compile-time union and runtime validation can't drift. Codify the file's
documented contract as tested helpers: isTouchTargetWaived (canonical waiver
matcher mirroring the audit:touch-target script, with an empty-file guard so a
blank waiver can't silently exempt the whole tree), validateTouchTargetWaiver
(makes the non-empty reason/valid element/non-empty file invariants
executable), and findInvalidWaivers (CI guard over the shipped allowlist).
Add a comprehensive co-located Vitest suite: 25 cases / 74 assertions covering
every export, matcher branch, normalization, and validation path.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(api): cover + harden cmd/metric-coverage-audit
Elevate the RED metric-coverage audit tool to production-grade.
Testability refactor (behavior-preserving):
- Split the monolithic main() into main() -> run(argv, stdout, stderr) int
with injected writers and explicit exit codes (0 ok / 1 report-IO / 2 gaps
or flag error), mirroring cmd/unit-drift-validator.
- Extract pure, IO-free checkMetrics() and checkRouter(); audit() owns the
file reads. Add additive -middleware/-router flags (defaults unchanged) so
the audit can run against fixtures, matching cmd/fleet-config-validator.
- Wrap report IO errors with %w context via writeReport().
Bug fix:
- On a router read error, skip the position/route-count heuristics instead of
running them against an empty body, which previously emitted a misleading
"zero routes detected / regex broken" cascade for what is really an IO
failure. Symmetric with the middleware read-error path. Exit code unchanged.
Tests (main_test.go, table-driven, -race clean, 97.6% stmt coverage):
- checkMetrics: all-present / each-missing / all-missing.
- checkRouter: middleware before/after routes, unregistered, zero routes,
below-threshold; plus a route-counting matrix across every chi verb, the
router. alias, whitespace, and lines that must not count.
- renderReport, parseArgs (defaults/overrides/bad-flag/help), audit, and
run() end-to-end (happy, gaps, unreadable inputs, write failure, flags).
Note: the tool's default targets are stale after a file->package refactor.
The three red_http_* series now live in the internal/api/middleware package
(observability.go) and the middleware is wired as apimw.Metrics in router.go,
so the default run reports false gaps. Re-pointing the audit targets is a
domain decision (risk of false negatives in a safety gate) left to the RED
metrics owners; the paths are now flag-configurable to support that.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/__tests__/lazyRoutes.list.ts
Fix list<->App.tsx drift: 13 lazy routes (admin DLQ/FeatureFlags/IngestXRay/
LiveSignalInspector/SchemaDrift/SlowQueries/VehicleCost/DiskForecast/
SecretRotation/AuditLog/GDPRExport, settings Helix, explore Explore) existed in
App.tsx but were never mirrored here, leaving the parity smoke test red and 13
chunks un-exercised. Sync the manifest to 141 entries and add a comprehensive
invariant test (shape, name/specifier uniqueness, on-disk resolution, App.tsx
parity drift-guard, real load() resolution).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/lib/queryBroadcast.ts
Add a comprehensive Vitest suite for the cross-tab query-invalidation adapter (local invalidate, coalescing, de-dup, 50ms auto-flush window, non-serializable key skip, batch isolation) with the bus mocked so nothing hits the network.
Harden the source: __flushQueryBroadcastForTests now flushes queued keys even when a timer was never armed (previously stranded them), and add __resetQueryBroadcastForTests to isolate module state between tests, mirroring __resetBroadcastForTests in broadcast.ts.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/types/fsm/automation.ts
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/App.tsx
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/lib/icons.ts
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/api/vehicles.ts
Add a comprehensive co-located Vitest suite covering every export of
api/vehicles.ts (getVehicleStatus, getVehicleState normalisation, the
GET read family, mutations, and the software-update/state-summary URL
branches), mocking the request boundary per the repo convention.
Harden getVehicleState: a 204 / JSON-null / non-object body now resolves
to { state: undefined, live: false } instead of throwing on res.state,
matching the modern mapVehicleStateResponse contract. Also replace the
any-typed response with a typed RawStateResponse shape.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(api): cover + harden cmd/audit-signal-types
Refactor the monolithic main() into testable units (run/buildFindings/sortFindings/writeReport + auditInputs) mirroring the sibling cmd/* run() pattern, with byte-identical report output. Add comprehensive table-driven tests covering normalizeSQLType, expectedGoType, compatible, the count helpers, parseSchema (temp-fixture success + missing-file error paths), buildFindings (all branches), sortFindings, writeReport, and run() end-to-end + bad-root. Fix the doc/behaviour mismatch: the tool exits 2 on fatal setup errors, not always 0.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/api/auth.ts
Add comprehensive co-located Vitest suite for the auth API module covering every export (getAuthStatus, getAuthURL, disconnectAuth): exact path + HTTP method, response passthrough, ApiError surfacing, SUDO_REQUIRED rejection, and no /api/v1 double-prefix / camelCase query-param regression guards. Document the destructive, sudo-gated disconnectAuth for parity with its siblings.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/test/setup.ts
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/lib/useLocalStorageSync.ts
Add comprehensive Vitest coverage (13 cases) and harden the hook:
- Memoize the setter with useCallback so consumers get a stable identity.
- Route parse/serialize/msgType through refs so the long-lived bus + storage subscription always runs the latest closures instead of a stale variant on cross-tab refresh.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(api): cover + harden cmd/ocpp-server
Refactor main into testable seams (config/loadConfig, newServer, newMux,
healthz, run) without changing behaviour, and fix racy startup-error
handling by binding the listener up front and returning serve/shutdown
errors instead of log.Fatal inside a goroutine. healthz now sets a JSON
Content-Type.
Add table-driven tests for envOr/envDurationOr/loadConfig, httptest
coverage of healthz + mux routing, run() lifecycle (graceful shutdown,
pre-cancelled ctx, serve error, external close, drain timeout), and an
end-to-end OCPP-J WebSocket BootNotification + wrong-subprotocol test.
All business logic at 100% coverage; passes under -race.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/lib/settingsImportSchema.ts
- Add comprehensive Vitest suite covering every export: both public
constants, validateSettingsBundle (all rejection branches + happy
paths), defaultExportFilename (UTC formatting + invalid-Date guard),
and summariseImportResult (aggregation + wire-shape null safety).
- Harden validateSettingsBundle: use Number.isInteger so a fractional
schema_version returns the documented 'positive integer' message
instead of the confusing 'newer than this build supports' branch.
- Harden defaultExportFilename: guard against an invalid Date that would
otherwise emit a literal NaNNaNNaN filename.
- Harden summariseImportResult: null-safe over a partial/absent API
response so missing counts contribute 0 (never NaN) and an absent
sections map summarises to zeroes instead of throwing.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/api/settings.ts
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/types/dashboard.ts
Add a co-located Vitest contract suite for the DashboardStats view-model
and document its SI-canonical wire contract.
- test: 8 cases across wire contract (exact 7 camelCase keys vs the Go DTO,
all-number finite values), Phase-48 SI field-name guard (no legacy
Mi/Min/Mph/Kwh/Kw/Psi suffix, no imperial totalMi/totalKm alias),
avgEfficiency = totalEnergyWh/totalM (Wh/m) semantics + zero-distance
NaN-guard branch, JSON round-trip, and zeroed empty-install payload.
Runtime expect assertions plus compile-time expectTypeOf identities.
- source: JSDoc documenting each field's SI unit (metres, watt-hours,
Wh/m, integer cents) and the FE<->BE contract; no API/behaviour change.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/types/fsm/ui-types.ts
Add a shared FSMSelectOption interface, explicitly type both option tables (HOURS_OPTIONS was previously untyped), and make the debugger filter options i18n-ready via an i18nKey field. Wire the one live consumer (StateMachineDebuggerPage) to translate FSM-type labels at the render boundary and add matching fsm.typeOption.* / fsm.rangeOption.* keys to en.json. New co-located Vitest suite covers every export: option-table order/uniqueness/non-empty invariants, closed FSMType union membership, i18nKey->en.json fallback parity, and construction of each transport interface including optional/nullable branches.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/test-setup.ts
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(api): cover + harden cmd/backup-verify
Refactor main to the repo's testable cmd idiom: main -> run (real
config/DB/processor wiring) -> runWithDeps (dependency-injected core via a
narrow verifier interface). emit now writes to an io.Writer instead of
os.Stdout. Behaviour and exit codes are unchanged; config/DB errors return
exit 1 (was log.Fatal) so deferred cleanup runs.
Add table-driven tests covering parseCriticals, parseDuration, emit, and
runWithDeps (success, verifier error, not-ok, nil-result paths). Tested
core is fully covered and passes under -race.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/types/charging.ts
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/types/annotations.ts
Add comprehensive co-located Vitest coverage for every export of annotations.ts (toDataAnnotation projection, ANNOTATION_SCOPES, ANNOTATION_COLORS, ANNOTATION_CATEGORY_LABELS) and fix a null-safety crash in toDataAnnotation: a nil Go slice (models.ChartAnnotation.Scope) marshals to JSON null, so row.scope[0] threw a TypeError at the wire boundary. Read scope defensively (row.scope?.[0] ?? '').
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/api/client.ts
Add comprehensive co-located Vitest coverage for every export of the
api/client foundation module (apiUrl, sentinel codes, SudoCanceledError,
provider register/unregister, token cache, and request() across success,
header, error, resilient fall-through, cancellation, and the full
SUDO_REQUIRED interceptor). 32 cases / 136 assertions.
Harden request(): short-circuit caller-initiated cancellation so an
AbortError propagates unchanged and never opens the reauth dialog nor
re-enters resilientFetch's retry loop, matching the cancellation
contract documented on ApiRequestOptions.signal.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(api): cover + harden cmd/trace-coverage-audit
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/types/energy.ts
Add a comprehensive contract test for the battery/energy domain types (all 30 exported interfaces), asserting runtime shape + domain relationships and compile-time type identities (nullable unions, the stress_level literal union, optional keys). Document the mixed SI/legacy-unit wire contract in the file header.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/types/fsm/notification.ts
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(api): cover + harden cmd/pub-test-signal
Add table-driven tests (87.3% coverage) covering every helper: publishField, runSynthetic, runCSVReplay, encodeRowValue, encodeEnumValue, enum canonicalisers, pairLatLonRows, topReasons, indexHeaders, parseCSVTimestamp/parseTimeFilter and the bounds-safe fieldAt accessor. Tests use an in-memory publisher/token fake and temp CSVs — no broker, DB, or sleeps; race-clean.
Harden the package for testability and correctness:
- Introduce a narrow publisher port (interface segregation) so helpers are unit-testable; the real pahomqtt.Client satisfies it.
- Replace log.Fatalf-in-helpers with wrapped error returns (indexHeaders, parseTimeFilter, runCSVReplay, runSynthetic); main handles fatal exit at the boundary.
- Fix publishField publish-result handling: distinguish timeout (paho leaves Error() nil, so %w wrapped a nil into a confusing message) from delivery error, and guard a nil token.
- Add bounds-safe fieldAt for CSV column reads so a truncated row is skipped instead of panicking (csv.Reader FieldsPerRecord=-1 tolerates short rows).
- Handle the pairLatLonRows sentinel before the SignalsByName lookup so diagnostics no longer surface a phantom empty-string unknown signal.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/types/vehicle.ts
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/ai/features.ts
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/types/signals.ts
narrowSignal lacked a default branch, so a malformed catalog value_type
(the catalog is sourced from an unvalidated API response) returned
undefined instead of null — breaking the declared \| null\ contract and
the JSDoc. Add the default and pin it with a co-located Vitest suite that
covers every export: the numeric/text/bool discriminant paths, falsy-but-
valid readings (0, '', false), null columns, catalog-driven narrowing,
input immutability, and the TypedSignalObservation union.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/api/polling.ts
Fix predictions response type: GET /polling/predictions?vin=X returns { vin, prediction } (singular), not { predictions } — model both shapes as a faithful union. Make decisions vin optional (disabled-engine branch omits it) and normalise the limit param to a positive integer mirroring the backend clamp. Add a comprehensive co-located Vitest suite (21 cases) covering every export, URL/encoding/param contracts, edge cases, and error paths.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/types/fsm/drive-session.ts
Add a comprehensive co-located Vitest suite (36 cases / 161 assertions) covering every export of the Drive-Session FSM: states, state entries, triggers, guards, DriveSignalContext, transitions, edges, disallowed pairs, validation rules, coverage matrix, scenarios, and FSM assembly + registry wiring.
Fix a real modeling bug the tests surface: the frontend FSM mirrored the backend end_snapshot_timeout (ending->completed) but omitted the symmetric start-side fallback. Add the start_snapshot_timeout trigger and its unguarded pending->active row to match backend drive.TriggerStartTimeout (internal/fsm/drive/machine.go, 30s StartSnapshotTimeout). Edge count stays 7 (dedup); all 179 FSM tests pass.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/types/fsm/telemetry-connection.ts
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/types/fsm/command.ts
Add a comprehensive co-located test for the command FSM covering every runtime export (states, entries, triggers, guards, transitions, edges, disallowed, coverage, toasts, scenarios, FSM wiring) plus machine invariants (determinism, reachability, terminal/initial states, isValidTransition semantics).
Fix: sync COMMAND_COVERAGE with COMMAND_DISALLOWED. The six explicitly-forbidden transitions were marked null instead of 'disallowed', so isValidTransition() could not distinguish a forbidden pair from a merely-absent one. Now matches the vehicle FSM convention.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/types/driving.ts
Add a runtime data-contract harness (web/src/types/driving.test.ts) that pins every driving.ts interface through the real camelCaseKeys producer and the request() client, using fixtures built from the backend Go JSON tags. Covers SI field names, nullability, union literals, dual snake+camel keys, and arrays.
Fix a real contract bug: SpeedBucket declared legacy avg_power_kw/avgPowerKw but the speedprofile backend emits SI avg_power_w, so SpeedProfileWidget's efficiency line was always flat-zero. Correct the type to avg_power_w/avgPowerW (SI canonical, Phase-48), drop the dead legacy range/percentage/driveCount fields, and update the widget consumer.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/types/admin-diagnostics.ts
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/api/devtools.ts
Add a comprehensive co-located Vitest suite covering every export in
devtools.ts (telemetry capture, Redis L2 signal cache, API call logs,
system health, data-repair, backup/restore, API keys, audit, export/import
jobs, chatbot) — pinning each wire contract: un-prefixed path, snake_case
query params, HTTP verb, and serialised body.
Fix two real bugs surfaced by the tests:
- submitImportJob routed a multipart FormData body through request(), whose
buildHeaders forces Content-Type: application/json and strips the
boundary, breaking server-side form parsing. Now bypasses request() with a
raw fetch (mirroring the canonical useUploadVehiclePhoto pattern) so the
browser owns Content-Type, and surfaces failures as a typed ApiError.
- getChatHistory did not URL-encode the session_id query param (unlike
rename/delete). Now uses encodeURIComponent to guard malformed queries.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/api/types.ts
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/types/location.ts
Add co-located contract test for the Location & Geofence view-models: runtime shape / null-safety / consumer-projection assertions plus expectTypeOf identity locks (8 cases, 70 assertions). Harden the source with concise SI-unit (totalDurationS seconds) and nullability JSDoc without changing any field type.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/types/trip.ts
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/types/sharing.ts
Extract the shared-drive wire discriminator + legacy-v1 to SI normaliser out of SharedDrivePage into types/sharing.ts as null-safe, reusable helpers (isCanonicalSharedDrive, isLegacySharedDrive, normalizeSharedDriveData), and add a comprehensive co-located Vitest suite covering v2/v1-tagged passthrough, legacy km/min/kmh to SI conversion, null-scalar preservation, and empty-array profile normalisation.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/types/fsm/registry.ts
Add comprehensive Vitest coverage for the FSM registry barrel (FSM_REGISTRY, FSM_STATES, FSM_EDGES, STATE_COLORS, getStateColor, getStateDefinition) and harden the two lookup helpers: guard against nullish/non-string state so getStateColor/getStateDefinition return the neutral default instead of throwing on state.toLowerCase(), and DRY the StateStyle projection via a shared helper. Behaviour is unchanged for valid inputs.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/types/analytics.ts
Add a co-located type-contract test for the analytics API interfaces and
make AnalyticsSummary.co2SavedKg optional to match the wire reality.
- New web/src/types/analytics.test.ts pins all 12 exported interfaces with
assert*Shape guards (compile-time + runtime) and validates the REAL
camelCaseKeys transform over backend-accurate fixtures — a path the
request-mocking hook tests never exercise. Covers nullable mileage fields
and safeArray null-safety on the envelope selects.
- Fix: /analytics/fleet (internal/api/analytics/queries.go) never emits
co2_saved_kg, so co2SavedKg resolved undefined while the type promised a
number. Typed it optional; QuickStatsPage already reads it as \?? 0\.
- Documented the VehicleComparisonEntry.id number-vs-string wire divergence.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/types/export.ts
Elevate the export-job wire type into a tested runtime backbone:
- EXPORT_JOB_FSM_STATES single source of truth mirroring
internal/domain/export/fsm.go, ExportJobFsmState union + guard
- exportJobStatus classifier + active/complete/failed predicates
(four-bucket UI status), isExportJob fetch-boundary shape guard
- wire ExportStatusWidget to exportJobStatus, fixing a real bug where
the uploading/validating FSM states bucketed to 'queued'
- comprehensive two-level contract test (27 cases, 121 assertions)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/types/admin-operator-confidence.ts
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/types/automations.ts
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/types/fsm/theme.ts
Add a comprehensive co-located Vitest suite for theme.ts covering VARIANT_THEME, resolveStyle, and DEFAULT_STATE (variants, overrides, empty overrides, immutability, and unknown-variant fallback).
Harden resolveStyle to fall back to the neutral theme when a variant is not in VARIANT_THEME, so runtime data with an unexpected variant no longer yields a ResolvedStateStyle with undefined class keys (unstyled badge). Behavior is unchanged for all valid variants and existing callers.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/types/fsm/alert-cooldown.ts
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/types/telemetry.ts
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/types/fsm/charge-session.ts
Add a comprehensive co-located Vitest suite covering every runtime export of the charge-session FSM (states, entries/theming, triggers, guards, transitions, edges, disallowed<->coverage consistency, coverage matrix, scenarios, FSM assembly + registry integration, and ChargeSignalContext shape).
Fix a latent coverage-matrix bug: the four CHARGE_SESSION_DISALLOWED pairs were marked null instead of 'disallowed', so isValidTransition() reported them as ambiguous 'no info' rather than forbidden-with-reason. Align them to 'disallowed' to match the vehicle FSM contract.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/types/user.ts
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/types/fsm/cross-fsm.ts
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/types/fsm/types.ts
Add comprehensive co-located Vitest suite (27 cases) covering deriveEdges and isValidTransition plus every exported FSM type contract. Harden deriveEdges with a null-safe input guard and a collision-safe JSON composite dedup key, and make isValidTransition null-safe when the coverage matrix is missing.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/types/fsm/vehicle.ts
Add direct whole-module coverage for the vehicle FSM (states, entries, labels, triggers, guards, transitions, disallowed, coverage matrix, truth table, scenarios, edges, FSM wiring, signal context, deriveVehicleStatus) with trigger-level truth-table/transition consistency checks stronger than the registry sweep.
Fix deriveVehicleStatus JSDoc: it claimed an 'offline fallback' but returns 'online' when a state object exists without a stronger signal (matches the tested twin in api/types.ts). Comment corrected; behaviour unchanged.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/types/notifications.ts
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): cover + harden src/types/vehicle-systems.ts
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix(api,db): resolve whole-suite go validation failures from Apex elevation
The per-unit gate only ran 'go test -race' on each unit's own package, so
cross-package regressions surfaced only in the aggregate 'go test -race ./...'
validation. Fixes:
- api/{energy,teslachargehist,teslachargesess,teslaenergylivestatus,
teslauserconfig,teslauserorder,teslauserprofile,vehicleaccess}: handler
construction tests passed nil pools into eager, nil-hostile constructors
(db.Pool nil-panic / 'db must not be nil'). Pass construction-safe
&database.DB{} / &tesla.Client{} instead.
- adapter/postgres: tripRepository used a concrete *pgxpool.Pool field while
every sibling repo uses the pgxPool interface seam; align it so the fake
pool injects, and wrap CollectRows errors with context ('collecting trips
for vehicle') to match the getters.
- api/apiauthctx: add the arch-required doc.go // Layer: handler (package was
extracted by an elevation unit that fixed a real 403 bug but omitted doc.go).
- web/src/ai: regenerate stale aigen mirrors (pre-existing drift) via
'go run ./tools/aigen'.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test(web): fix whole-suite vitest regressions from Apex elevation
30 elevation-added FE test files passed in isolation (the per-unit gate ran
only that file) but failed in the full 'vitest run' because later units
hardened shared components/hooks/formatting, leaving stale expectations.
Fixed all 30 to assert current behaviour; corrected real source bugs found
along the way:
- cost-analysis useCostAnalysisData: fix double distance conversion (was
metres->miles before a display fn that converts again) — pass SI metres
directly; add null-safety + finite guards.
- useVehiclePhoto, BreadcrumbOverridesContext, AutomationBuilderPage,
ExportStatusWidget, TirePressureVisualWidget, TripReplayPage, InfoTile,
TelemetryGrid: null-safety / merge / unit-display hardening surfaced by tests.
Full suite: 1574 test files / 23021 tests pass; tsc --noEmit clean.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>1 parent 576d863 commit ec7e85c
0 file changed
0 commit comments