Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
190 changes: 134 additions & 56 deletions .github/workflows/rebuild-rpms.yml

Large diffs are not rendered by default.

14 changes: 14 additions & 0 deletions config/buildroot-lock.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"schema": 1,
"image": {
"reference": "quay.io/fedora/fedora:44",
"pinned": "quay.io/fedora/fedora:44@sha256:629e314a46769025e2e0252a0f212b8efe4cc8adb6691cd5604cd6a3934bd18f"
},
"release": "44",
"repositories": {
"fedora_base": "fedora-44",
"fedora_updates": "fedora-44-updates",
"hummingbird": "config/hummingbird.repo"
},
"measured_at": "2026-08-31T05:41:57Z"
}
44 changes: 44 additions & 0 deletions tests/test_capture_buildroot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#!/usr/bin/env python3

import json
import tempfile
import unittest
from pathlib import Path
from unittest.mock import patch

from tools.capture_buildroot import nevra, query_rpms


class CaptureBuildrootTests(unittest.TestCase):
def test_nevra_with_epoch(self) -> None:
record = {"name": "openssl-libs", "epoch": "1", "version": "3.5.7", "release": "2.fc44", "arch": "x86_64"}
self.assertEqual(nevra(record), "1:openssl-libs-3.5.7-2.fc44.x86_64")

def test_nevra_without_epoch(self) -> None:
record = {"name": "glibc", "epoch": "0", "version": "2.42", "release": "1.fc44", "arch": "x86_64"}
self.assertEqual(nevra(record), "glibc-2.42-1.fc44.x86_64")

def test_nevra_none_epoch(self) -> None:
record = {"name": "bash", "epoch": "0", "version": "5.3", "release": "1.fc44", "arch": "x86_64"}
self.assertEqual(nevra(record), "bash-5.3-1.fc44.x86_64")


class EmitManifestTests(unittest.TestCase):
def test_collect_rpms_excludes_source_rpms(self) -> None:
from tools.emit_manifest import collect_rpms
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
(root / "pkg").mkdir()
(root / "pkg" / "foo-1.0-1.x86_64.rpm").write_bytes(b"")
(root / "pkg" / "foo-1.0-1.src.rpm").write_bytes(b"")
# Mock rpm -qp since we do not have real RPM files
with patch("tools.emit_manifest.subprocess.check_output", return_value="foo-1.0-1.x86_64"):
with patch("tools.emit_manifest.sha256_file", return_value="abc123"):
with patch.object(Path, "stat", lambda self: unittest.mock.Mock(st_size=0)):
result = collect_rpms(root)
self.assertEqual(len(result), 1)
self.assertEqual(result[0]["nevra"], "foo-1.0-1.x86_64")


if __name__ == "__main__":
unittest.main()
167 changes: 167 additions & 0 deletions tests/test_emit_manifest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
#!/usr/bin/env python3

import json
import tempfile
import unittest
from pathlib import Path
from unittest.mock import patch


class CollectBuildrootLocksTests(unittest.TestCase):
def test_collects_preflight_and_per_package_locks(self) -> None:
from tools.emit_manifest import collect_buildroot_locks
with tempfile.TemporaryDirectory() as tmpdir:
report_dir = Path(tmpdir)
(report_dir / "buildroot-lock-preflight.json").write_text(
json.dumps({"image_ref": "preflight-image", "package_count": 10})
)
(report_dir / "buildroot-udev.json").write_text(
json.dumps({"image_ref": "udev-image", "package_count": 20})
)
(report_dir / "buildroot-firefox.json").write_text(
json.dumps({"image_ref": "firefox-image", "package_count": 30})
)
result = collect_buildroot_locks(report_dir)
self.assertIn("preflight", result)
self.assertIn("udev", result)
self.assertIn("firefox", result)
self.assertEqual(result["preflight"]["image_ref"], "preflight-image")

def test_returns_empty_dict_when_no_locks(self) -> None:
from tools.emit_manifest import collect_buildroot_locks
with tempfile.TemporaryDirectory() as tmpdir:
result = collect_buildroot_locks(Path(tmpdir))
self.assertEqual(result, {})


