"""Exercise the exact image update helper used by the publishing workflow.""" import importlib.util import tempfile import unittest from pathlib import Path SCRIPT = Path(__file__).resolve().parents[1] / "scripts" / "update_gitops_image.py" SPEC = importlib.util.spec_from_file_location("update_gitops_image", SCRIPT) assert SPEC is not None and SPEC.loader is not None MODULE = importlib.util.module_from_spec(SPEC) SPEC.loader.exec_module(MODULE) REPOSITORY = "tea.ahmza.com/ahmza/blog-ahmza-com" IMAGE = REPOSITORY + "@sha256:" + "a" * 64 class UpdateImageTests(unittest.TestCase): def setUp(self): self.directory = tempfile.TemporaryDirectory() self.addCleanup(self.directory.cleanup) self.manifest = Path(self.directory.name) / "deployment.yaml" def test_replaces_only_blog_image_and_preserves_other_fields(self): original = ( "spec:\n" " containers:\n" " - name: blog\n" f" image: {REPOSITORY}:latest\n" " - name: other\n" " image: example.org/other:latest\n" ) self.manifest.write_text(original) self.assertTrue(MODULE.update_image(self.manifest, IMAGE)) self.assertEqual( self.manifest.read_text(), original.replace(REPOSITORY + ":latest", IMAGE) ) def test_existing_digest_update_preserves_comment(self): self.manifest.write_text( " image: " + REPOSITORY + "@sha256:" + "b" * 64 + " # keep\n" ) self.assertTrue(MODULE.update_image(self.manifest, IMAGE)) self.assertEqual(self.manifest.read_text(), " image: " + IMAGE + " # keep\n") def test_same_digest_is_noop(self): text = " image: " + IMAGE + "\n" self.manifest.write_text(text) self.assertFalse(MODULE.update_image(self.manifest, IMAGE)) self.assertEqual(self.manifest.read_text(), text) def test_missing_or_duplicate_image_fails_without_modification(self): for text in ["spec: {}\n", (" image: " + IMAGE + "\n") * 2]: with self.subTest(text=text): self.manifest.write_text(text) with self.assertRaises(ValueError): MODULE.update_image(self.manifest, IMAGE) self.assertEqual(self.manifest.read_text(), text) def test_rejects_unexpected_or_mutable_image_input(self): text = " image: " + REPOSITORY + ":latest\n" self.manifest.write_text(text) for image in [ REPOSITORY + ":latest", REPOSITORY + "@sha256:short", "example.org/untrusted@sha256:" + "a" * 64, IMAGE + "\nimage: injected", REPOSITORY + "@sha256:" + "A" * 64, ]: with self.subTest(image=image): with self.assertRaises(ValueError): MODULE.update_image(self.manifest, image) self.assertEqual(self.manifest.read_text(), text) def test_refuses_partial_repository_match(self): text = " image: " + REPOSITORY + "-other:latest\n" self.manifest.write_text(text) with self.assertRaises(ValueError): MODULE.update_image(self.manifest, IMAGE) self.assertEqual(self.manifest.read_text(), text) if __name__ == "__main__": unittest.main()