mirror of
https://github.com/MacRimi/ProxMenux.git
synced 2026-10-08 22:46:41 +00:00
81 lines
3.9 KiB
Python
81 lines
3.9 KiB
Python
"""Regression tests for volume_preparations coverage across generated and
|
|
curated-profile catalog entries.
|
|
|
|
Covers the original OCI lost+found fix: a fresh ext4 managed volume carries
|
|
a lost+found directory, which stops images that take ownership of their own
|
|
data directories. volume_preparations (remove_lost_found +
|
|
only_when_mount_type: managed-volume) must be present for every persistent
|
|
mount, for both regular generated apps/*.json entries and curated-profile
|
|
entries (curated/*.json) — the curated-profile gap (Jellyfin Official first
|
|
surfaced it) is exactly what this suite guards against regressing.
|
|
"""
|
|
from pathlib import Path
|
|
import sys
|
|
import unittest
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
sys.path.insert(0, str(ROOT / "src"))
|
|
|
|
from proxmenux_oci.catalog import Catalog
|
|
|
|
|
|
class VolumePreparationsCoverageTests(unittest.TestCase):
|
|
def _preparations_by_path(self, template):
|
|
preparations = template["proxmox"]["installer_profile"].get("volume_preparations", [])
|
|
return {item["container_path"]: item for item in preparations}
|
|
|
|
def test_generated_app_volume_preparations_match_persistent_mounts(self):
|
|
"""Regular generated app (Uptime Kuma, apps/uptimekuma.json): every
|
|
declared volume has a matching volume_preparations entry, and every
|
|
entry is scoped to only_when_mount_type: managed-volume."""
|
|
template = Catalog(ROOT).compose("uptimekuma")
|
|
|
|
volume_paths = {v["container_path"] for v in template["container_contract"].get("volumes", [])}
|
|
preparations = self._preparations_by_path(template)
|
|
|
|
self.assertTrue(volume_paths, "fixture app must declare at least one volume")
|
|
self.assertEqual(set(preparations.keys()), volume_paths)
|
|
for path, entry in preparations.items():
|
|
self.assertTrue(entry.get("remove_lost_found"))
|
|
self.assertEqual(entry.get("only_when_mount_type"), "managed-volume")
|
|
|
|
def test_curated_profile_app_volume_preparations_match_persistent_mounts(self):
|
|
"""Curated-profile app (Jellyfin Official, curated/jellyfin-official.json,
|
|
3 mounts: /config, /cache, /media): same coverage requirement as
|
|
generated apps. This is the regression guard for the gap first found
|
|
via live testing — the original fix only touched apps/*.json and
|
|
missed curated/*.json entirely."""
|
|
template = Catalog(ROOT).compose("jellyfin-official")
|
|
|
|
volume_paths = {v["container_path"] for v in template["container_contract"].get("volumes", [])}
|
|
preparations = self._preparations_by_path(template)
|
|
|
|
self.assertEqual(volume_paths, {"/config", "/cache", "/media"})
|
|
self.assertEqual(set(preparations.keys()), volume_paths)
|
|
for path, entry in preparations.items():
|
|
self.assertTrue(entry.get("remove_lost_found"))
|
|
self.assertEqual(entry.get("only_when_mount_type"), "managed-volume")
|
|
|
|
|
|
def test_overlay_app_volume_preparations_match_the_overlay_remapped_mount(self):
|
|
"""PocketBase (apps/pocketbase.json + overlays/pocketbase.json): the
|
|
overlay remaps the container_path from /pb_data to
|
|
/pocketbase/pb_data. volume_preparations must follow the overlay's
|
|
remapped path, not the pre-overlay source path — this is the
|
|
regression guard for the overlay/mount desync first found via
|
|
read-only audit (2026-09-29)."""
|
|
template = Catalog(ROOT).compose("pocketbase")
|
|
|
|
volume_paths = {v["container_path"] for v in template["container_contract"].get("volumes", [])}
|
|
preparations = self._preparations_by_path(template)
|
|
|
|
self.assertEqual(volume_paths, {"/pocketbase/pb_data"})
|
|
self.assertEqual(set(preparations.keys()), volume_paths)
|
|
for path, entry in preparations.items():
|
|
self.assertTrue(entry.get("remove_lost_found"))
|
|
self.assertEqual(entry.get("only_when_mount_type"), "managed-volume")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|