#!/usr/bin/env python3 """Check review cleanup scope without Docker or build output.""" import json import os from pathlib import Path import subprocess import tempfile import unittest SCRIPT = Path(__file__).with_name('cleanup-review-images.sh') SHA = 'a' * 40 FAKE_DOCKER = '''#!/usr/bin/env python3 import json, os, sys args=sys.argv[1:] with open(os.environ['DOCKER_CALLS'], 'a') as output: output.write(json.dumps(args)+'\\n') if args[0]=='ps': if os.environ.get('FAIL_INVENTORY'): sys.exit(1) if os.environ.get('REFERENCED'): print('container-id') elif args[0]=='inspect': print('sha256:review-image') elif args[:2]==['image','inspect']: if os.environ.get('MISSING_IMAGE'): sys.exit(1) print('sha256:review-image') ''' class ReviewCleanupTests(unittest.TestCase): def run_cleanup(self, sha=SHA, **extra): with tempfile.TemporaryDirectory() as directory: root = Path(directory) docker = root / 'docker' docker.write_text(FAKE_DOCKER) docker.chmod(0o700) calls = root / 'calls' result = subprocess.run(['bash', str(SCRIPT)], capture_output=True, env={ **os.environ, 'PATH': directory + os.pathsep + os.environ['PATH'], 'DOCKER_CALLS': str(calls), 'REVIEW_SHA': sha, **extra}) return result.returncode, [json.loads(line) for line in calls.read_text().splitlines()] if calls.exists() else [] def test_removes_only_exact_review_tags_without_force_and_keeps_parent_cache(self): code, calls = self.run_cleanup() self.assertEqual(code, 0) self.assertEqual([args for args in calls if args[:2] == ['image', 'rm']], [ ['image', 'rm', '--no-prune', name + ':' + SHA] for name in ['creative-studio-review', 'creative-wiki-tests', 'creative-wiki-review']]) self.assertFalse(any('-f' in args or 'prune' in args or 'volume' in args for args in calls)) def test_invalid_sha_never_contacts_docker(self): for value in ['', 'main', 'a' * 39, 'a' * 41, 'A' * 40, '*' * 40]: with self.subTest(value=value): code, calls = self.run_cleanup(value) self.assertNotEqual(code, 0) self.assertEqual(calls, []) def test_container_referenced_images_are_kept(self): code, calls = self.run_cleanup(REFERENCED='1') self.assertEqual(code, 0) self.assertFalse(any(args[:2] == ['image', 'rm'] for args in calls)) def test_missing_tags_are_a_successful_noop(self): code, calls = self.run_cleanup(MISSING_IMAGE='1') self.assertEqual(code, 0) self.assertFalse(any(args[:2] == ['image', 'rm'] for args in calls)) def test_inventory_failure_prevents_removal(self): code, calls = self.run_cleanup(FAIL_INVENTORY='1') self.assertNotEqual(code, 0) self.assertEqual(len(calls), 1) if __name__ == '__main__': unittest.main()