class CollectSourceReportsTests(unittest.TestCase):
def test_collects_source_reports_excluding_special_files(self) -> None:
from tools.emit_manifest import collect_source_reports
with tempfile.TemporaryDirectory() as tmpdir:
report_dir = Path(tmpdir)
(report_dir / "udev.json").write_text(json.dumps({
"package": "udev", "sha512": "abc", "resolved_url": "https://example.com/udev.tar.gz",
"signature_verified": {"verified": True},
"sha256_url": "https://example.com/SHA256",
}))
(report_dir / "firefox.json").write_text(json.dumps({
"package": "firefox", "sha512": "def", "resolved_url": "https://example.com/firefox.tar.gz",
}))
(report_dir / "buildroot-udev.json").write_text(
json.dumps({"should": "be skipped"})
)
(report_dir / "signature-report.json").write_text(
json.dumps({"should": "be skipped"})
)
(report_dir / "manifest.json").write_text(
json.dumps({"should": "be skipped"})
)
result = collect_source_reports(report_dir)
self.assertEqual(len(result), 2)
packages = {r["package"] for r in result}
self.assertEqual(packages, {"udev", "firefox"})

def test_skips_invalid_json(self) -> None:
from tools.emit_manifest import collect_source_reports
with tempfile.TemporaryDirectory() as tmpdir:
report_dir = Path(tmpdir)
(report_dir / "bad.json").write_text("not json{")
(report_dir / "good.json").write_text(json.dumps({
"package": "good", "sha512": "abc", "resolved_url": "https://example.com/good.tar.gz",
}))
result = collect_source_reports(report_dir)
self.assertEqual(len(result), 1)
self.assertEqual(result[0]["package"], "good")


class CollectSignatureReportTests(unittest.TestCase):
def test_returns_none_when_report_missing(self) -> None:
from tools.emit_manifest import collect_signature_report
with tempfile.TemporaryDirectory() as tmpdir:
result = collect_signature_report(Path(tmpdir))
self.assertIsNone(result)

def test_returns_report_when_present(self) -> None:
from tools.emit_manifest import collect_signature_report
with tempfile.TemporaryDirectory() as tmpdir:
report_dir = Path(tmpdir)
data = {"summary": "all verified", "packages": {}}
(report_dir / "signature-report.json").write_text(json.dumps(data))
result = collect_signature_report(report_dir)
self.assertEqual(result, data)


class ManifestStructureTests(unittest.TestCase):
def test_manifest_includes_all_sections(self) -> None:
from tools.emit_manifest import main
with tempfile.TemporaryDirectory() as tmpdir:
root = Path(tmpdir)
config = root / "config"
config.mkdir()
(config / "buildroot-lock.json").write_text(json.dumps({
"schema": 1,
"image": {"pinned": "sha256:aaa"},
"release": "44",
}))
report_dir = root / "reports"
report_dir.mkdir()
(report_dir / "udev.json").write_text(json.dumps({
"package": "udev", "sha512": "abc", "resolved_url": "https://example.com/udev.tar.gz",
"signature_verified": {"verified": True},
"sha256_url": "https://example.com/SHA256",
}))
(report_dir / "buildroot-lock-preflight.json").write_text(
json.dumps({"image_ref": "img1", "package_count": 10, "nevras": ["glibc-2.42-1.fc44.x86_64"]})
)
(report_dir / "signature-report.json").write_text(json.dumps({
"summary": "verified", "packages": {}
}))
package_dir = root / "rpms"
package_dir.mkdir()
output = root / "manifest.json"
with patch("sys.argv", [
"emit_manifest.py",
"--buildroot-lock", str(config / "buildroot-lock.json"),
"--package-dir", str(package_dir),
"--report-dir", str(report_dir),
"--output", str(output),
"--oci-digest", "sha256:digest123",
]):
with patch("tools.emit_manifest.subprocess.check_output", return_value="udev-260-1.fc44.x86_64"):
with patch("tools.emit_manifest.sha256_file", return_value="deadbeef"):
with patch.object(Path, "stat", lambda self: type("MockStat", (), {"st_size": 1000})()):
ret = main()
self.assertEqual(ret, 0)
manifest = json.loads(output.read_text())
self.assertEqual(manifest["schema"], 1)
self.assertEqual(manifest["oci"]["digest"], "sha256:digest123")
self.assertEqual(manifest["buildroot"]["release"], "44")
self.assertIn("preflight", manifest["buildroot"]["neura_snapshots"])
self.assertIn("udev", manifest["sources"])
self.assertEqual(manifest["signature_provenance"]["summary"], "verified")
self.assertEqual(manifest["packages"]["total"], 0)

def test_manifest_handles_missing_buildroot_lock(self) -> None:
from tools.emit_manifest import main
with tempfile.TemporaryDirectory() as tmpdir:
root = Path(tmpdir)
package_dir = root / "rpms"
package_dir.mkdir()
report_dir = root / "reports"
report_dir.mkdir()
output = root / "manifest.json"
with patch("sys.argv", [
"emit_manifest.py",
"--buildroot-lock", str(root / "nonexistent-lock.json"),
"--package-dir", str(package_dir),
"--report-dir", str(report_dir),
"--output", str(output),
]):
ret = main()
self.assertEqual(ret, 0)
manifest = json.loads(output.read_text())
self.assertEqual(manifest["buildroot"]["image"], {})


