|
2 | 2 | from policyengine_core.parameters import ParameterNode |
3 | 3 | from policyengine_core.parameters.operations import get_parameter |
4 | 4 | from policyengine_core.errors import ParameterPathError |
| 5 | +from policyengine_core.parameters.operations.get_parameter import ( |
| 6 | + _find_similar_parameters, |
| 7 | + _navigate_to_node, |
| 8 | + _access_bracket, |
| 9 | + _handle_bracket_access |
| 10 | +) |
5 | 11 |
|
6 | 12 |
|
7 | 13 | def test_parameter_not_found_message(): |
@@ -87,3 +93,188 @@ def test_bracket_on_non_bracket_parameter(): |
87 | 93 | assert "does not support bracket indexing" in str(excinfo.value) |
88 | 94 | assert excinfo.value.parameter_path == "tax.simple_rate[0]" |
89 | 95 | # Don't assert the exact failed_at value since it depends on implementation details |
| 96 | + |
| 97 | + |
| 98 | +def test_parse_path_component_with_invalid_bracket(): |
| 99 | + """Test error when parsing invalid bracket syntax in a path component.""" |
| 100 | + from policyengine_core.parameters.operations.get_parameter import _parse_path_component |
| 101 | + |
| 102 | + # Test parsing with invalid bracket syntax (missing closing bracket) |
| 103 | + with pytest.raises(ParameterPathError) as excinfo: |
| 104 | + _parse_path_component("brackets[0", "tax.brackets[0") |
| 105 | + |
| 106 | + assert "Invalid bracket syntax" in str(excinfo.value) |
| 107 | + assert "Use format: parameter_name[index]" in str(excinfo.value) |
| 108 | + assert excinfo.value.parameter_path == "tax.brackets[0" |
| 109 | + assert "brackets[0" in str(excinfo.value.failed_at) |
| 110 | + |
| 111 | + |
| 112 | +def test_parse_path_component_with_non_integer(): |
| 113 | + """Test error when parsing a non-integer bracket index.""" |
| 114 | + parameters = ParameterNode( |
| 115 | + data={ |
| 116 | + "tax": { |
| 117 | + "brackets": [ |
| 118 | + { |
| 119 | + "threshold": {"values": {"2022-01-01": 0}}, |
| 120 | + "rate": {"values": {"2022-01-01": 0.1}}, |
| 121 | + }, |
| 122 | + ], |
| 123 | + } |
| 124 | + } |
| 125 | + ) |
| 126 | + |
| 127 | + # Test with invalid bracket index |
| 128 | + with pytest.raises(ParameterPathError) as excinfo: |
| 129 | + get_parameter(parameters, "tax.brackets[abc]") |
| 130 | + |
| 131 | + # The error should be about invalid syntax rather than invalid index |
| 132 | + # This is because the parser detects a syntax issue before trying to convert to int |
| 133 | + assert "Invalid bracket syntax" in str(excinfo.value) |
| 134 | + assert excinfo.value.parameter_path == "tax.brackets[abc]" |
| 135 | + |
| 136 | + |
| 137 | +def test_parse_path_component_with_multiple_brackets(): |
| 138 | + """Test error when parsing a path component with multiple opening brackets.""" |
| 139 | + from policyengine_core.parameters.operations.get_parameter import _parse_path_component |
| 140 | + |
| 141 | + # Test parsing with multiple brackets |
| 142 | + with pytest.raises(ParameterPathError) as excinfo: |
| 143 | + _parse_path_component("brackets[0][1]", "tax.brackets[0][1]") |
| 144 | + |
| 145 | + assert "Invalid bracket syntax" in str(excinfo.value) |
| 146 | + assert "Use format: parameter_name[index]" in str(excinfo.value) |
| 147 | + assert excinfo.value.parameter_path == "tax.brackets[0][1]" |
| 148 | + assert excinfo.value.failed_at == "brackets[0][1]" |
| 149 | + |
| 150 | + |
| 151 | +def test_access_child_of_parameter(): |
| 152 | + """Test error when trying to access a child of a parameter (not a node).""" |
| 153 | + parameters = ParameterNode( |
| 154 | + data={ |
| 155 | + "tax": { |
| 156 | + "simple_rate": {"values": {"2022-01-01": 0.2}}, |
| 157 | + } |
| 158 | + } |
| 159 | + ) |
| 160 | + |
| 161 | + # Try to access a child of a parameter |
| 162 | + with pytest.raises(ParameterPathError) as excinfo: |
| 163 | + get_parameter(parameters, "tax.simple_rate.child") |
| 164 | + |
| 165 | + assert "Cannot access 'child'" in str(excinfo.value) |
| 166 | + assert "not a parameter node with children" in str(excinfo.value) |
| 167 | + assert excinfo.value.parameter_path == "tax.simple_rate.child" |
| 168 | + assert excinfo.value.failed_at == "child" |
| 169 | + |
| 170 | + |
| 171 | +def test_multiple_brackets_in_path_component(): |
| 172 | + """Test error handling with multiple brackets in a path component.""" |
| 173 | + parameters = ParameterNode( |
| 174 | + data={ |
| 175 | + "tax": { |
| 176 | + "brackets": [ |
| 177 | + { |
| 178 | + "threshold": {"values": {"2022-01-01": 0}}, |
| 179 | + "rate": {"values": {"2022-01-01": 0.1}}, |
| 180 | + }, |
| 181 | + ], |
| 182 | + } |
| 183 | + } |
| 184 | + ) |
| 185 | + |
| 186 | + # Test path with multiple bracket components |
| 187 | + with pytest.raises(ParameterPathError) as excinfo: |
| 188 | + get_parameter(parameters, "tax.brackets[0][1]") |
| 189 | + |
| 190 | + assert "Invalid bracket syntax" in str(excinfo.value) |
| 191 | + assert excinfo.value.parameter_path == "tax.brackets[0][1]" |
| 192 | + assert excinfo.value.failed_at == "brackets[0][1]" |
| 193 | + |
| 194 | + |
| 195 | +def test_find_similar_parameters(): |
| 196 | + """Test the _find_similar_parameters helper function.""" |
| 197 | + # Create a node with some children |
| 198 | + parameters = ParameterNode( |
| 199 | + data={ |
| 200 | + "tax": { |
| 201 | + "income_tax": {}, |
| 202 | + "property_tax": {}, |
| 203 | + "sales_tax": {}, |
| 204 | + "inheritance_tax": {}, |
| 205 | + } |
| 206 | + } |
| 207 | + ) |
| 208 | + |
| 209 | + # Get the "tax" node |
| 210 | + tax_node = parameters.children["tax"] |
| 211 | + |
| 212 | + # Test finding similar parameters |
| 213 | + similar = _find_similar_parameters(tax_node, "tax") |
| 214 | + assert "income_tax" in similar |
| 215 | + assert "property_tax" in similar |
| 216 | + assert "sales_tax" in similar |
| 217 | + assert "inheritance_tax" in similar |
| 218 | + |
| 219 | + # Test with case insensitivity |
| 220 | + similar = _find_similar_parameters(tax_node, "TAX") |
| 221 | + assert "income_tax" in similar |
| 222 | + assert "property_tax" in similar |
| 223 | + |
| 224 | + # Test with partial match |
| 225 | + similar = _find_similar_parameters(tax_node, "inc") |
| 226 | + assert "income_tax" in similar |
| 227 | + assert "inheritance_tax" not in similar |
| 228 | + |
| 229 | + # Test with no matches |
| 230 | + similar = _find_similar_parameters(tax_node, "xyz") |
| 231 | + assert len(similar) == 0 |
| 232 | + |
| 233 | + # Test with a non-node (no children attribute) |
| 234 | + parameter = get_parameter(parameters, "tax.income_tax") |
| 235 | + similar = _find_similar_parameters(parameter, "anything") |
| 236 | + assert len(similar) == 0 |
| 237 | + |
| 238 | + |
| 239 | +def test_handle_bracket_access_index_out_of_range(): |
| 240 | + """Test index out of range in _handle_bracket_access function.""" |
| 241 | + from policyengine_core.parameters.parameter_scale import ParameterScale |
| 242 | + import os |
| 243 | + |
| 244 | + # Create a scale with brackets |
| 245 | + scale = ParameterScale("test.brackets", { |
| 246 | + "brackets": [ |
| 247 | + {"threshold": {"values": {"2022-01-01": 0}}, "rate": {"values": {"2022-01-01": 0.1}}}, |
| 248 | + ] |
| 249 | + }, os.path.join(os.getcwd(), "test.yaml")) |
| 250 | + |
| 251 | + # Test accessing out-of-range bracket |
| 252 | + with pytest.raises(ParameterPathError) as excinfo: |
| 253 | + _handle_bracket_access(scale, "[5]", "test.brackets[5]") |
| 254 | + |
| 255 | + assert "Bracket index out of range" in str(excinfo.value) |
| 256 | + assert "Valid indices are 0 to 0" in str(excinfo.value) |
| 257 | + assert excinfo.value.parameter_path == "test.brackets[5]" |
| 258 | + assert excinfo.value.failed_at == "[5]" |
| 259 | + |
| 260 | + |
| 261 | +def test_access_bracket_index_out_of_range(): |
| 262 | + """Test index out of range in _access_bracket function.""" |
| 263 | + from policyengine_core.parameters.parameter_scale import ParameterScale |
| 264 | + import os |
| 265 | + |
| 266 | + # Create a scale with brackets |
| 267 | + scale = ParameterScale("test.brackets", { |
| 268 | + "brackets": [ |
| 269 | + {"threshold": {"values": {"2022-01-01": 0}}, "rate": {"values": {"2022-01-01": 0.1}}}, |
| 270 | + ] |
| 271 | + }, os.path.join(os.getcwd(), "test.yaml")) |
| 272 | + |
| 273 | + # Test accessing out-of-range bracket |
| 274 | + with pytest.raises(ParameterPathError) as excinfo: |
| 275 | + _access_bracket(scale, "brackets", 5, "brackets[5]", "test.brackets[5]") |
| 276 | + |
| 277 | + assert "Bracket index out of range" in str(excinfo.value) |
| 278 | + assert "Valid indices are 0 to 0" in str(excinfo.value) |
| 279 | + assert excinfo.value.parameter_path == "test.brackets[5]" |
| 280 | + assert excinfo.value.failed_at == "brackets[5]" |
0 commit comments