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

1"""Edge case tests for zanj/loading.py to improve coverage.""" 

2 

3from __future__ import annotations 

4 

5import pytest 

6 

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) 

14 

15 

16class TestPopulateExternalsErrorChecking: 

17 """Tests for _populate_externals_error_checking function.""" 

18 

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) 

28 

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) 

34 

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) 

40 

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) 

46 

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) 

52 

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) 

58 

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 

64 

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 

70 

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 

79 

80 

81class TestLoaderHandlerFromFormattedClass: 

82 """Tests for LoaderHandler.from_formattedclass method.""" 

83 

84 def test_missing_serialize_method(self): 

85 """Line 137: Class missing serialize method should raise AssertionError.""" 

86 

87 class MissingSerialize: 

88 __muutils_format__ = "test" 

89 

90 @classmethod 

91 def load(cls, json_item, path=None, z=None): 

92 pass 

93 

94 with pytest.raises(AssertionError): 

95 LoaderHandler.from_formattedclass(MissingSerialize) 

96 

97 def test_missing_load_method(self): 

98 """Line 139: Class missing load method should raise AssertionError.""" 

99 

100 class MissingLoad: 

101 __muutils_format__ = "test" 

102 

103 def serialize(self): 

104 pass 

105 

106 with pytest.raises(AssertionError): 

107 LoaderHandler.from_formattedclass(MissingLoad) 

108 

109 def test_missing_format_key(self): 

110 """Line 141: Class missing __muutils_format__ should raise AssertionError.""" 

111 

112 class MissingFormat: 

113 def serialize(self): 

114 pass 

115 

116 @classmethod 

117 def load(cls, json_item, path=None, z=None): 

118 pass 

119 

120 with pytest.raises(AssertionError): 

121 LoaderHandler.from_formattedclass(MissingFormat) 

122 

123 def test_non_string_format_key(self): 

124 """Line 142: Class with non-string __muutils_format__ should raise AssertionError.""" 

125 

126 class NonStringFormat: 

127 __muutils_format__ = 12345 # should be string 

128 

129 def serialize(self): 

130 pass 

131 

132 @classmethod 

133 def load(cls, json_item, path=None, z=None): 

134 pass 

135 

136 with pytest.raises(AssertionError): 

137 LoaderHandler.from_formattedclass(NonStringFormat) 

138 

139 def test_valid_formatted_class(self): 

140 """Valid class should create a LoaderHandler successfully.""" 

141 

142 class ValidClass: 

143 __muutils_format__ = "test.ValidClass" 

144 

145 def serialize(self): 

146 return {} 

147 

148 @classmethod 

149 def load(cls, json_item, path=None, z=None): 

150 return cls() 

151 

152 handler = LoaderHandler.from_formattedclass(ValidClass) 

153 assert handler.uid == "test.ValidClass" 

154 assert "ValidClass" in handler.desc 

155 

156 

157class TestGetItemLoader: 

158 """Tests for get_item_loader function.""" 

159 

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",)) 

168 

169 

170class TestLoadItemRecursive: 

171 """Tests for load_item_recursive function.""" 

172 

173 def test_unloadable_type_strict_mode(self): 

174 """Line 395-398: Unloadable type with allow_not_loading=False should raise ValueError.""" 

175 

176 # Create a custom object that isn't handled by any loader 

177 class CustomUnloadable: 

178 pass 

179 

180 item = CustomUnloadable() 

181 with pytest.raises(ValueError, match="unknown type"): 

182 load_item_recursive(item, ("test",), None, allow_not_loading=False) 

183 

184 def test_unloadable_type_permissive_mode(self): 

185 """Line 395-396: Unloadable type with allow_not_loading=True should return as-is.""" 

186 

187 class CustomUnloadable: 

188 pass 

189 

190 item = CustomUnloadable() 

191 result = load_item_recursive(item, ("test",), None, allow_not_loading=True) 

192 assert result is item