from __future__ import annotations from pathlib import Path import pytest from tests.helpers import StubEcosystemHandler from vuln_scan.core.models import Ecosystem from vuln_scan.core.registry import EcosystemRegistry from vuln_scan.ecosystems.base import EcosystemHandler # -------------------------------------------------------- # Fixtures # -------------------------------------------------------- @pytest.fixture def pip_handler() -> StubEcosystemHandler: return StubEcosystemHandler( ecosystem=Ecosystem.PIP, handler_id="pip", manifest_names={"requirements.txt"}, manifest_globs={"*requirements*.txt"}, ) @pytest.fixture def npm_handler() -> StubEcosystemHandler: return StubEcosystemHandler( ecosystem=Ecosystem.NPM, handler_id="npm", manifest_names={"package.json"}, ) @pytest.fixture def registry( pip_handler: StubEcosystemHandler, npm_handler: StubEcosystemHandler, ) -> EcosystemRegistry: return EcosystemRegistry([pip_handler, npm_handler]) # -------------------------------------------------------- # Constructor validation # -------------------------------------------------------- @pytest.mark.parametrize( "handlers, match", [ pytest.param([], "requires at least one handler", id="empty"), pytest.param( [ StubEcosystemHandler(ecosystem=Ecosystem.PIP, handler_id="pip1"), StubEcosystemHandler(ecosystem=Ecosystem.PIP, handler_id="pip2"), ], "Duplicate handler for ecosystem", id="duplicate", ), ], ) def test_registry_rejects_invalid_handlers( handlers: list[EcosystemHandler], match: str, ) -> None: with pytest.raises(ValueError, match=match): EcosystemRegistry(handlers) # -------------------------------------------------------- # for_manifest # -------------------------------------------------------- @pytest.mark.parametrize( "manifest_path, expected_ecosystem", [ pytest.param("services/api/requirements.txt", Ecosystem.PIP, id="exact-filename"), pytest.param("requirements-dev.txt", Ecosystem.PIP, id="glob-match"), pytest.param("package.json", Ecosystem.NPM, id="npm-exact"), pytest.param(Path("no/match/Gemfile"), None, id="no-match-returns-none"), ], ) def test_for_manifest( registry: EcosystemRegistry, manifest_path: str | Path, expected_ecosystem: Ecosystem | None, ) -> None: handler = registry.for_manifest(manifest_path) if expected_ecosystem is None: assert handler is None else: assert handler is not None assert handler.ecosystem == expected_ecosystem # -------------------------------------------------------- # for_ecosystem # -------------------------------------------------------- @pytest.mark.parametrize( "ecosystem, expected_found", [ pytest.param(Ecosystem.PIP, True, id="pip-found"), pytest.param(Ecosystem.NPM, True, id="npm-found"), pytest.param(Ecosystem.RUBYGEMS, False, id="rubygems-not-found"), ], ) def test_for_ecosystem( registry: EcosystemRegistry, pip_handler: StubEcosystemHandler, npm_handler: StubEcosystemHandler, ecosystem: Ecosystem, expected_found: bool, ) -> None: handler = registry.for_ecosystem(ecosystem) if not expected_found: assert handler is None else: expected = pip_handler if ecosystem == Ecosystem.PIP else npm_handler assert handler is expected # -------------------------------------------------------- # handlers property # -------------------------------------------------------- def test_handlers_property_returns_tuple_in_registration_order( registry: EcosystemRegistry, pip_handler: StubEcosystemHandler, npm_handler: StubEcosystemHandler, ) -> None: assert registry.handlers == (pip_handler, npm_handler)