mirror of
https://github.com/home-assistant/supervisor.git
synced 2026-07-07 13:55:07 +01:00
ed91b18c4b
* tests: enable flake8-pytest-style (PT) ruff rules Enable the `PT` ruff rule set and fix the resulting violations across the test suite: - PT006: pass parametrize argument names as tuples instead of a single comma-separated string. - PT022: switch fixtures that have no teardown from `yield` to `return` so the lack of cleanup is obvious at a glance. - PT011: add `match=` to broad `pytest.raises(ValueError)` blocks so the expected error is anchored to a specific message. - PT012: hoist setup (patches, branching) out of `pytest.raises()` blocks so only the call that is expected to raise remains inside. - PT013: replace `from pytest import X` with `import pytest` and access attributes via the module. - PT015: replace `try/except` + `assert False` patterns with `pytest.raises(...)`. - PT017: replace `assert` on exceptions inside `except` blocks with `pytest.raises(...) as exc_info` and assert on `exc_info.value`. No behavioral changes to the tests; the full suite still passes. * tests: address review feedback on PT ruff rule enablement - Fix fixture return-type annotations after switching `yield` to `return` in tests/conftest.py: drop the `Generator[...]`/`AsyncGenerator[...]` wrapper for `dns_manager_service`, `supervisor_internet`, `websession`, and `mock_update_data` so the annotation matches what the fixture actually returns. - Correct the return-type annotation of `fixture_ip6config_service` from `IP4ConfigService` to `IP6ConfigService`. - Fix recurring "excepiton" typo in tests/utils/test_exception_helper.py. * tests: verify backup cleanup on permission error After `test_new_backup_permission_error` raises `BackupPermissionError`, assert that no tarfile was left behind and `tmp_path` is empty. The previous version only checked that the exception was raised, which missed any regression where a partial tarfile would survive the failed create. * tests: rename DNS_GOOD_V6 to DNS_V6_UNSUPPORTED The constant was named "good" but its tests assert that the URLs are rejected by the DNS validator. The IPv6 URLs are well-formed but currently rejected because IPv6 doesn't work with the Docker network (see `dns_url` in supervisor/validate.py). Rename the constant and the related test to make the intent obvious.
154 lines
4.6 KiB
Python
154 lines
4.6 KiB
Python
"""Test git repository."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
from unittest.mock import AsyncMock, MagicMock, patch
|
|
|
|
from git import GitCommandError, InvalidGitRepositoryError, NoSuchPathError
|
|
import pytest
|
|
|
|
from supervisor.coresys import CoreSys
|
|
from supervisor.exceptions import StoreGitCloneError, StoreGitError
|
|
from supervisor.resolution.const import ContextType, IssueType, SuggestionType
|
|
from supervisor.store.git import GitRepo
|
|
|
|
REPO_URL = "https://github.com/awesome-developer/awesome-repo"
|
|
|
|
|
|
@pytest.fixture(name="clone_from")
|
|
async def fixture_clone_from():
|
|
"""Mock git clone_from."""
|
|
with patch("git.Repo.clone_from") as clone_from:
|
|
yield clone_from
|
|
|
|
|
|
@pytest.mark.parametrize("branch", [None, "dev"])
|
|
async def test_git_clone(
|
|
coresys: CoreSys, tmp_path: Path, clone_from: AsyncMock, branch: str | None
|
|
):
|
|
"""Test git clone."""
|
|
fragment = f"#{branch}" if branch else ""
|
|
repo = GitRepo(coresys, tmp_path, f"{REPO_URL}{fragment}")
|
|
|
|
await repo.clone.__wrapped__(repo)
|
|
|
|
kwargs = {"recursive": True, "depth": 1, "shallow-submodules": True}
|
|
if branch:
|
|
kwargs["branch"] = branch
|
|
|
|
clone_from.assert_called_once_with(
|
|
REPO_URL,
|
|
str(tmp_path),
|
|
**kwargs,
|
|
)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"git_error",
|
|
[
|
|
InvalidGitRepositoryError(),
|
|
NoSuchPathError(),
|
|
GitCommandError("clone"),
|
|
UnicodeDecodeError("decode", b"", 0, 0, ""),
|
|
],
|
|
)
|
|
async def test_git_clone_error(
|
|
coresys: CoreSys, tmp_path: Path, clone_from: AsyncMock, git_error: Exception
|
|
):
|
|
"""Test git clone error."""
|
|
repo = GitRepo(coresys, tmp_path, REPO_URL)
|
|
|
|
clone_from.side_effect = git_error
|
|
with pytest.raises(StoreGitCloneError):
|
|
await repo.clone.__wrapped__(repo)
|
|
|
|
assert len(coresys.resolution.suggestions) == 0
|
|
|
|
|
|
async def test_git_load(coresys: CoreSys, tmp_path: Path):
|
|
"""Test git load."""
|
|
repo_dir = tmp_path / "repo"
|
|
repo = GitRepo(coresys, repo_dir, REPO_URL)
|
|
repo.clone = AsyncMock()
|
|
|
|
# Test with non-existing git repo root directory
|
|
await repo.load()
|
|
assert repo.clone.call_count == 1
|
|
|
|
repo.clone.reset_mock()
|
|
|
|
# Test with existing git repo root directory, but empty
|
|
repo_dir.mkdir()
|
|
await repo.load()
|
|
assert repo.clone.call_count == 1
|
|
|
|
repo.clone.reset_mock()
|
|
|
|
# Pretend we have a repo
|
|
(repo_dir / ".git").mkdir()
|
|
|
|
with patch("git.Repo") as mock_repo:
|
|
await repo.load()
|
|
assert repo.clone.call_count == 0
|
|
assert mock_repo.call_count == 1
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"git_errors",
|
|
[
|
|
InvalidGitRepositoryError(),
|
|
NoSuchPathError(),
|
|
GitCommandError("init"),
|
|
UnicodeDecodeError("decode", b"", 0, 0, ""),
|
|
GitCommandError("fsck"),
|
|
],
|
|
)
|
|
async def test_git_load_error(coresys: CoreSys, tmp_path: Path, git_errors: Exception):
|
|
"""Test git load error."""
|
|
coresys.hardware.disk.get_disk_free_space = lambda x: 5000
|
|
repo = GitRepo(coresys, tmp_path, REPO_URL)
|
|
|
|
# Pretend we have a repo
|
|
(tmp_path / ".git").mkdir()
|
|
|
|
with patch("git.Repo") as mock_repo:
|
|
mock_repo.side_effect = git_errors
|
|
with pytest.raises(StoreGitError):
|
|
await repo.load()
|
|
|
|
assert len(coresys.resolution.suggestions) == 0
|
|
|
|
|
|
@pytest.mark.usefixtures("supervisor_internet")
|
|
async def test_git_pull_missing_origin_remote(coresys: CoreSys, tmp_path: Path):
|
|
"""Test git pull with missing origin remote creates reset suggestion.
|
|
|
|
This tests the scenario where a repository exists but has no 'origin' remote,
|
|
which can happen if the remote was renamed or deleted. The pull operation
|
|
should create a CORRUPT_REPOSITORY issue with EXECUTE_RESET suggestion.
|
|
|
|
Fixes: SUPERVISOR-69Z, SUPERVISOR-172C
|
|
"""
|
|
repo = GitRepo(coresys, tmp_path, REPO_URL)
|
|
|
|
# Create a mock git repo without an origin remote
|
|
mock_repo = MagicMock()
|
|
mock_repo.remotes = [] # Empty remotes list - no 'origin'
|
|
mock_repo.active_branch.name = "main"
|
|
repo.repo = mock_repo
|
|
|
|
with patch("git.Git") as mock_git:
|
|
mock_git.return_value.ls_remote = MagicMock()
|
|
with pytest.raises(StoreGitError):
|
|
await repo.pull.__wrapped__(repo)
|
|
|
|
# Verify resolution issue was created
|
|
assert len(coresys.resolution.issues) == 1
|
|
assert coresys.resolution.issues[0].type == IssueType.CORRUPT_REPOSITORY
|
|
assert coresys.resolution.issues[0].context == ContextType.STORE
|
|
|
|
# Verify reset suggestion was created
|
|
assert len(coresys.resolution.suggestions) == 1
|
|
assert coresys.resolution.suggestions[0].type == SuggestionType.EXECUTE_RESET
|