"""Tests for data collector.""" from unittest.mock import MagicMock from gitea_dashboard.collector import RepoData, collect_all def _make_repo( name="my-repo", full_name="admin/my-repo", description="A repo", open_issues_count=5, open_pr_counter=2, fork=False, archived=False, mirror=False, owner_login="admin", ): """Build a fake repo dict as returned by the Gitea API.""" return { "name": name, "full_name": full_name, "description": description, "open_issues_count": open_issues_count, "open_pr_counter": open_pr_counter, "fork": fork, "archived": archived, "mirror": mirror, "owner": {"login": owner_login}, } class TestCollectAll: """Test collect_all function.""" def test_basic_repo(self): """Collects repo data with release and milestones.""" client = MagicMock() client.get_repos.return_value = [_make_repo()] client.get_latest_release.return_value = { "tag_name": "v1.0", "published_at": "2026-01-01", } client.get_milestones.return_value = [ {"title": "v2.0", "open_issues": 3, "closed_issues": 2, "due_on": None}, ] result = collect_all(client) assert len(result) == 1 repo = result[0] assert isinstance(repo, RepoData) assert repo.name == "my-repo" assert repo.full_name == "admin/my-repo" assert repo.description == "A repo" assert repo.open_issues == 3 # 5 - 2 assert repo.is_fork is False assert repo.is_archived is False assert repo.is_mirror is False assert repo.latest_release == {"tag_name": "v1.0", "published_at": "2026-01-01"} assert len(repo.milestones) == 1 def test_repo_without_release(self): """Repo with no release gets None for latest_release.""" client = MagicMock() client.get_repos.return_value = [_make_repo()] client.get_latest_release.return_value = None client.get_milestones.return_value = [] result = collect_all(client) assert result[0].latest_release is None def test_repo_without_milestones(self): """Repo with no milestones gets empty list.""" client = MagicMock() client.get_repos.return_value = [_make_repo()] client.get_latest_release.return_value = None client.get_milestones.return_value = [] result = collect_all(client) assert result[0].milestones == [] def test_open_issues_subtracts_prs(self): """open_issues = open_issues_count - open_pr_counter.""" client = MagicMock() client.get_repos.return_value = [ _make_repo(open_issues_count=10, open_pr_counter=4), ] client.get_latest_release.return_value = None client.get_milestones.return_value = [] result = collect_all(client) assert result[0].open_issues == 6 def test_multiple_repos(self): """Collects data for multiple repos.""" client = MagicMock() client.get_repos.return_value = [ _make_repo(name="repo-a", full_name="admin/repo-a"), _make_repo(name="repo-b", full_name="org/repo-b", owner_login="org"), ] client.get_latest_release.return_value = None client.get_milestones.return_value = [] result = collect_all(client) assert len(result) == 2 assert result[0].name == "repo-a" assert result[1].name == "repo-b" # Verify correct owner/repo passed to enrichment calls client.get_latest_release.assert_any_call("admin", "repo-a") client.get_latest_release.assert_any_call("org", "repo-b") def test_fork_and_archived_flags(self): """Fork and archived flags are propagated.""" client = MagicMock() client.get_repos.return_value = [ _make_repo(fork=True, archived=True, mirror=True), ] client.get_latest_release.return_value = None client.get_milestones.return_value = [] result = collect_all(client) assert result[0].is_fork is True assert result[0].is_archived is True assert result[0].is_mirror is True class TestCollectAllFiltering: """Test collect_all filtering (include/exclude).""" def _setup_client(self, repo_names: list[str]) -> MagicMock: """Create a mock client returning repos with the given names.""" client = MagicMock() client.get_repos.return_value = [ _make_repo(name=n, full_name=f"admin/{n}") for n in repo_names ] client.get_latest_release.return_value = None client.get_milestones.return_value = [] return client def test_no_filter_returns_all(self): """Without include/exclude, all repos are returned (backward compat).""" client = self._setup_client(["alpha", "beta", "gamma"]) result = collect_all(client) assert [r.name for r in result] == ["alpha", "beta", "gamma"] def test_include_single(self): """Include filters repos by substring match.""" client = self._setup_client(["gitea-dashboard", "infra-core", "notes"]) result = collect_all(client, include=["dashboard"]) assert [r.name for r in result] == ["gitea-dashboard"] def test_include_multiple(self): """Multiple include patterns are OR-combined.""" client = self._setup_client(["gitea-dashboard", "infra-core", "notes"]) result = collect_all(client, include=["dashboard", "infra"]) assert [r.name for r in result] == ["gitea-dashboard", "infra-core"] def test_exclude_single(self): """Exclude removes repos matching the substring.""" client = self._setup_client(["gitea-dashboard", "old-fork", "notes"]) result = collect_all(client, exclude=["fork"]) assert [r.name for r in result] == ["gitea-dashboard", "notes"] def test_include_and_exclude(self): """Include is applied first, then exclude.""" client = self._setup_client(["projet-web", "projet-old", "infra"]) result = collect_all(client, include=["projet"], exclude=["old"]) assert [r.name for r in result] == ["projet-web"] def test_case_insensitive(self): """Filtering is case-insensitive.""" client = self._setup_client(["Gitea-Dashboard", "infra"]) result = collect_all(client, include=["dashboard"]) assert [r.name for r in result] == ["Gitea-Dashboard"] def test_no_match_returns_empty(self): """Returns empty list when no repo matches include filter.""" client = self._setup_client(["alpha", "beta"]) result = collect_all(client, include=["inexistant"]) assert result == [] def test_exclude_all_returns_empty(self): """Returns empty list when all repos are excluded.""" client = self._setup_client(["alpha", "beta"]) result = collect_all(client, exclude=["alpha", "beta"]) assert result == []