Coverage for tests / unit / test_package_imports.py: 89%
37 statements
« prev ^ index » next coverage.py v7.13.0, created at 2026-04-03 18:53 +0000
« prev ^ index » next coverage.py v7.13.0, created at 2026-04-03 18:53 +0000
1"""Smoke tests to verify all package modules are importable.
3This test ensures that:
41. All modules listed in pyproject.toml are actually included in the package build
52. All packages in the source tree are listed in pyproject.toml (catches forgotten packages)
7This prevents packaging errors where a module exists in the source tree but is
8missing from the packages list (like the 0.43.0 bug with lintro.utils.environment).
9"""
11import importlib
12from pathlib import Path
14import pytest
16# Project root directory
17PROJECT_ROOT = Path(__file__).parent.parent.parent
20def _discover_packages_from_source() -> set[str]:
21 """Discover all Python packages in the lintro source tree.
23 Returns:
24 Set of package names (e.g., "lintro.utils.environment").
25 """
26 lintro_dir = PROJECT_ROOT / "lintro"
27 packages: set[str] = set()
29 for path in lintro_dir.rglob("__init__.py"):
30 # Convert path to package name
31 relative = path.parent.relative_to(PROJECT_ROOT)
32 package_name = ".".join(relative.parts)
33 packages.add(package_name)
35 return packages
38def _get_packages_from_pyproject() -> set[str]:
39 """Read the packages list from pyproject.toml.
41 Returns:
42 Set of package names listed in [tool.setuptools].packages.
43 """
44 import tomllib
46 pyproject_path = PROJECT_ROOT / "pyproject.toml"
47 with pyproject_path.open("rb") as f:
48 data = tomllib.load(f)
50 packages = data.get("tool", {}).get("setuptools", {}).get("packages", [])
51 return set(packages)
54def _get_configured_packages() -> list[str]:
55 """Get packages from pyproject.toml for parametrized tests."""
56 return sorted(_get_packages_from_pyproject())
59@pytest.mark.parametrize("package", _get_configured_packages())
60def test_package_importable(package: str) -> None:
61 """Verify each configured package can be imported successfully."""
62 # Note: We intentionally don't clear sys.modules here because doing so
63 # would reinitialize global singletons (like tool_manager in lintro.tools)
64 # which breaks other tests that depend on monkeypatching those singletons.
65 # The import test is still valid - if the package is missing from
66 # pyproject.toml, it won't be importable in a fresh install.
67 try:
68 # nosemgrep: python.lang.security.audit.non-literal-import.non-literal-import
69 importlib.import_module(package)
70 except ImportError as e:
71 pytest.fail(
72 f"Failed to import '{package}': {e}\n"
73 f"This likely means the package is missing from "
74 f"[tool.setuptools].packages in pyproject.toml",
75 )
78def test_all_source_packages_are_configured() -> None:
79 """Verify all packages in the source tree are listed in pyproject.toml.
81 This catches the case where a developer adds a new package directory
82 but forgets to add it to [tool.setuptools].packages.
83 """
84 source_packages = _discover_packages_from_source()
85 configured_packages = _get_packages_from_pyproject()
87 missing = source_packages - configured_packages
88 if missing:
89 missing_list = "\n - ".join(sorted(missing))
90 pytest.fail(
91 f"Found {len(missing)} package(s) in source tree not listed in "
92 f"pyproject.toml [tool.setuptools].packages:\n - {missing_list}\n\n"
93 f"Add these packages to pyproject.toml to include them in the build.",
94 )
97def test_doctor_command_imports() -> None:
98 """Verify the doctor command and its dependencies are importable.
100 This is a regression test for the 0.43.0 packaging bug where
101 lintro.utils.environment was missing from the package.
102 """
103 from lintro.cli_utils.commands import doctor # noqa: F401
104 from lintro.utils.environment import ( # noqa: F401
105 CIEnvironment,
106 EnvironmentReport,
107 GoInfo,
108 LintroInfo,
109 NodeInfo,
110 ProjectInfo,
111 PythonInfo,
112 RubyInfo,
113 RustInfo,
114 SystemInfo,
115 collect_full_environment,
116 render_environment_report,
117 )