01 — Core — Subsystem Test Map¶
The core package is the orchestrator: EufyVacuumManager ties every subsystem
together and owns the live read surfaces the dashboard polls (lifecycle, job
progress, start-status), plus the storage layer, capability cache, error-tracker
latch, the post-job water amendment, and the brand-agnostic charging /
low-battery-return reads. Covered by 241 tests across 15 files.
Source: custom_components/eufy_vacuum/core/
Architecture reference: docs/dev/05-core-manager.md, docs/dev/23-error-tracker.md
Coverage map¶
| Source module | Stmts | Cov | Test files | Layer |
|---|---|---|---|---|
manager.py |
1463 | 96% | test_manager_lifecycle_status.py, test_manager_progress.py, test_manager_delegation.py, test_manager_start_selected.py, test_manager_external_finalize.py, test_manager_init_migrations.py, test_core_manager_registry.py, test_manager_compare_sources.py, test_manager_live_pose.py, test_core_manager_helpers.py (unit) |
int + unit |
error_tracker.py |
327 | 89% | test_core_error_tracker.py |
integration |
capabilities.py |
118 | 97% | test_core_capabilities.py |
integration |
charging.py |
39 | 100% | test_charging.py (unit) |
unit |
water_amendment.py |
120 | 92% | test_core_water_amendment.py |
integration |
storage.py |
17 | 100% | test_core_storage.py |
integration |
manager.py is the single largest module in the codebase; most of its public
surface delegates to a subsystem (see the delegation seam),
so its own tests target the logic that genuinely lives in the orchestrator.
What's tested¶
- Lifecycle + error overlay (
LS) —get_lifecycle_statefolds the ErrorTracker active-run latch into the user-visible message: a livecurrent_messagewins; a blank current message with error history derives a "Run had N error(s); last: …" summary. - Start-status gates (
LS) —get_start_statusblocks a new run on a paused job (job_paused), incomplete floor-type onboarding (onboarding_required), and every-selected-room-blocked (all_selected_rooms_blocked, reached by building a complete access graph so rule-bearing rooms clear the graph gate). - Live job progress (
PR) —get_job_progress_snapshot: idle terminal snapshot, started timeline (estimate / reanchored), completed-room reanchor, and run-anomaly detection. The anomaly logic + event emission live inActiveJobTracker.detect_run_anomalies(jobs/active_job.py); the snapshot composer calls it (self.active_job.detect_run_anomalies(...)) and folds the returned fields in. Three anomalies are covered: stall (bounds gate blocking and the room stuck ≥ 2× its estimate — firesEVENT_STALL_DETECTEDonce per room per job), running_long (the soft band 1.5×–2× below stall, no event), and skipped (a queued room advanced past but not completed — firesEVENT_ROOM_SKIPPEDonce per room per job).test_manager_progress.pyexercises all three through the manager. - Finalize bridge (
PR) —finalize_learning_for_active_job: no-learning → None, missingstarted_at→ not finalized, full job →completed_job. - Room-history ingest —
_ingest_jobs_index_entry_into_room_history(newer-wins merge, bad-row skips). - Pure helpers (
CMH, unit) —_safe_float,_safe_int,_normalize_path_block_action,_hours_text,_display_label,_settings_profile_display. - Delegation seams (
MD) — every thin forwarder (return self.<sub>.x(...)) is smoke-tested through the manager so a delegation lost in a refactor fails loudly. This is the #11 / #13 bug-class net (a forwarder that went missing while a listener still called it). - ErrorTracker (
ET) — latch lifecycle, secondary-error detection. - Capabilities / storage / water amendment — capability resolution + cache, the persistent store round-trip, and the post-job water patch.
How it's tested¶
Driven against the real manager fixture (tests/integration/conftest.py). The
error overlay uses a recording _FakeErrorTracker registered at
hass.data[DOMAIN][DATA_ERROR_TRACKER]. Progress tests wire a real
LearningManager and seed an active job with _seed_job(...). The delegation
smoke test calls each seam with minimal valid args and asserts it forwards
without AttributeError.
Known gaps¶
manager.py (97%) — the external-run capture/review orchestration
(maybe_handle_external_run / _external_grace_finalize / confirm /
resegment / get_external_pending_runs / discard), the room-history ingest
helpers, the registry-model backfill, the init migrations, the job/dock
delegations, and the progress snapshot are now all covered (the EXT-, CMR-,
init-migration, delegation, and progress suites). What's left (~2%, ~16 lines) is
the defensive tail: # pragma: no cover best-effort log-only excepts
(_async_save_logged, _refresh_room_derived_state, a defensive read inside
get_lifecycle_state), a few malformed-input guards, the
recorder-dependent return-overhead extraction (skipped when no recorder is
configured under test), and a handful of not-taken partial branches. The
_notify_* fan-out excepts (MD-7) and the snapshot-degrade except (SS-7) are
covered because they escape into behavior.
The remaining per-module gaps are within the held ceiling: error_tracker.py
(89%) and water_amendment.py (92%) are short of the others only on defensive
except/guard paths; capabilities.py (97%), charging.py (100%) and
storage.py (100%) are effectively complete.