from __future__ import annotations import base64 import pytest from vuln_scan.github.base_client import AuthConfig from vuln_scan.github.repo_client import GitHubRepoClient # -------------------------------------------------------- # Fakes # -------------------------------------------------------- class _FakeUnknownObjectException(Exception): pass class _FakeGithubException(Exception): def __init__(self, status: int, data: dict) -> None: super().__init__("github error") self.status = status self.data = data class _FakeContentFile: def __init__(self, content: str | None) -> None: self.content = content class _FakeRepo: def __init__(self) -> None: self.default_branch = "main" self._content_response = None self._content_exception: Exception | None = None def get_contents(self, path: str, ref: str): if self._content_exception: raise self._content_exception return self._content_response # -------------------------------------------------------- # Fixtures # -------------------------------------------------------- @pytest.fixture def fake_repo() -> _FakeRepo: return _FakeRepo() @pytest.fixture def client(fake_repo: _FakeRepo) -> GitHubRepoClient: """Pre-wired client bypassing __post_init__.""" c = GitHubRepoClient.__new__(GitHubRepoClient) c._repo = fake_repo return c @pytest.fixture def _patch_content_file(monkeypatch: pytest.MonkeyPatch) -> None: """Patch ContentFile so isinstance checks work with _FakeContentFile.""" monkeypatch.setattr("vuln_scan.github.repo_client.ContentFile", _FakeContentFile) # -------------------------------------------------------- # __post_init__ # -------------------------------------------------------- def test_post_init_sets_repo_from_owner_and_name(monkeypatch: pytest.MonkeyPatch) -> None: fake_repo = _FakeRepo() class _FakeGH: def get_repo(self, full_name: str): assert full_name == "org/repo" return fake_repo monkeypatch.setattr( "vuln_scan.github.repo_client.GitHubBaseClient.__post_init__", lambda self: setattr(self, "_gh", _FakeGH()), ) client = GitHubRepoClient(auth_config=AuthConfig(token="t"), owner="org", repo="repo") assert client._repo is fake_repo # -------------------------------------------------------- # get_default_branch # -------------------------------------------------------- def test_get_default_branch(client: GitHubRepoClient) -> None: assert client.get_default_branch() == "main" # -------------------------------------------------------- # get_file_content — returns None cases # -------------------------------------------------------- @pytest.mark.parametrize( "setup_repo, patch_ids, expected", [ pytest.param( {"exception": _FakeUnknownObjectException("missing")}, {"UnknownObjectException": _FakeUnknownObjectException}, None, id="missing-file", ), pytest.param( {"response": ["dir", "items"]}, {}, None, id="directory-list", ), pytest.param( {"response": object()}, {"ContentFile": _FakeContentFile}, None, id="non-contentfile-type", ), pytest.param( {"response": _FakeContentFile(None)}, {"ContentFile": _FakeContentFile}, None, id="content-is-none", ), pytest.param( {"response": _FakeContentFile(base64.b64encode(b"hello world").decode())}, {"ContentFile": _FakeContentFile}, "hello world", id="decodes-base64-content", ), ], ) def test_get_file_content( monkeypatch: pytest.MonkeyPatch, fake_repo: _FakeRepo, client: GitHubRepoClient, setup_repo: dict, patch_ids: dict, expected: str | None, ) -> None: if "exception" in setup_repo: fake_repo._content_exception = setup_repo["exception"] if "response" in setup_repo: fake_repo._content_response = setup_repo["response"] for attr, fake_cls in patch_ids.items(): monkeypatch.setattr(f"vuln_scan.github.repo_client.{attr}", fake_cls) assert client.get_file_content("a.txt", "main") == expected # -------------------------------------------------------- # get_file_content — re-raises GithubException # -------------------------------------------------------- def test_get_file_content_reraises_github_exception( monkeypatch: pytest.MonkeyPatch, fake_repo: _FakeRepo, client: GitHubRepoClient, ) -> None: monkeypatch.setattr("vuln_scan.github.repo_client.GithubException", _FakeGithubException) fake_repo._content_exception = _FakeGithubException(500, {"message": "oops"}) with pytest.raises(_FakeGithubException): client.get_file_content("a.txt", "main")