fix(diagnostics): D2 — redact through homeassistant.helpers.redact (t37)

`homeassistant.util.diagnostic` does not exist — verified against a real
install: `import homeassistant.util.diagnostic` raises ModuleNotFoundError, and
the helper has always lived in `homeassistant.helpers.redact`. The import sat
inside a `try/except ImportError` that set the helper to None, so the
documented G14 pass — the one carrying `_TO_REDACT`, hosts included — silently
did nothing; only the marker scan (pairing-/key-shaped names) masked anything,
which is why the existing test's pairing-code assertion stayed green.

The helper is now imported at MODULE scope from homeassistant.helpers.redact and
applied unconditionally after the marker-name pass. A missing module is a loud
import error at integration load instead of a silent downgrade.

* the diagnostics test now asserts `adb_host` and `protocol_host` really are
  masked in the emitted payload (options and a nested data value);
* TestFaithfulHaStubs asserts the module USES the helper from that path
  (`nsh.diagnostics.async_redact_data is homeassistant.helpers.redact.async_redact_data`),
  that no production module imports the non-existent module (AST), and that the
  try/except ImportError fallback is gone;
* `entity_stublib` gains a faithful `homeassistant.helpers.redact` stub (same
  masking, same nested mapping/list recursion, same None/empty-string skip) so
  the harness meets the import the way a real install does.

Verified: 287 passed, 2 skipped (D2 stage); ruff clean. Full gate re-run on the
completed branch: 289 passed / 2 skipped and 290 passed / 1 skipped.
This commit is contained in:
James Mackay committed 2026-10-10 21:20:04 +01:00
1 parent 37b2accfda
commit bd72ae3170
3 files changed
+98 -10

No files matched your search

@@ -16,6 +16,7 @@ from dataclasses import asdict, is_dataclass
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.redact import async_redact_data
from .connection import ShieldConnectionManager
@@ -87,15 +88,13 @@ async def async_get_config_entry_diagnostics(
"is_on": manager.is_on,
},
}
# G14 (t30): the documented helper is the primary redaction path with
# an explicit field list; the marker scan above runs defensively after.
try:
from homeassistant.util.diagnostic import async_redact_data
except ImportError:
async_redact_data = None
if async_redact_data is not None:
payload = async_redact_data(payload, _TO_REDACT)
return _redact(payload)
# G14 (t30) + t37 D2: the documented helper is the primary redaction path
# with an explicit field list (the hosts included), and the marker scan
# above runs defensively after it. It is imported at MODULE scope from
# homeassistant.helpers.redact — the real module; the old
# `homeassistant.util.diagnostic` never existed, and the swallowed
# ImportError turned the whole pass into a silent no-op.
return _redact(async_redact_data(payload, _TO_REDACT))
__all__ = ["async_get_config_entry_diagnostics"]