Coverage for tests / unit / no_torch / test_loading_edge_cases.py: 92%
105 statements
« prev ^ index » next coverage.py v7.13.4, created at 2026-02-22 02:02 -0700
« prev ^ index » next coverage.py v7.13.4, created at 2026-02-22 02:02 -0700
1"""Edge case tests for zanj/loading.py to improve coverage."""
3from __future__ import annotations
5import pytest
7from zanj.consts import _FORMAT_KEY
8from zanj.loading import (
9 LoaderHandler,
10 _populate_externals_error_checking,
11 get_item_loader,
12 load_item_recursive,
13)
16class TestPopulateExternalsErrorChecking:
17 """Tests for _populate_externals_error_checking function."""
19 def test_external_item_missing_data_field(self):
20 """Line 68: External item with format key but no data field should raise KeyError."""
21 malformed_item = {
22 _FORMAT_KEY: "list:external",
23 "some_field": "value",
24 # missing "data" field
25 }
26 with pytest.raises(KeyError, match="expected an external item"):
27 _populate_externals_error_checking("key", malformed_item)
29 def test_sequence_with_non_int_key(self):
30 """Line 75-76: Accessing a sequence with a non-int key should raise TypeError."""
31 sequence = [1, 2, 3]
32 with pytest.raises(TypeError, match="expected int"):
33 _populate_externals_error_checking("string_key", sequence)
35 def test_sequence_with_out_of_range_key(self):
36 """Line 77-78: Accessing a sequence with an out-of-range index should raise IndexError."""
37 sequence = [1, 2, 3]
38 with pytest.raises(IndexError, match="index out of range"):
39 _populate_externals_error_checking(100, sequence)
41 def test_mapping_with_non_str_key(self):
42 """Line 82-83: Accessing a mapping with a non-str key should raise TypeError."""
43 mapping = {"a": 1, "b": 2}
44 with pytest.raises(TypeError, match="expected str"):
45 _populate_externals_error_checking(123, mapping)
47 def test_mapping_with_missing_key(self):
48 """Line 84-85: Accessing a mapping with a missing key should raise KeyError."""
49 mapping = {"a": 1, "b": 2}
50 with pytest.raises(KeyError, match="key not in dict"):
51 _populate_externals_error_checking("missing_key", mapping)
53 def test_invalid_item_type(self):
54 """Line 88-89: Passing an invalid item type should raise TypeError."""
55 invalid_item = 42 # int is neither sequence nor mapping
56 with pytest.raises(TypeError, match="expected dict or list"):
57 _populate_externals_error_checking("key", invalid_item)
59 def test_valid_sequence_access(self):
60 """Valid sequence access should not raise and return False."""
61 sequence = [1, 2, 3]
62 result = _populate_externals_error_checking(1, sequence)
63 assert result is False
65 def test_valid_mapping_access(self):
66 """Valid mapping access should not raise and return False."""
67 mapping = {"a": 1, "b": 2}
68 result = _populate_externals_error_checking("a", mapping)
69 assert result is False
71 def test_external_item_with_data_returns_true(self):
72 """External item with data field should return True."""
73 external_item = {
74 _FORMAT_KEY: "list:external",
75 "data": [1, 2, 3],
76 }
77 result = _populate_externals_error_checking("key", external_item)
78 assert result is True
81class TestLoaderHandlerFromFormattedClass:
82 """Tests for LoaderHandler.from_formattedclass method."""
84 def test_missing_serialize_method(self):
85 """Line 137: Class missing serialize method should raise AssertionError."""
87 class MissingSerialize:
88 __muutils_format__ = "test"
90 @classmethod
91 def load(cls, json_item, path=None, z=None):
92 pass
94 with pytest.raises(AssertionError):
95 LoaderHandler.from_formattedclass(MissingSerialize)
97 def test_missing_load_method(self):
98 """Line 139: Class missing load method should raise AssertionError."""
100 class MissingLoad:
101 __muutils_format__ = "test"
103 def serialize(self):
104 pass
106 with pytest.raises(AssertionError):
107 LoaderHandler.from_formattedclass(MissingLoad)
109 def test_missing_format_key(self):
110 """Line 141: Class missing __muutils_format__ should raise AssertionError."""
112 class MissingFormat:
113 def serialize(self):
114 pass
116 @classmethod
117 def load(cls, json_item, path=None, z=None):
118 pass
120 with pytest.raises(AssertionError):
121 LoaderHandler.from_formattedclass(MissingFormat)
123 def test_non_string_format_key(self):
124 """Line 142: Class with non-string __muutils_format__ should raise AssertionError."""
126 class NonStringFormat:
127 __muutils_format__ = 12345 # should be string
129 def serialize(self):
130 pass
132 @classmethod
133 def load(cls, json_item, path=None, z=None):
134 pass
136 with pytest.raises(AssertionError):
137 LoaderHandler.from_formattedclass(NonStringFormat)
139 def test_valid_formatted_class(self):
140 """Valid class should create a LoaderHandler successfully."""
142 class ValidClass:
143 __muutils_format__ = "test.ValidClass"
145 def serialize(self):
146 return {}
148 @classmethod
149 def load(cls, json_item, path=None, z=None):
150 return cls()
152 handler = LoaderHandler.from_formattedclass(ValidClass)
153 assert handler.uid == "test.ValidClass"
154 assert "ValidClass" in handler.desc
157class TestGetItemLoader:
158 """Tests for get_item_loader function."""
160 def test_non_string_format_key(self):
161 """Line 306-309: Item with non-string __muutils_format__ should raise TypeError."""
162 malformed_item = {
163 _FORMAT_KEY: 12345, # should be str
164 "data": [1, 2, 3],
165 }
166 with pytest.raises(TypeError, match="invalid __muutils_format__ type"):
167 get_item_loader(malformed_item, ("test",))
170class TestLoadItemRecursive:
171 """Tests for load_item_recursive function."""
173 def test_unloadable_type_strict_mode(self):
174 """Line 395-398: Unloadable type with allow_not_loading=False should raise ValueError."""
176 # Create a custom object that isn't handled by any loader
177 class CustomUnloadable:
178 pass
180 item = CustomUnloadable()
181 with pytest.raises(ValueError, match="unknown type"):
182 load_item_recursive(item, ("test",), None, allow_not_loading=False)
184 def test_unloadable_type_permissive_mode(self):
185 """Line 395-396: Unloadable type with allow_not_loading=True should return as-is."""
187 class CustomUnloadable:
188 pass
190 item = CustomUnloadable()
191 result = load_item_recursive(item, ("test",), None, allow_not_loading=True)
192 assert result is item