Files
ProxMenux/oci/tests/test_update_metadata.py
T

74 lines
4.1 KiB
Python

"""Native image checks reuse immutable metadata only after a fresh digest lookup."""
import json
from pathlib import Path
import sys
import unittest
from unittest.mock import patch
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / 'remote'))
import oci_installation_state as state
def digest(value): return 'sha256:' + state.sha(value)
class MetadataTests(unittest.TestCase):
def setUp(self):
self.platform = json.dumps({'schemaVersion': 2, 'config': {'digest': 'sha256:' + 'c' * 64},
'layers': [{'digest': 'sha256:' + 'd' * 64, 'size': 15}]}).encode()
self.index = json.dumps({'schemaVersion': 2, 'manifests': [
{'digest': digest(self.platform), 'platform': {'architecture': 'amd64', 'os': 'linux'}}]}).encode()
self.config = json.dumps({'architecture': 'amd64', 'os': 'linux', 'created': '2026-10-18T00:00:00Z',
'config': {'Labels': {'org.opencontainers.image.version': '2.0'}, 'Env': []}}).encode()
self.known = {'manifest_digest': digest(self.platform), 'architecture': 'amd64',
'version': '1.0', 'created': '2026-10-01T00:00:00Z'}
def test_unchanged_multiarch_image_requires_only_a_fresh_index_lookup(self):
with patch.object(state, 'command', return_value=self.index) as command:
candidate = state.resolve_candidate_metadata('ghcr.io/example/private:latest', 'amd64', self.known)
command.assert_called_once_with('skopeo', 'inspect', '--raw', 'docker://ghcr.io/example/private:latest')
self.assertEqual(candidate['manifest_digest'], self.known['manifest_digest'])
self.assertEqual(candidate['version'], '1.0')
self.assertEqual(candidate['registry_digest'], digest(self.index))
def test_unchanged_singlearch_image_requires_only_a_fresh_manifest_lookup(self):
with patch.object(state, 'command', return_value=self.platform) as command:
candidate = state.resolve_candidate_metadata('example/app:latest', 'amd64', self.known)
self.assertEqual(command.call_count, 1)
self.assertEqual(candidate['version'], '1.0')
def test_changed_digest_fetches_and_validates_new_metadata(self):
old = dict(self.known, manifest_digest='sha256:' + 'a' * 64)
with patch.object(state, 'command', side_effect=[self.index, self.platform, self.config]) as command:
candidate = state.resolve_candidate_metadata('example/app:latest', 'amd64', old)
self.assertEqual(command.call_count, 3)
self.assertEqual(candidate['version'], '2.0')
self.assertEqual(candidate['manifest_digest'], digest(self.platform))
def test_different_architecture_cannot_reuse_metadata(self):
known = dict(self.known, architecture='arm64')
with patch.object(state, 'command', side_effect=[self.index, self.platform, self.config]) as command:
candidate = state.resolve_candidate_metadata('example/app:latest', 'amd64', known)
self.assertEqual(command.call_count, 3)
self.assertEqual(candidate['version'], '2.0')
def test_install_and_update_still_fetch_complete_verified_contract(self):
with patch.object(state, 'command', side_effect=[self.index, self.platform, self.config]) as command:
candidate = state.resolve_candidate('example/app:latest', 'amd64')
self.assertEqual(command.call_count, 3)
self.assertEqual(candidate['layers'], ['sha256:' + 'd' * 64])
self.assertIn('Env', candidate['defaults'])
def test_changed_manifest_must_still_match_the_registry_digest(self):
with patch.object(state, 'command', side_effect=[self.index, b'{}']):
with self.assertRaises(ValueError):
state.resolve_candidate_metadata('example/app:latest', 'amd64', None)
def test_registry_failure_cannot_be_hidden_by_saved_metadata(self):
with patch.object(state, 'command', side_effect=RuntimeError('Registry unavailable')):
with self.assertRaises(RuntimeError):
state.resolve_candidate_metadata('example/app:latest', 'amd64', self.known)
if __name__ == '__main__': unittest.main()