if __name__ == "__main__":
unittest.main()
93 changes: 93 additions & 0 deletions tests/test_lock_buildroot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
#!/usr/bin/env python3

import json
import tempfile
import unittest
from pathlib import Path
from unittest.mock import patch

from tools.lock_buildroot import split_pinned, load_lock, _pick_digest


class LockBuildrootTests(unittest.TestCase):
def test_pick_digest_prefers_non_fedora_registry(self) -> None:
digests = [
"registry.fedoraproject.org/fedora@sha256:aaa",
"quay.io/fedora/fedora@sha256:bbb",
]
self.assertEqual(_pick_digest(digests), "quay.io/fedora/fedora@sha256:bbb")

def test_pick_digest_falls_back_to_first_when_all_fedora(self) -> None:
digests = [
"registry.fedoraproject.org/fedora@sha256:aaa",
]
self.assertEqual(_pick_digest(digests), "registry.fedoraproject.org/fedora@sha256:aaa")

def test_split_pinned_returns_reference_without_digest(self) -> None:
pinned = "quay.io/fedora/fedora:44@sha256:" + "0" * 64
result = split_pinned(pinned)
self.assertEqual(result, "quay.io/fedora/fedora:44")

def test_split_pinned_rejects_unpinned(self) -> None:
with self.assertRaises(ValueError):
split_pinned("quay.io/fedora/fedora:44")

def test_load_lock_rejects_missing_file(self) -> None:
with tempfile.TemporaryDirectory() as directory:
with self.assertRaises(FileNotFoundError):
load_lock(Path(directory) / "nonexistent.json")

def test_load_lock_rejects_missing_pinned(self) -> None:
with tempfile.TemporaryDirectory() as directory:
lock = Path(directory) / "lock.json"
lock.write_text(json.dumps({"schema": 1, "image": {}}))
with self.assertRaisesRegex(ValueError, "image.pinned"):
load_lock(lock)

def test_load_lock_validates_schema(self) -> None:
with tempfile.TemporaryDirectory() as directory:
lock = Path(directory) / "lock.json"
lock.write_text(json.dumps({"schema": 2, "image": {"pinned": "x"}}))
with self.assertRaisesRegex(ValueError, "schema"):
load_lock(lock)

def test_load_lock_returns_valid_document(self) -> None:
with tempfile.TemporaryDirectory() as directory:
lock = Path(directory) / "lock.json"
lock.write_text(json.dumps({
"schema": 1,
"image": {"reference": "quay.io/fedora/fedora:44", "pinned": "quay.io/fedora/fedora:44@sha256:abc"},
"release": "44",
}))
data = load_lock(lock)
self.assertEqual(data["image"]["pinned"], "quay.io/fedora/fedora:44@sha256:abc")
self.assertEqual(data["release"], "44")


class ResolveTests(unittest.TestCase):
@patch("tools.lock_buildroot.subprocess.run")
def test_resolve_pulls_when_image_not_present(self, mock_run: unittest.mock.MagicMock) -> None:
mock_run.side_effect = [
unittest.mock.Mock(returncode=1, stdout="", stderr=""), # inspect fails
unittest.mock.Mock(returncode=0, stdout=""), # pull succeeds
unittest.mock.Mock(returncode=0, stdout=json.dumps([ # second inspect
{"RepoDigests": ["registry.fedoraproject.org/fedora@sha256:aaa",
"quay.io/fedora/fedora@sha256:bbb"]}
])),
]
from tools.lock_buildroot import resolve
result = resolve("quay.io/fedora/fedora:44")
self.assertEqual(result, "quay.io/fedora/fedora:44@sha256:bbb")

@patch("tools.lock_buildroot.subprocess.run")
def test_resolve_uses_tag_from_input(self, mock_run: unittest.mock.MagicMock) -> None:
mock_run.return_value = unittest.mock.Mock(returncode=0, stdout=json.dumps([
{"RepoDigests": ["quay.io/fedora/fedora@sha256:bbb"]}
]))
from tools.lock_buildroot import resolve
result = resolve("quay.io/fedora/fedora:44")
self.assertTrue(result.startswith("quay.io/fedora/fedora:44@sha256:"))


if __name__ == "__main__":
unittest.main()
Loading