| name | verify |
|---|---|
| description | Stand up the real mock-HA + backend E2E stack locally and drive it to observe a change working, instead of just running tests. |
Use docker-compose.ci.yml (works with podman-compose) to run the real
backend against a scripted mock Home Assistant instance, then hit the actual
HTTP API (or load the served frontend) to observe the change.
- In a fresh worktree, run
./scripts/worktree-setup.shfirst — it shares.venvand bothnode_modulestrees with the main checkout and repairs a broken Playwright browser cache, instead of reinstalling all of it (~35 min). podman-composeisn't always on PATH:pip install --user podman-compose, then add~/Library/Python/<ver>/binto PATH for the shell session.podman compose(the built-in plugin) does NOT work here — it looks for adocker-compose/podman-composebinary and fails without one.- Backend container serves the frontend from a bind-mounted
frontend/dist(./frontend/dist:/app/frontend:ro). Build it first or the container crashes onStaticFilesinit:cd frontend && npm run build.
Use a unique project name (-p) and non-default ports so it doesn't collide
with another worktree's running stack:
SCENARIO=ci-normal-day BESS_PORT=18180 MOCK_HA_PORT=18123 \
podman-compose -p <unique-name> -f docker-compose.ci.yml up -dAdd --build only when you changed something baked into the image
(backend/Dockerfile.dev, requirements*.txt, or anything under
scripts/mock_ha/ other than scenarios/) — the compose file bind-mounts the
backend source, so an unnecessary rebuild costs minutes and changes nothing.
Note that mock-HA is the opposite case: its Dockerfile does COPY . . and only
scenarios/ is bind-mounted, so an edit to server.py without --build runs
the stale baked copy and silently invalidates the whole observation.
Wait for both containers healthy (podman ps --filter name=<unique-name>),
then hit the real API:
curl -s http://localhost:18180/api/system-health
curl -s http://localhost:18180/api/dashboard-health-summaryscripts/mock_ha/server.py exposes POST /mock/update_sensor/{entity_id} to
mutate a sensor's state on the running mock-HA at any time (no restart
needed) — this is the way to observe a transition (e.g. a sensor going
unavailable then recovering) through the real system, since a fresh
container/process has no "previous" state to transition from:
curl -s -X POST http://localhost:18123/mock/update_sensor/number.growatt_battery_charging_power_rate \
-H "Content-Type: application/json" -d '{"state": "unavailable", "attributes": {...}}'
curl -s -X POST http://localhost:18180/api/system-health/recheck # observe the break
curl -s -X POST http://localhost:18123/mock/update_sensor/number.growatt_battery_charging_power_rate \
-H "Content-Type: application/json" -d '{"state": "100", "attributes": {...}}'
curl -s -X POST http://localhost:18180/api/system-health/recheck # observe the recoveryGet the full current sensor snapshot with GET /mock/sensors on the mock-HA
port to find real entity IDs/attributes to restore.
${BESS_SETTINGS:-./e2e/ci-bess-settings.json}is mounted read-write (no:ro), and it has to be — that mount is how the app persists settings, so:rowould break the wizard rather than protect the file. Running the app against it therefore writes settings back into the fixture (schema migrations, demo_mode defaults, etc). After tearing down:git diff -- e2e/andgit checkout -- e2e/if the only changes are ones you didn't intend.e2e/ci-wizard-settings.jsonis the exception and needs no such reset: it is gitignored, because every consumer truncates it to{}before mounting it and nothing reads its content. Theci-bess-settings*.jsonfiles are real fixtures whose content matters, so those still dirty the tree.- Many scenarios (e.g.
ci-wizard-entsoe.json) pinmock_timeto a fixed past date (ci-normal-day.json→2025-01-15), not "today" — the real container clock is today's date, so date-anchored service calls (Nordpoolget_prices_for_date) mismatch and the DP scheduler logs "No prices for ".backend/Dockerfile.devinstallslibfaketimefor this; recreate thebesscontainer manually (not via compose) with-e LD_PRELOAD=/usr/lib/aarch64-linux-gnu/faketime/libfaketime.so.1 -e FAKETIME="2025-01-15 12:00:00"and the same network/volumes/env, confirm withpodman exec <container> date. Note this only helps a fresh process — it won't recover in-memory state accumulated before the restart (e.g. a pending health-check transition), since restarting always wipes process memory. - Verifying a bundled frontend UI string without a browser: fetch the served
JS bundle and grep for the new text —
curl -s http://localhost:<port>/ | grep -o '/assets/main-[^"]*\.js'thencurl -s http://localhost:<port><that path> | grep -o '<new UI string>'confirms the exact deployed artifact contains the change, even without a headless browser available. - Tear down:
podman-compose -p <unique-name> -f docker-compose.ci.yml down.
Only exposed via WebSocket (config/entity_registry/list), not REST — to
verify registry-based sensor discovery, go through the real backend endpoint
that calls it (e.g. POST /api/setup/discover) rather than curling mock-HA
directly.