Initial curated RepoPulse source from 2524ccc3c1c27739996dccb2b7992989f88cd26d
This commit is contained in:
commit
0adf8ef5d9
90 files changed
+14315
No files matched your search
@@ -0,0 +1,122 @@
|
||||
from pathlib import Path
|
||||
from threading import BoundedSemaphore
|
||||
|
||||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from app import main as main_module
|
||||
from app.store import ScanStore
|
||||
|
||||
|
||||
def _repository(root: Path) -> Path:
|
||||
repository = root / "repo"
|
||||
repository.mkdir()
|
||||
(repository / "README.md").write_text("# Demo\n", encoding="utf-8")
|
||||
(repository / "LICENSE").write_text("MIT\n", encoding="utf-8")
|
||||
(repository / ".gitignore").write_text(".env\n", encoding="utf-8")
|
||||
(repository / "app.py").write_text("print('hello')\n", encoding="utf-8")
|
||||
return repository
|
||||
|
||||
|
||||
def test_scan_api_respects_root_and_exports_reports(tmp_path: Path) -> None:
|
||||
repository = _repository(tmp_path)
|
||||
main_module.SCAN_ROOT = str(tmp_path)
|
||||
main_module.store = ScanStore(str(tmp_path / "history.db"))
|
||||
main_module.SCAN_SLOTS = BoundedSemaphore(1)
|
||||
client = TestClient(main_module.app)
|
||||
|
||||
first_response = client.post("/api/v1/scans", json={"path": "repo"})
|
||||
assert first_response.status_code == 201
|
||||
first = first_response.json()
|
||||
assert first["repository_path"] == str(repository.resolve())
|
||||
|
||||
html = client.get(f"/api/v1/scans/{first['id']}/export?format=html")
|
||||
assert html.status_code == 200
|
||||
assert "RepoPulse report" in html.text
|
||||
assert "repo" in html.text
|
||||
|
||||
blocked = client.post("/api/v1/scans", json={"path": str(tmp_path.parent)})
|
||||
assert blocked.status_code == 422
|
||||
|
||||
|
||||
def test_scan_api_comparison_delta_and_trend(tmp_path: Path) -> None:
|
||||
repository = _repository(tmp_path)
|
||||
main_module.SCAN_ROOT = str(tmp_path)
|
||||
main_module.store = ScanStore(str(tmp_path / "history.db"))
|
||||
main_module.SCAN_SLOTS = BoundedSemaphore(1)
|
||||
client = TestClient(main_module.app)
|
||||
|
||||
first = client.post("/api/v1/scans", json={"path": "repo"}).json()
|
||||
(repository / "tests").mkdir()
|
||||
(repository / "tests" / "test_app.py").write_text("def test_ok():\n assert True\n", encoding="utf-8")
|
||||
second = client.post("/api/v1/scans", json={"path": "repo"}).json()
|
||||
|
||||
comparison = client.get(f"/api/v1/scans/{second['id']}/comparison")
|
||||
assert comparison.status_code == 200
|
||||
assert comparison.json()["previous_scan_id"] == first["id"]
|
||||
|
||||
delta = client.get(f"/api/v1/scans/{second['id']}/delta")
|
||||
assert delta.status_code == 200
|
||||
assert delta.json()["previous_health_score"] == first["health_score"]
|
||||
assert delta.json()["previous_finding_count"] == len(first["findings"])
|
||||
|
||||
trend = client.get(f"/api/v1/trend?path={repository}")
|
||||
assert trend.status_code == 200
|
||||
assert [item["scan_id"] for item in trend.json()] == [first["id"], second["id"]]
|
||||
|
||||
|
||||
def test_scan_api_rejects_when_scan_capacity_is_busy(tmp_path: Path) -> None:
|
||||
_repository(tmp_path)
|
||||
main_module.SCAN_ROOT = str(tmp_path)
|
||||
main_module.store = ScanStore(str(tmp_path / "history.db"))
|
||||
main_module.SCAN_SLOTS = BoundedSemaphore(1)
|
||||
client = TestClient(main_module.app)
|
||||
|
||||
assert main_module.SCAN_SLOTS.acquire(blocking=False)
|
||||
try:
|
||||
response = client.post("/api/v1/scans", json={"path": "repo"})
|
||||
finally:
|
||||
main_module.SCAN_SLOTS.release()
|
||||
|
||||
assert response.status_code == 429
|
||||
assert response.headers["retry-after"] == "2"
|
||||
|
||||
|
||||
def test_scan_api_rejects_symlinked_repository_outside_root(tmp_path: Path) -> None:
|
||||
outside = tmp_path.parent / f"{tmp_path.name}-outside-repository"
|
||||
outside.mkdir()
|
||||
(outside / "app.py").write_text("print('outside')\n", encoding="utf-8")
|
||||
linked = tmp_path / "linked-repository"
|
||||
try:
|
||||
linked.symlink_to(outside, target_is_directory=True)
|
||||
except (NotImplementedError, OSError):
|
||||
pytest.skip("directory symlinks are unavailable in this environment")
|
||||
|
||||
main_module.SCAN_ROOT = str(tmp_path)
|
||||
main_module.store = ScanStore(str(tmp_path / "history.db"))
|
||||
main_module.SCAN_SLOTS = BoundedSemaphore(1)
|
||||
client = TestClient(main_module.app)
|
||||
|
||||
response = client.post("/api/v1/scans", json={"path": "linked-repository"})
|
||||
|
||||
assert response.status_code == 422
|
||||
|
||||
|
||||
def test_mutating_api_requires_operator_token_only_when_configured(tmp_path: Path, monkeypatch) -> None:
|
||||
_repository(tmp_path)
|
||||
main_module.SCAN_ROOT = str(tmp_path)
|
||||
main_module.store = ScanStore(str(tmp_path / "history.db"))
|
||||
main_module.SCAN_SLOTS = BoundedSemaphore(1)
|
||||
monkeypatch.setenv("REPOPULSE_OPERATOR_TOKEN", "test-operator-token")
|
||||
client = TestClient(main_module.app)
|
||||
|
||||
assert client.get("/api/v1/meta").status_code == 200
|
||||
denied = client.post("/api/v1/scans", json={"path": "repo"})
|
||||
assert denied.status_code == 401
|
||||
|
||||
allowed = client.post(
|
||||
"/api/v1/scans",
|
||||
json={"path": "repo"},
|
||||
headers={"X-RepoPulse-Token": "test-operator-token"},
|
||||
)
|
||||
assert allowed.status_code == 201
|
||||
Reference in new issue
Block a user