|
9 | 9 | PlaceholderResolver, |
10 | 10 | replace_placeholders, |
11 | 11 | ) |
| 12 | +from docbuild.models.config.app import AppConfig |
| 13 | +from pydantic import ValidationError |
12 | 14 |
|
13 | 15 |
|
14 | 16 | def test_replace_placeholders(): |
@@ -267,15 +269,63 @@ def test_placeholder_wrong_type(): |
267 | 269 | config = 42 |
268 | 270 | assert replace_placeholders(config) == 42, 'Non-dict input should return unchanged' |
269 | 271 |
|
| 272 | +def test_appconfig_resolve_placeholders_non_dict_validator_runs(): |
| 273 | + """Ensure the AppConfig model validator runs for non-dict input. |
270 | 274 |
|
| 275 | + We exercise the validator branch that returns the input unchanged by |
| 276 | + calling the public `model_validate` API with a non-mapping value. The |
| 277 | + validation itself should raise a `ValidationError` for the wrong type, |
| 278 | + but the validator ran and the branch is covered without calling private |
| 279 | + methods directly. |
| 280 | + """ |
| 281 | + with pytest.raises(ValidationError): |
| 282 | + AppConfig.model_validate(123) |
| 283 | + |
| 284 | +# @pytest.mark.skip(reason="Private method test; not typically needed.") |
271 | 285 | def test_get_container_name_with_none_key(): |
272 | 286 | """Test _get_container_name when _current_key is None.""" |
273 | 287 | config = {'test': 'value'} |
274 | 288 | resolver = PlaceholderResolver(config) |
| 289 | + # This should return 'unknown' via the public accessor |
| 290 | + assert resolver.get_container_name() == 'unknown' |
| 291 | + |
275 | 292 |
|
276 | | - # Initially _current_key is None |
277 | | - assert resolver._current_key is None |
| 293 | +def test_list_items_are_processed_and_resolved(): |
| 294 | + """Ensure list items (dict and str) are pushed onto the stack and processed. |
| 295 | +
|
| 296 | + This targets the branch that appends list items to the processing stack. |
| 297 | + """ |
| 298 | + config = { |
| 299 | + 'global': {'val': 'G'}, |
| 300 | + 'items': [ |
| 301 | + {'inner': '{global.val}'}, |
| 302 | + '{global.val}', |
| 303 | + 42, # non-processable should be left untouched |
| 304 | + ], |
| 305 | + } |
| 306 | + |
| 307 | + result = replace_placeholders(config) |
| 308 | + |
| 309 | + assert result['items'][0]['inner'] == 'G' |
| 310 | + assert result['items'][1] == 'G' |
| 311 | + assert result['items'][2] == 42 |
| 312 | + |
| 313 | + |
| 314 | +def test_nested_list_processing_appends_stack_items(): |
| 315 | + """Explicitly target the code path that appends list items to the stack. |
| 316 | +
|
| 317 | + The root-level key 'seq' holds a list with a dict and a string placeholder. |
| 318 | + This ensures the `elif isinstance(value, list)` branch runs and the |
| 319 | + inner `stack.append((value, i, context))` line is executed. |
| 320 | + """ |
| 321 | + config = { |
| 322 | + 'seq': [ |
| 323 | + {'inner': '{global.v}'}, |
| 324 | + '{global.v}', |
| 325 | + ], |
| 326 | + 'global': {'v': 'VAL'}, |
| 327 | + } |
278 | 328 |
|
279 | | - # This should return 'unknown' |
280 | | - container_name = resolver._get_container_name() |
281 | | - assert container_name == 'unknown' |
| 329 | + out = replace_placeholders(config) |
| 330 | + assert out['seq'][0]['inner'] == 'VAL' |
| 331 | + assert out['seq'][1] == 'VAL' |
0 commit comments