Files

155 lines
5.1 KiB
Python

"""Tests for the shelly_dimmer firmware download and prefetch extraction."""
from __future__ import annotations
import hashlib
from pathlib import Path
from unittest.mock import patch
import pytest
from esphome import external_files
from esphome.components.shelly_dimmer import light as shd
from esphome.config_validation import Invalid
from esphome.external_files import RemoteFile
def _sha(data: bytes) -> str:
return hashlib.sha256(data).hexdigest()
def test_prefetch_known_version(setup_core: Path) -> None:
entries = [{"firmware": {"version": "51.6", "update": True}}]
stages = list(shd.PREFETCH_FILES(entries))
url, sha = shd.KNOWN_FIRMWARE["51.6"]
assert stages == [[RemoteFile(url, shd._firmware_cache_path(sha))]]
def test_prefetch_normalizes_update_like_the_schema(setup_core: Path) -> None:
"""Quoted booleans behave as the schema will normalize them."""
url, sha = shd.KNOWN_FIRMWARE["51.6"]
off = [{"firmware": {"version": "51.6", "update": "false"}}]
assert list(shd.PREFETCH_FILES(off)) == [[]]
on = [{"firmware": {"version": "51.6", "update": "true"}}]
assert list(shd.PREFETCH_FILES(on)) == [
[RemoteFile(url, shd._firmware_cache_path(sha))]
]
def test_prefetch_rejects_malformed_sha256(setup_core: Path) -> None:
"""A raw sha256 that is not a hash never becomes a path component."""
entries = [
{
"firmware": {
"url": "https://example.com/fw.bin",
"sha256": "/tmp/payload",
"update": True,
}
}
]
assert list(shd.PREFETCH_FILES(entries)) == [[]]
def test_prefetch_skips_content_addressed_blob_on_disk(setup_core: Path) -> None:
"""A sha-keyed cache file needs no revalidation; get_firmware hashes it."""
url, sha = shd.KNOWN_FIRMWARE["51.6"]
shd._firmware_cache_path(sha).write_bytes(b"pinned firmware")
entries = [{"firmware": {"version": "51.6", "update": True}}]
assert list(shd.PREFETCH_FILES(entries)) == [[]]
def test_prefetch_explicit_url_without_sha(setup_core: Path) -> None:
url = "https://example.com/fw.bin"
entries = [{"firmware": {"url": url, "update": True}}]
stages = list(shd.PREFETCH_FILES(entries))
key = external_files.url_cache_key(url)
# No sha means the bytes cannot be verified, so the prefetch itself
# must carry the validator's strict no-stale policy.
assert stages == [
[RemoteFile(url, shd._firmware_cache_path(key), allow_stale=False)]
]
def test_prefetch_skips_no_update(setup_core: Path) -> None:
entries = [
{"firmware": {"version": "51.6"}},
{"firmware": "51.6"},
{"firmware": {"version": "0.0", "update": True}},
{},
]
assert list(shd.PREFETCH_FILES(entries)) == [[]]
def test_get_firmware_rejects_corrupted_cache(setup_core: Path) -> None:
"""A cached blob failing its hash check is discarded and re-downloaded."""
good = b"good firmware"
expected = _sha(good)
path = shd._firmware_cache_path(expected)
path.write_bytes(b"corrupted blob")
with patch(
"esphome.components.shelly_dimmer.light.external_files.download_content",
return_value=good,
) as mock_download:
result = shd.get_firmware(
{
"update": True,
"url": "https://example.com/fw.bin",
"sha256": expected,
}
)
mock_download.assert_called_once()
assert result == [int(b) for b in good]
def test_get_firmware_trusts_valid_cache(setup_core: Path) -> None:
"""A cached blob passing its hash check is used with zero network."""
good = b"good firmware"
expected = _sha(good)
shd._firmware_cache_path(expected).write_bytes(good)
with patch(
"esphome.components.shelly_dimmer.light.external_files.download_content"
) as mock_download:
result = shd.get_firmware(
{
"update": True,
"url": "https://example.com/fw.bin",
"sha256": expected,
}
)
mock_download.assert_not_called()
assert result == [int(b) for b in good]
def test_get_firmware_hash_mismatch_raises_and_uncaches(setup_core: Path) -> None:
"""A fresh download failing its hash check raises and is not cached."""
expected = _sha(b"expected firmware")
path = shd._firmware_cache_path(expected)
with (
patch(
"esphome.components.shelly_dimmer.light.external_files.download_content",
return_value=b"wrong firmware",
),
pytest.raises(Invalid, match="Hash mismatch"),
):
shd.get_firmware(
{"update": True, "url": "https://example.com/fw.bin", "sha256": expected}
)
assert not path.exists()
def test_get_firmware_without_sha_rejects_stale(setup_core: Path) -> None:
"""The unverifiable no-hash branch must not accept a stale copy."""
with patch(
"esphome.components.shelly_dimmer.light.external_files.download_content",
return_value=b"fw",
) as mock_download:
shd.get_firmware({"update": True, "url": "https://example.com/fw.bin"})
assert mock_download.call_args.kwargs["allow_stale"] is False