Files
localthings/tests
Marc Billow 1e9fbd4ec5 Cover the identity migration against what existing users can lose
The move onto the OCF device UUID rewrites the identity of registry rows
that a user's automations, dashboards, history and areas all hang off, and
it does so on the first live poll rather than inside async_migrate_entry.
That makes it the riskiest migration in the codebase and the one least
covered by its own tests, so it gets a dedicated suite organised around
what must not break rather than around the functions involved.

tests/localthings/test_identity_migration.py (19 tests) covers: the v3
upgrade end to end; the full v1 -> v4 walk an oldest install takes; a
host-keyed placeholder-serial board adopting a real identity; issue #381's
actual two-unit install migrating to separate identities; every
customization carried on an entity row (rename, icon, area, hidden_by) and
the device's own area; a composite appliance's subdevice identifiers and
via_device links; scoping to one config entry's rows; the key-boundary
match; upgrading while the appliance is off, and the deferred adoption
completing when it returns; restarting after the upgrade being a no-op;
and the three guards that defend an identity once it has moved.

tests/test_rekey_statistics_end_to_end.py drives a real in-memory recorder
to prove the claim the in-place rewrite exists to make: statistics are
filed under entity_id, so preserving the row preserves the history --
including on the one path that deletes a row rather than rewriting it.
It lives outside tests/localthings/ for the same reason the existing
statistics end-to-end test does (that package's autouse fixture starts HA
before recorder_mock can claim its database).

Every guarantee was checked by mutation: ten separate breakages of the
production code -- dropping the unique_id update, the subdevice prefix,
the entry scoping, the key boundary, the entity rewrite, both snapshot
guards, the no-demote rule, the rejected-serial rule, and recreating the
row under a new entity_id -- each fail the suite.

Two gaps this found and closed:

- ConfigFlow.VERSION had to move to 4. Home Assistant only calls
  async_migrate_entry for entries behind the flow's version, so the v3 ->
  v4 step silently never ran. Pinned with the reasoning written down.
- An offline load could re-key the registry from a stale snapshot, and
  freeze that answer into CONF_DEVICE_KEY so the real UUID could never be
  adopted afterwards. The guards existed; nothing proved they held.

The identity-migration tests move out of test_migration.py, which stays
about the migrations that finish inside async_migrate_entry.
2026-08-17 05:24:22 +00:00
..