Files
ai-exchange/tests/unit/test_script_static_checks.py

97 lines
4.0 KiB
Python

import contextlib
import io
import json
import os
import tempfile
import unittest
from pathlib import Path
os.environ.setdefault("DATABASE_URL", "postgresql+psycopg://user:pass@localhost:5432/ai_ict_test")
from scripts import run_script_static_checks
class ScriptStaticChecksTests(unittest.TestCase):
def test_detects_ambient_env_fallback(self) -> None:
with tempfile.TemporaryDirectory() as tmpdir:
script_dir = Path(tmpdir)
(script_dir / "bad.py").write_text("env = env or os.environ\n", encoding="utf-8")
result = run_script_static_checks.run_script_static_checks(script_dir=script_dir)
self.assertFalse(result["passed"])
self.assertEqual(result["findings"][0]["file"], "bad.py")
self.assertEqual(result["findings"][0]["reason"], "empty_env_must_not_fall_back_to_ambient_environment")
def test_passes_clean_scripts(self) -> None:
with tempfile.TemporaryDirectory() as tmpdir:
script_dir = Path(tmpdir)
(script_dir / "ok.py").write_text("env = os.environ if env is None else env\n", encoding="utf-8")
result = run_script_static_checks.run_script_static_checks(script_dir=script_dir)
self.assertTrue(result["passed"])
self.assertEqual(result["script_count"], 1)
def test_detects_python_syntax_errors(self) -> None:
with tempfile.TemporaryDirectory() as tmpdir:
script_dir = Path(tmpdir)
(script_dir / "broken.py").write_text("def nope(:\n", encoding="utf-8")
result = run_script_static_checks.run_script_static_checks(script_dir=script_dir)
self.assertFalse(result["passed"])
self.assertEqual(result["findings"][0]["reason"], "script_python_syntax_error")
def test_detects_missing_system_exit_for_automation_entrypoints(self) -> None:
with tempfile.TemporaryDirectory() as tmpdir:
script_dir = Path(tmpdir)
(script_dir / "run_pipeline.py").write_text(
"def main() -> int:\n return 2\n\nif __name__ == '__main__':\n main()\n",
encoding="utf-8",
)
result = run_script_static_checks.run_script_static_checks(script_dir=script_dir)
self.assertFalse(result["passed"])
self.assertEqual(result["findings"][0]["reason"], "automation_entrypoint_must_preserve_exit_code")
def test_detects_missing_system_exit_for_any_script_with_main(self) -> None:
with tempfile.TemporaryDirectory() as tmpdir:
script_dir = Path(tmpdir)
(script_dir / "ad_hoc.py").write_text(
"def main() -> int:\n return 0\n\nif __name__ == '__main__':\n main()\n",
encoding="utf-8",
)
result = run_script_static_checks.run_script_static_checks(script_dir=script_dir)
self.assertFalse(result["passed"])
self.assertEqual(result["findings"][0]["file"], "ad_hoc.py")
self.assertEqual(result["findings"][0]["reason"], "automation_entrypoint_must_preserve_exit_code")
def test_detects_python39_incompatible_optional_annotation(self) -> None:
with tempfile.TemporaryDirectory() as tmpdir:
script_dir = Path(tmpdir)
(script_dir / "bad.py").write_text("def f(value: str | None = None):\n return value\n", encoding="utf-8")
result = run_script_static_checks.run_script_static_checks(script_dir=script_dir)
self.assertFalse(result["passed"])
self.assertEqual(result["findings"][0]["reason"], "python39_incompatible_pep604_optional_annotation")
def test_json_output_is_machine_readable(self) -> None:
output = io.StringIO()
with contextlib.redirect_stdout(output):
exit_code = run_script_static_checks.main(env={"SCRIPT_CHECKS_OUTPUT_JSON": "1"})
summary = json.loads(output.getvalue())
self.assertEqual(exit_code, 0)
self.assertTrue(summary["passed"])
self.assertGreaterEqual(summary["script_count"], 1)
if __name__ == "__main__":
unittest.main()