import hashlib import subprocess import sys import tarfile import tempfile import unittest from pathlib import Path from unittest.mock import patch from package.scripts import publish_latest_generic_package class PackagePublishingTests(unittest.TestCase): def test_configured_packager_matches_publisher(self): root = Path(__file__).resolve().parents[1] runtime = root / 'build/build_common/modulus' if not (runtime / 'modulus/cli.py').is_file(): self.skipTest('Configure first to check the exported Modulus packager') with patch.object(sys, 'path', [str(runtime), *sys.path]): import modulus.cli import modulus.config with tempfile.TemporaryDirectory() as temporary: directory = Path(temporary) context = modulus.cli.build_context(directory, modulus.config.load_repo_config(root)) name, registry, targets = publish_latest_generic_package.read_config(root / 'modulus.json') def fake_build(context, command, **kwargs): if command[:2] == ['cmake', '--install']: stage = Path(command[2]).parent / 'stage' (stage / 'manifest.json').write_text('{}\n') return 0 with patch.object(modulus.cli, 'in_docker', return_value=True), \ patch.object(modulus.cli, 'run', side_effect=fake_build), \ patch.object(modulus.cli, 'run_capture', return_value=subprocess.CompletedProcess([], 0, stdout='fixture\n', stderr='')), \ patch.object(modulus.cli, 'upload_generic_artifact') as upload: self.assertEqual(modulus.cli.package_artifacts(context, ['--no-remote']), 0) upload.assert_not_called() try: artifacts = publish_latest_generic_package.discover_artifacts(directory / 'build/packages', name, targets) except SystemExit as error: self.fail(str(error)) for archive, checksum in zip(artifacts[::2], artifacts[1::2]): self.assertEqual(checksum.read_text(), f'{hashlib.sha256(archive.read_bytes()).hexdigest()} {archive.name}\n') with tarfile.open(archive) as package: self.assertIn('manifest.json', package.getnames()) def test_only_configured_target_archives_and_checksums_are_uploaded(self): with tempfile.TemporaryDirectory() as temporary: root = Path(temporary) archive = root / 'product-vamp-arm_64_gcc12_release.tar.gz' checksum = archive.with_name(archive.name + '.sha256') for path in (archive, checksum, root / 'product-vamp-obsolete-arm_64_gcc12_release.tar.gz'): path.write_bytes(b'fixture') self.assertEqual(publish_latest_generic_package.discover_artifacts(root, 'product-vamp', ['arm_64_gcc12_release']), [archive, checksum]) checksum.unlink() with self.assertRaisesRegex(SystemExit, 'missing artifact files'): publish_latest_generic_package.discover_artifacts(root, 'product-vamp', ['arm_64_gcc12_release']) archive.unlink() with self.assertRaisesRegex(SystemExit, 'missing artifact files'): publish_latest_generic_package.discover_artifacts(root, 'product-vamp', ['arm_64_gcc12_release']) def test_mismatched_filenames_report_actual_artifacts_without_uploading(self): with tempfile.TemporaryDirectory() as temporary, patch.object(publish_latest_generic_package, 'request') as request: archive = Path(temporary) / 'product-vamp-abc123-arm_64_gcc12_release.tar.gz' archive.write_bytes(b'fixture') with self.assertRaises(SystemExit) as error: publish_latest_generic_package.main(['--dist-dir', temporary, '--user', 'user', '--token', 'test-token']) self.assertIn(f'Found: {archive.name}', str(error.exception)) self.assertIn('Publish the current Modulus tooling', str(error.exception)) request.assert_not_called() def test_missing_artifacts_do_not_touch_the_registry(self): with tempfile.TemporaryDirectory() as temporary, patch.object(publish_latest_generic_package, 'request') as request: with self.assertRaisesRegex(SystemExit, 'missing artifact files'): publish_latest_generic_package.main(['--dist-dir', temporary, '--user', 'user', '--token', 'test-token']) request.assert_not_called() def test_publication_replaces_the_single_registry_slot(self): archive = Path('product-vamp-arm_64_gcc12_release.tar.gz') with patch.object(publish_latest_generic_package, 'discover_artifacts', return_value=[archive]), \ patch.object(publish_latest_generic_package, 'list_versions', return_value=['latest', 'obsolete']), \ patch.object(publish_latest_generic_package, 'delete_version') as delete, \ patch.object(publish_latest_generic_package, 'upload_file') as upload: self.assertEqual(publish_latest_generic_package.main(['--user', 'user', '--token', 'test-token']), 0) self.assertEqual([call.args[-2] for call in delete.call_args_list], ['latest', 'obsolete']) self.assertEqual(upload.call_args.args[2], archive) url = publish_latest_generic_package.file_upload_url('https://example.test/generic', 'product-vamp', archive) self.assertEqual(url, 'https://example.test/generic/product-vamp/latest/product-vamp-arm_64_gcc12_release.tar.gz')