# SPDX-License-Identifier: CC0-1.0 # Copyright (C) 2022 Wojtek Kosior # # Available under the terms of Creative Commons Zero v1.0 Universal. import pytest import pathlib import re import dataclasses as dc from immutables import Map from hydrilla import item_infos, versions, json_instances from hydrilla.exceptions import HaketiloException def test_make_item_refs_seq_empty(): """....""" assert item_infos.make_item_refs_seq([]) == () def test_get_item_refs_seq_nonempty(): """....""" ref_objs = [{'identifier': 'abc'}, {'identifier': 'def'}] result = item_infos.make_item_refs_seq(ref_objs) assert type(result) is tuple assert [ref.identifier for ref in result] == ['abc', 'def'] @pytest.fixture def mock_make_item_refs_seq(monkeypatch): """....""" def mocked_make_item_refs_seq(ref_objs): """....""" assert ref_objs == getattr( mocked_make_item_refs_seq, 'expected', [{'identifier': 'abc'}, {'identifier': 'def'}] ) return (item_infos.ItemRef('abc'), item_infos.ItemRef('def')) monkeypatch.setattr(item_infos, 'make_item_refs_seq', mocked_make_item_refs_seq) return mocked_make_item_refs_seq def test_make_required_mappings_compat_too_low(): """....""" assert item_infos.make_required_mappings('whatever', 1) == () @pytest.mark.usefixtures('mock_make_item_refs_seq') def test_make_required_mappings_compat_ok(): """....""" ref_objs = [{'identifier': 'abc'}, {'identifier': 'def'}] assert item_infos.make_required_mappings(ref_objs, 2) == \ (item_infos.ItemRef('abc'), item_infos.ItemRef('def')) def test_make_file_refs_seq_empty(): """....""" assert item_infos.make_file_refs_seq([]) == () def test_make_file_refs_seq_nonempty(): """....""" ref_objs = [{'file': 'abc', 'sha256': 'dummy_hash1'}, {'file': 'def', 'sha256': 'dummy_hash2'}] result = item_infos.make_file_refs_seq(ref_objs) assert type(result) is tuple assert [ref.name for ref in result] == ['abc', 'def'] assert [ref.sha256 for ref in result] == ['dummy_hash1', 'dummy_hash2'] def test_generated_by_make_empty(): """....""" assert item_infos.GeneratedBy.make(None) == None @pytest.mark.parametrize('_in, out_version', [ ({'name': 'abc'}, None), ({'name': 'abc', 'version': '1.1.1'}, '1.1.1') ]) def test_generated_by_make_nonempty(_in, out_version): """....""" generated_by = item_infos.GeneratedBy.make(_in) assert generated_by.name == 'abc' assert generated_by.version == out_version def test_load_item_info(monkeypatch): """....""" def mocked_read_instance(instance_or_path): """....""" assert instance_or_path == 'dummy_path' return 'dummy_instance' monkeypatch.setattr(json_instances, 'read_instance', mocked_read_instance) def mocked_validate_instance(instance, schema_fmt): """....""" assert instance == 'dummy_instance' assert schema_fmt == 'api_exotictype_description-{}.schema.json' return 7 monkeypatch.setattr(json_instances, 'validate_instance', mocked_validate_instance) class MockedLoadedType: """....""" def make(instance, schema_compat, repository): """....""" assert instance == 'dummy_instance' assert schema_compat == 7 assert repository == 'somerepo' return 'dummy_item_info' type_name = 'exotictype' assert item_infos._load_item_info( MockedLoadedType, 'dummy_path', 'somerepo' ) == 'dummy_item_info' def test_make_payloads(monkeypatch): """....""" payloads_obj = {'http*://example.com/': {'identifier': 'someresource'}} def mocked_parse_pattern(pattern): """....""" assert pattern == 'http*://example.com/' yield 'dummy_parsed_pattern_1' yield 'dummy_parsed_pattern_2' monkeypatch.setattr(item_infos, 'parse_pattern', mocked_parse_pattern) assert item_infos.make_payloads(payloads_obj) == Map({ 'dummy_parsed_pattern_1': item_infos.ItemRef('someresource'), 'dummy_parsed_pattern_2': item_infos.ItemRef('someresource') }) @pytest.mark.parametrize('info_mod, in_mod', [ ({}, {}), ({'uuid': 'dummy_uuid'}, {}), ({}, {'uuid': 'dummy_uuid'}), ({'uuid': 'dummy_uuid'}, {'uuid': 'dummy_uuid'}), ({}, {'identifier': 'abc', '_initialized': True}), ({}, {'_by_version': Map({(1, 2): 'dummy_old_info'})}) ]) def test_versioned_item_info_register(info_mod, in_mod): """....""" class DummyInfo: """....""" uuid = None identifier = 'abc' version = (1, 2) for name, value in info_mod.items(): setattr(DummyInfo, name, value) in_fields = { 'uuid': None, 'identifier': '', '_by_version': Map(), '_initialized': False, **in_mod } out_fields = { 'uuid': DummyInfo.uuid or in_mod.get('uuid'), 'identifier': DummyInfo.identifier, '_by_version': Map({(1, 2): DummyInfo}), '_initialized': True } versioned = item_infos.VersionedItemInfo(**in_fields) new_versioned = versioned.register(DummyInfo) assert dc.asdict(versioned) == in_fields assert dc.asdict(new_versioned) == out_fields def test_versioned_item_info_register_bad_uuid(): """....""" versioned = item_infos.VersionedItemInfo( identifier='abc', uuid='old_uuid' ) class DummyInfo: """....""" uuid = 'new_uuid' identifier = 'abc' version = (1, 2) with pytest.raises(HaketiloException, match='^uuid_mismatch_abc$'): versioned.register(DummyInfo) @pytest.mark.parametrize('previous_registrations', [ Map(), Map({(1, 2): 'dummy_info'}) ]) def test_versioned_item_info_unregister(previous_registrations): """....""" versioned = item_infos.VersionedItemInfo( identifier = 'abc', _by_version = previous_registrations ) assert versioned.unregister((1, 2)) == \ dc.replace(versioned, _by_version=Map()) @pytest.mark.parametrize('registrations, out', [ (Map(), True), (Map({(1, 2): 'dummy_info'}), False) ]) def test_versioned_item_info_is_empty(registrations, out): """....""" versioned = item_infos.VersionedItemInfo( identifier = 'abc', _by_version = registrations ) assert versioned.is_empty() == out @pytest.mark.parametrize('versions, out', [ ([(1, 2), (1, 2, 1), (0, 9999, 4), (1, 0, 2)], (1, 2, 1)), ([(1, 2)], (1, 2)) ]) def test_versioned_item_info_newest_version(versions, out): """....""" versioned = item_infos.VersionedItemInfo( identifier = 'abc', _by_version = Map((ver, 'dummy_info') for ver in versions) ) assert versioned.newest_version() == out def test_versioned_item_info_newest_version_bad(monkeypatch): """....""" monkeypatch.setattr(item_infos.VersionedItemInfo, 'newest_version', lambda self: 'dummy_ver1') versioned = item_infos.VersionedItemInfo( identifier = 'abc', _by_version = Map(dummy_ver1='dummy_info1', dummy_ver2='dummy_info2') ) assert versioned.get_newest() == 'dummy_info1' def test_versioned_item_info_get_by_ver(): """....""" versioned = item_infos.VersionedItemInfo( identifier = 'abc', _by_version = Map({(1, 2): 'dummy_info1', (3, 4, 5): 'dummy_info2'}) ) assert versioned.get_by_ver(range(1, 3)) == 'dummy_info1' @pytest.mark.parametrize('versions, out', [ ([(1, 2), (0, 999, 4), (1, 0, 2)], ['(0, 999, 4)', '(1, 0, 2)', '(1, 2)']), ([], []) ]) def test_versioned_item_get_all(versions, out): """....""" versioned = item_infos.VersionedItemInfo( identifier = 'abc', _by_version = Map((ver, str(ver)) for ver in versions) ) assert [*versioned.get_all()] == out sample_resource_obj = { 'source_name': 'somesource', 'source_copyright': [{'file': 'ABC', 'sha256': 'dummy_sha256'}], 'version': [1, 2, 3, 0], 'identifier': 'someid', 'uuid': None, 'long_name': 'Some Thing', 'required_mappings': [{'identifier': 'required1'}], 'generated_by': {'name': 'sometool', 'version': '1.1.1'}, 'revision': 4, 'dependencies': [{'identifier': 'abc'}, {'identifier': 'def'}], 'scripts': [{'file': 'ABC', 'sha256': 'dummy_sha256'}] } sample_mapping_obj = { **sample_resource_obj, 'payloads': { 'https://example.com/': {'identifier': 'someresource'} } } del sample_mapping_obj['dependencies'] del sample_mapping_obj['scripts'] @pytest.fixture(scope='session') def sample_resource_info(): """....""" return item_infos.ResourceInfo( repository = 'somerepo', source_name = 'somesource', source_copyright = (item_infos.FileRef('ABC', 'dummy_sha256'),), version = (1, 2, 3), identifier = 'someid', uuid = None, long_name = 'Some Thing', required_mappings = (item_infos.ItemRef('required1'),), generated_by = item_infos.GeneratedBy('sometool', '1.1.1'), revision = 4, dependencies = (item_infos.ItemRef('abc'), item_infos.ItemRef('def')), scripts = (item_infos.FileRef('ABC', 'dummy_sha256'),) ) @pytest.fixture(scope='session') def sample_mapping_info(): """....""" payloads = Map({'https://example.com/': item_infos.ItemRef('someresource')}) return item_infos.MappingInfo( repository = 'somerepo', source_name = 'somesource', source_copyright = (item_infos.FileRef('ABC', 'dummy_sha256'),), version = (1, 2, 3), identifier = 'someid', uuid = None, long_name = 'Some Thing', required_mappings = (item_infos.ItemRef('required1'),), generated_by = item_infos.GeneratedBy('sometool', '1.1.1'), payloads = payloads ) @pytest.fixture(scope='session') def sample_info_base_init_kwargs(sample_resource_info): kwargs = {} for field_name in item_infos.ItemInfoBase.__annotations__.keys(): kwargs[field_name] = getattr(sample_resource_info, field_name) return Map(kwargs) @pytest.fixture def mock_version_string(monkeypatch): """....""" def mocked_version_string(version, revision=None): """....""" assert version == (1, 2, 3) assert revision in (None, 4) return '1.2.3' if revision is None else '1.2.3-4' monkeypatch.setattr(versions, 'version_string', mocked_version_string) @pytest.mark.usefixtures('mock_version_string') def test_item_info_path(sample_resource_info): """....""" assert sample_resource_info.path_relative_to_type() == 'someid/1.2.3' assert sample_resource_info.path() == 'resource/someid/1.2.3' @pytest.mark.usefixtures('mock_version_string') def test_resource_info_versioned_identifier(sample_resource_info, monkeypatch): """....""" monkeypatch.setattr(item_infos.ItemInfoBase, 'versioned_identifier', lambda self: '') assert sample_resource_info.versioned_identifier == '-4' @pytest.mark.usefixtures('mock_version_string') def test_mapping_info_versioned_identifier(sample_mapping_info): """....""" assert sample_mapping_info.versioned_identifier == 'someid-1.2.3' @pytest.fixture def mock_make_file_refs_seq(monkeypatch): """....""" def mocked_make_file_refs_seq(ref_objs): """....""" assert ref_objs == getattr( mocked_make_file_refs_seq, 'expected', [{'file': 'ABC', 'sha256': 'dummy_sha256'}] ) return (item_infos.FileRef(name='ABC', sha256='dummy_sha256'),) monkeypatch.setattr(item_infos, 'make_file_refs_seq', mocked_make_file_refs_seq) return mocked_make_file_refs_seq @pytest.mark.parametrize('missing_prop', [ 'required_mappings', 'generated_by', 'uuid' ]) @pytest.mark.usefixtures('mock_make_item_refs_seq', 'mock_make_file_refs_seq') def test_item_info_get_base_init_kwargs( missing_prop, monkeypatch, sample_resource_info, sample_info_base_init_kwargs, mock_make_file_refs_seq ): """....""" monkeypatch.delitem(sample_resource_obj, missing_prop) def mocked_normalize_version(version): """....""" assert version == [1, 2, 3, 0] return (1, 2, 3) monkeypatch.setattr(versions, 'normalize_version', mocked_normalize_version) def mocked_make_required_mappings(ref_objs, schema_compat): """....""" if missing_prop == 'required_mappings': assert ref_objs == [] else: assert ref_objs == [{'identifier': 'required1'}] assert schema_compat == 2 return (item_infos.ItemRef('required1'),) monkeypatch.setattr(item_infos, 'make_required_mappings', mocked_make_required_mappings) def mocked_generated_by_make(generated_by_obj): """....""" if missing_prop == 'generated_by': assert generated_by_obj == None else: assert generated_by_obj == {'name': 'sometool', 'version': '1.1.1'} return item_infos.GeneratedBy(name='sometool', version='1.1.1') monkeypatch.setattr(item_infos.GeneratedBy, 'make', mocked_generated_by_make) expected = sample_info_base_init_kwargs if missing_prop == 'uuid': expected = expected.set('uuid', None) Base = item_infos.ItemInfoBase assert Base._get_base_init_kwargs(sample_resource_obj, 2, 'somerepo') == \ expected @pytest.fixture def mock_get_base_init_kwargs(monkeypatch, sample_info_base_init_kwargs): """....""" def mocked_get_base_init_kwargs(item_obj, schema_compat, repository): """....""" assert schema_compat == 2 assert item_obj['identifier'] == 'someid' assert repository == 'somerepo' return sample_info_base_init_kwargs monkeypatch.setattr(item_infos.ItemInfoBase, '_get_base_init_kwargs', mocked_get_base_init_kwargs) @pytest.mark.parametrize('missing_prop', ['dependencies', 'scripts']) @pytest.mark.usefixtures('mock_get_base_init_kwargs') def test_resource_info_make( missing_prop, monkeypatch, sample_resource_info, mock_make_item_refs_seq, mock_make_file_refs_seq ): """....""" _in = sample_resource_obj monkeypatch.delitem(_in, missing_prop) if missing_prop == 'dependencies': mock_make_item_refs_seq.expected = [] elif missing_prop == 'scripts': mock_make_file_refs_seq.expected = [] assert item_infos.ResourceInfo.make(_in, 2, 'somerepo') == \ sample_resource_info @pytest.mark.parametrize('missing_payloads', [True, False]) @pytest.mark.usefixtures('mock_get_base_init_kwargs', 'mock_make_item_refs_seq') def test_mapping_info_make(missing_payloads, monkeypatch, sample_mapping_info): """....""" _in = sample_mapping_obj if missing_payloads: monkeypatch.delitem(_in, 'payloads') def mocked_make_payloads(payloads_obj): """....""" if missing_payloads: assert payloads_obj == {} else: assert payloads_obj == \ {'https://example.com/': {'identifier': 'someresource'}} return Map({'https://example.com/': item_infos.ItemRef('someresource')}) monkeypatch.setattr(item_infos, 'make_payloads', mocked_make_payloads) assert item_infos.MappingInfo.make(_in, 2, 'somerepo') == \ sample_mapping_info @pytest.mark.parametrize('type_name', ['ResourceInfo', 'MappingInfo']) def test_make_item_info(type_name, monkeypatch): """....""" info_type = getattr(item_infos, type_name) def mocked_load_item_info(_info_type, instance_or_path, repository): """....""" assert _info_type == info_type assert instance_or_path == 'dummy_path' assert repository == 'somerepo' return 'dummy_info' monkeypatch.setattr(item_infos, '_load_item_info', mocked_load_item_info) assert info_type.load('dummy_path', 'somerepo') == 'dummy_info' def test_resource_info_hash(sample_resource_info): """....""" hash(sample_resource_info) def test_mapping_info_hash(sample_mapping_info): """....""" hash(sample_mapping_info)