audit-labs/audit-tools
A collection of scripts, queries, and other goodies you can use in an audit.
clone: git clone https://gitbay.org/audit-labs/audit-tools.git
v1.0.0: tui/tests/test_gitlab_runner.py · raw
1"""Tests for the TUI's GitLab audit orchestration.
2
3Stub the network-bound collectors and verify run_audit's wiring: base vs
4project-scoped dispatch, the shared project cache, per-check error handling,
5and that the CSV package (per-check files + summary) is written.
6"""
7
8import csv
9import os
10
11import pytest
12
13from tui import gitlab_runner as r
14
15
16@pytest.fixture
17def fake_checks(monkeypatch):
18 calls = {}
19
20 def base_fn(group, cfg):
21 calls["base"] = (group, cfg)
22 return [{"username": "alice"}, {"username": "bob"}]
23
24 def projects_fn(group, cfg, projects):
25 calls["projects"] = (group, cfg, projects)
26 return [{"project": p["path_with_namespace"]} for p in projects]
27
28 def boom_fn(group, cfg):
29 raise RuntimeError("kaboom")
30
31 checks = [
32 r.Check("base", "Base", base_fn, "base.csv"),
33 r.Check("projects", "Projects", projects_fn, "projects.csv", arg="projects"),
34 r.Check("boom", "Boom", boom_fn, "boom.csv"),
35 ]
36 monkeypatch.setattr(r, "CHECKS", checks)
37
38 fetch_calls = []
39
40 def fake_fetch(group, cfg):
41 fetch_calls.append(group)
42 return [{"id": 1, "path_with_namespace": "grp/proj"}]
43
44 monkeypatch.setattr(r.projects, "fetch_projects", fake_fetch)
45
46 return calls, fetch_calls
47
48
49def run(tmp_path, keys, base_url="https://gitlab.com/api/v4"):
50 events = []
51 sections = r.run_audit(
52 group="grp",
53 token="tok",
54 base_url=base_url,
55 output_dir=str(tmp_path),
56 selected_keys=keys,
57 on_event=events.append,
58 )
59 return events, sections
60
61
62def test_argument_dispatch_and_files(tmp_path, fake_checks):
63 calls, _ = fake_checks
64 run(tmp_path, ["base", "projects"])
65
66 assert calls["base"][0] == "grp"
67 assert calls["projects"][2] == [{"id": 1, "path_with_namespace": "grp/proj"}]
68
69 for name in ("base.csv", "projects.csv", "summary.txt"):
70 assert os.path.exists(tmp_path / name), name
71
72 with open(tmp_path / "projects.csv", newline="") as f:
73 rows = list(csv.DictReader(f))
74 assert rows == [{"project": "grp/proj"}]
75
76
77def test_project_cache_fetched_once(tmp_path, fake_checks):
78 _, fetch_calls = fake_checks
79 run(tmp_path, ["projects", "base"])
80 assert fetch_calls == ["grp"]
81
82
83def test_project_cache_skipped_when_not_needed(tmp_path, fake_checks):
84 _, fetch_calls = fake_checks
85 run(tmp_path, ["base"])
86 assert fetch_calls == []
87
88
89def test_failing_check_does_not_abort_run(tmp_path, fake_checks):
90 events, sections = run(tmp_path, ["boom", "base"])
91
92 kinds = [(e.kind, e.label) for e in events]
93 assert ("error", "Boom") in kinds
94 assert ("done", "Base") in kinds
95
96 labels = dict(sections)
97 assert labels["Boom"] == 0
98 assert labels["Base"] == 2
99
100
101def test_base_url_reaches_cfg(tmp_path, fake_checks):
102 calls, _ = fake_checks
103 run(tmp_path, ["base"], base_url="https://gitlab.example.com/api/v4/")
104 # build_cfg strips the trailing slash.
105 assert calls["base"][1]["base_url"] == "https://gitlab.example.com/api/v4"
106 assert calls["base"][1]["headers"]["PRIVATE-TOKEN"] == "tok"
107
108
109def test_premium_checks_off_by_default():
110 assert "approval_rules" not in r.DEFAULT_SELECTION
111 assert "audit_events" not in r.DEFAULT_SELECTION
112 assert "password_policy" not in r.DEFAULT_SELECTION
113 assert "group_members" in r.DEFAULT_SELECTION