"""Every agent is its own image (Development Plan ยง3, M7.6): nisse-review's runs as its own user with git and only its own agent, and the test stage over it keeps what it installed. Builds the images, so it needs Docker.""" import shutil import subprocess from pathlib import Path import pytest ROOT = Path(__file__).parent.parent.parent pytestmark = [ pytest.mark.integration, pytest.mark.skipif( shutil.which("docker") is None or subprocess.run( ["docker", "info"], capture_output=True, check=False ).returncode, reason="needs Docker", ), ] # The installed packages' files and their times, hashed: the same in an # image and the test stage over it means the tests ran against what was # built, not a reinstall (even one from the same source). HASH = ( "import hashlib, pathlib, nisse_review, nisse_core; h = hashlib.sha256(); " "[h.update(p.read_bytes() + str(p.stat().st_mtime_ns).encode()) for m in (nisse_review, nisse_core) " "for p in sorted(pathlib.Path(m.__file__).parent.rglob('*.py'))]; " "print(h.hexdigest())" ) def build(tag: str, *args: str) -> str: subprocess.run( ["docker", "build", "--quiet", "-t", tag, *args, str(ROOT)], check=True, capture_output=True, timeout=1200, ) return tag def sh(image: str, script: str) -> subprocess.CompletedProcess[str]: return subprocess.run( ["docker", "run", "--rm", "--entrypoint", "sh", image, "-c", script], capture_output=True, text=True, timeout=120, check=False, ) @pytest.fixture(scope="module") def review() -> str: return build("nisse-review:image-test", "--target", "review") def test_the_reviewer_runs_as_its_own_user_with_git(review: str) -> None: done = sh(review, "id -un; git --version; command -v nisse-review") user, git, entry = done.stdout.splitlines() assert user == "nisse-review" and git.startswith("git version") assert entry == "/opt/nisse/bin/nisse-review" inspected = subprocess.run( ["docker", "inspect", "-f", "{{json .Config.Volumes}}", review], capture_output=True, text=True, check=True, ).stdout assert "/var/lib/nisse-review" in inspected def test_the_reviewer_image_holds_no_other_agent(review: str) -> None: assert sh(review, "python -c 'import nisse_review'").returncode == 0 assert sh(review, "python -c 'import nisse_dev'").returncode != 0 def test_its_state_is_writable_and_its_config_folder_is_not(review: str) -> None: assert sh(review, "touch /var/lib/nisse-review/x").returncode == 0 assert sh(review, "touch /etc/nisse-review/x").returncode != 0 def test_the_test_stage_over_it_keeps_what_it_installed(review: str) -> None: tests = build( "nisse-review:image-test-tests", "--target", "test", "--build-arg", f"TEST_BASE={review}", ) both = sh(tests, "python -c 'import nisse_dev, nisse_review, pytest'") assert both.returncode == 0, both.stderr # the suite's imports resolve assert ( sh(review, f'python -c "{HASH}"').stdout == sh(tests, f'python -c "{HASH}"').stdout )