Files
pkgmgr/tests/unit/pkgmgr/actions/code_scanning/test_code_scanning.py
Kevin Veen-Birkenbach b4e0594901 style: modernise typing and clean up lint findings
Repository-wide mechanical cleanup so `ruff check src tests` has a chance
of passing; no behavioural changes.

- Add `from __future__ import annotations` where PEP 604 unions are used.
  This has to come first: pyproject declares requires-python >= 3.9, where
  `X | None` is not evaluable at runtime unless annotations are stringified.
- Replace typing.List/Dict/Tuple/Set with the builtin generics and
  Optional[X] with X | None, then drop the imports that became unused.
  The four actions/*/__init__.py files needed this by hand because ruff
  leaves unused imports in __init__.py alone (possible re-exports).
- Strip shebangs from 72 importable modules. None of them are executable
  or invoked directly; the entry points are console_scripts and runpy.
- Flatten nested `with` blocks, collapse needless-bool returns, and apply
  the remaining mechanical ruff fixes (PIE810, FLY002, PERF102, FURB192,
  RUF059, I001).
- Pass check=False explicitly to the four subprocess.run() calls that
  inspect returncode themselves. That is the existing default.

Two rewrites are visible to mocks, so their tests move with them:
subprocess.run(stdout=PIPE, stderr=PIPE) became capture_output=True, and
open(path, "r", ...) lost the redundant mode.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-27 16:47:13 +02:00

99 lines
3.3 KiB
Python

from __future__ import annotations
import json
import os
import shutil
import tempfile
import unittest
from types import SimpleNamespace
from unittest import mock
from pkgmgr.actions.code_scanning import (
CodeScanningError,
download_code_scanning,
)
_ALERTS = [
{
"number": 1,
"state": "open",
"html_url": "https://example/1",
"rule": {
"id": "py/sql-injection",
"security_severity_level": "high",
"severity": "error",
"description": "SQL injection",
},
"most_recent_instance": {
"location": {"path": "app.py", "start_line": 10},
"message": {"text": "Possible SQL injection"},
},
"tool": {"name": "CodeQL"},
}
]
def _cp(stdout: str = "", returncode: int = 0, stderr: str = "") -> SimpleNamespace:
return SimpleNamespace(stdout=stdout, returncode=returncode, stderr=stderr)
def _fake_gh(args):
if args[:2] == ["repo", "view"]:
return _cp(stdout="owner/pkgmgr-cs-test\n")
endpoint = args[1] if len(args) > 1 else ""
if endpoint.endswith("code-scanning/alerts"):
return _cp(stdout=json.dumps(_ALERTS))
if endpoint.endswith("code-scanning/analyses"):
return _cp(stdout=json.dumps([{"id": 42, "created_at": "2026-01-01"}]))
return _cp(returncode=1, stderr=f"unexpected: {args}")
class TestDownloadCodeScanning(unittest.TestCase):
def test_writes_alerts_summary_and_analyses(self) -> None:
with tempfile.TemporaryDirectory() as tmp:
with (
mock.patch(
"pkgmgr.actions.code_scanning.shutil.which",
return_value="/usr/bin/gh",
),
mock.patch("pkgmgr.actions.code_scanning._gh", side_effect=_fake_gh),
):
result = download_code_scanning(output_dir=tmp)
self.assertEqual(result.repo, "owner/pkgmgr-cs-test")
self.assertEqual(result.alert_count, 1)
with open(os.path.join(tmp, "alerts.json"), encoding="utf-8") as f:
self.assertEqual(json.load(f), _ALERTS)
with open(os.path.join(tmp, "summary.md"), encoding="utf-8") as f:
summary = f.read()
self.assertIn("py/sql-injection", summary)
self.assertIn("app.py:10", summary)
self.assertIn("high", summary)
self.assertTrue(os.path.exists(os.path.join(tmp, "analyses.json")))
def test_default_output_dir_uses_repo_name(self) -> None:
with (
mock.patch(
"pkgmgr.actions.code_scanning.shutil.which", return_value="/usr/bin/gh"
),
mock.patch("pkgmgr.actions.code_scanning._gh", side_effect=_fake_gh),
):
result = download_code_scanning()
self.addCleanup(shutil.rmtree, "/tmp/pkgmgr-cs-test", ignore_errors=True)
self.assertTrue(
result.output_dir.startswith("/tmp/pkgmgr-cs-test/code-scanner/"),
msg=result.output_dir,
)
def test_errors_when_gh_missing(self) -> None:
with (
mock.patch("pkgmgr.actions.code_scanning.shutil.which", return_value=None),
self.assertRaises(CodeScanningError),
):
download_code_scanning(output_dir="/tmp/should-not-be-created")
if __name__ == "__main__": # pragma: no cover
unittest.main()