0.0.5-alpha.5
This commit is contained in:
132
tests/test_rust_export_completeness.py
Normal file
132
tests/test_rust_export_completeness.py
Normal file
@@ -0,0 +1,132 @@
|
||||
# file: tests/test_rust_export_completeness.py
|
||||
# version: 1
|
||||
|
||||
"""Regression tests for the optional Rust crate-root façade audit."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import pathlib
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
sys.path.insert(0, str(pathlib.Path(__file__).resolve().parents[1] / "scripts"))
|
||||
|
||||
from audit_rust_export_completeness import audit_workspace # noqa: E402
|
||||
|
||||
|
||||
class RustExportCompletenessTests(unittest.TestCase):
|
||||
def setUp(self) -> None:
|
||||
self.temporary = tempfile.TemporaryDirectory()
|
||||
self.addCleanup(self.temporary.cleanup)
|
||||
self.root = pathlib.Path(self.temporary.name)
|
||||
|
||||
def crate(self, relative: str, root_source: str, files: dict[str, str]) -> pathlib.Path:
|
||||
crate = self.root / relative
|
||||
(crate / "src").mkdir(parents=True)
|
||||
(crate / "Cargo.toml").write_text(
|
||||
'[package]\nname = "fixture"\nversion = "0.1.0"\nedition = "2024"\n',
|
||||
encoding="utf-8",
|
||||
)
|
||||
(crate / "src/lib.rs").write_text(root_source, encoding="utf-8")
|
||||
for name, text in files.items():
|
||||
path = crate / name
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
path.write_text(text, encoding="utf-8")
|
||||
return crate
|
||||
|
||||
def candidates(self) -> list[object]:
|
||||
_, candidates = audit_workspace(self.root)
|
||||
return candidates
|
||||
|
||||
def test_nested_crate_layout_is_discovered(self) -> None:
|
||||
self.crate("crates/common/example-lib", "mod api;\npub use self::api::Thing;\n", {"src/api.rs": "pub struct Thing;\n"})
|
||||
crates, candidates = audit_workspace(self.root)
|
||||
self.assertEqual(len(crates), 1)
|
||||
self.assertEqual(candidates, [])
|
||||
|
||||
def test_missing_public_reexport_is_rejected(self) -> None:
|
||||
self.crate("crates/example-lib", "mod api;\n", {"src/api.rs": "pub struct Thing;\n"})
|
||||
candidates = self.candidates()
|
||||
self.assertEqual([candidate.code for candidate in candidates], ["RUST-API-201"])
|
||||
|
||||
def test_external_crate_public_item_requires_reexport(self) -> None:
|
||||
self.crate(
|
||||
"crates/example-lib",
|
||||
"mod api;\nmod consumer;\n",
|
||||
{
|
||||
"src/api.rs": "pub(crate) const VALUE: u8 = 1;\n",
|
||||
"src/consumer.rs": "pub(crate) fn value() -> u8 { return crate::api::VALUE; }\n",
|
||||
},
|
||||
)
|
||||
self.assertTrue(any(candidate.code == "RUST-API-201" for candidate in self.candidates()))
|
||||
|
||||
def test_unused_crate_public_item_does_not_force_reexport(self) -> None:
|
||||
self.crate("crates/example-lib", "mod api;\n", {"src/api.rs": "pub(crate) const VALUE: u8 = 1;\n"})
|
||||
self.assertEqual(self.candidates(), [])
|
||||
|
||||
def test_exported_item_requires_crate_root_path(self) -> None:
|
||||
self.crate(
|
||||
"crates/example-lib",
|
||||
"mod api;\nmod consumer;\npub use self::api::Thing;\n",
|
||||
{
|
||||
"src/api.rs": "pub struct Thing;\n",
|
||||
"src/consumer.rs": "pub(crate) fn make() -> crate::api::Thing { return crate::api::Thing; }\n",
|
||||
},
|
||||
)
|
||||
candidates = self.candidates()
|
||||
self.assertTrue(any(candidate.code == "RUST-IMPORT-201" for candidate in candidates))
|
||||
|
||||
def test_grouped_and_aliased_reexports_are_understood(self) -> None:
|
||||
self.crate(
|
||||
"crates/example-lib",
|
||||
"mod api;\npub use self::api::{First, Second as PublicSecond};\n",
|
||||
{"src/api.rs": "pub struct First;\npub struct Second;\n"},
|
||||
)
|
||||
self.assertEqual(self.candidates(), [])
|
||||
|
||||
def test_mod_rs_maps_to_parent_module(self) -> None:
|
||||
self.crate(
|
||||
"crates/example-lib",
|
||||
"mod api;\npub use self::api::Thing;\n",
|
||||
{"src/api/mod.rs": "pub struct Thing;\n"},
|
||||
)
|
||||
self.assertEqual(self.candidates(), [])
|
||||
|
||||
def test_comments_and_literals_do_not_create_long_path_candidates(self) -> None:
|
||||
self.crate(
|
||||
"crates/example-lib",
|
||||
"mod api;\nmod consumer;\npub use self::api::Thing;\n",
|
||||
{
|
||||
"src/api.rs": "pub struct Thing;\n",
|
||||
"src/consumer.rs": 'pub(crate) fn text() -> &\'static str { return "crate::api::Thing"; } // crate::api::Thing\n',
|
||||
},
|
||||
)
|
||||
self.assertEqual(self.candidates(), [])
|
||||
|
||||
def test_cli_status_for_clean_violation_report_only_and_missing_crates(self) -> None:
|
||||
script = pathlib.Path(__file__).resolve().parents[1] / "scripts" / "audit_rust_export_completeness.py"
|
||||
|
||||
def run(root: pathlib.Path, *extra: str) -> subprocess.CompletedProcess[str]:
|
||||
return subprocess.run(
|
||||
[sys.executable, "-B", str(script), "--root", str(root), *extra],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=False,
|
||||
)
|
||||
|
||||
empty = self.root / "empty"
|
||||
empty.mkdir()
|
||||
self.assertEqual(run(empty).returncode, 2)
|
||||
|
||||
self.crate("crates/example-lib", "mod api;\npub use self::api::Thing;\n", {"src/api.rs": "pub struct Thing;\n"})
|
||||
self.assertEqual(run(self.root).returncode, 0)
|
||||
|
||||
(self.root / "crates/example-lib/src/lib.rs").write_text("mod api;\n", encoding="utf-8")
|
||||
self.assertEqual(run(self.root).returncode, 1)
|
||||
self.assertEqual(run(self.root, "--report-only").returncode, 0)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user