66from lxml import etree
77import pytest
88
9- # Import the module to allow patching with patch.object
109from docbuild .cli .cmd_validate import process as process_module
1110from docbuild .cli .cmd_validate .process import (
1211 process ,
1312 process_file ,
1413 registry ,
1514 run_python_checks ,
15+ validate_rng_lxml ,
1616)
1717from docbuild .cli .context import DocBuildContext
1818from docbuild .config .xml .checks import CheckResult
1919
2020
2121@pytest .fixture
2222def mock_context () -> DocBuildContext :
23- """Fixture for a mocked DocBuildContext."""
2423 context = MagicMock (spec = DocBuildContext )
2524 context .verbose = 3
2625 context .envconfig = {
@@ -29,38 +28,29 @@ def mock_context() -> DocBuildContext:
2928 'base_server_cache_dir' : '/fake/cache' ,
3029 }
3130 }
31+ context .validation_method = 'jing'
3232 return context
3333
3434
35- @patch .object (
36- process_module , 'validate_rng' , new_callable = AsyncMock , return_value = (True , '' )
37- )
38- @patch .object (process_module .asyncio , 'to_thread' )
35+ @patch .object (process_module , 'validate_rng' , new_callable = AsyncMock , return_value = (True , '' ))
36+ @patch .object (process_module .etree , 'parse' , side_effect = ValueError ('Generic test error' ))
3937async def test_process_file_with_generic_parsing_error (
40- mock_to_thread : MagicMock ,
41- mock_validate_rng : AsyncMock ,
42- mock_context : DocBuildContext ,
43- tmp_path : Path ,
44- capsys : pytest .CaptureFixture ,
38+ mock_etree_parse , mock_validate_rng , mock_context , tmp_path , capsys
4539):
46- """Test process_file with a generic exception during parsing.
47-
48- This covers the `except Exception` block.
49- """
50- mock_to_thread .side_effect = ValueError ('Generic test error' )
5140 xml_file = tmp_path / 'file.xml'
5241 xml_file .touch ()
5342
5443 exit_code = await process_file (xml_file , mock_context , max_len = 20 )
5544
5645 assert exit_code == 200
46+ mock_validate_rng .assert_awaited_once ()
47+ mock_etree_parse .assert_called_once ()
5748 captured = capsys .readouterr ()
5849 assert 'Error:' in captured .err
5950 assert 'Generic test error' in captured .err
6051
6152
62- async def test_process_no_envconfig (mock_context : DocBuildContext ):
63- """Test that process raises ValueError if envconfig is missing."""
53+ async def test_process_no_envconfig (mock_context ):
6454 mock_context .envconfig = None
6555 with pytest .raises (ValueError , match = 'No envconfig found in context.' ):
6656 await process (mock_context , xmlfiles = (Path ('dummy.xml' ),))
@@ -69,21 +59,57 @@ async def test_process_no_envconfig(mock_context: DocBuildContext):
6959@patch .object (process_module , 'process_file' , new_callable = AsyncMock , return_value = 0 )
7060@patch .object (process_module , 'create_stitchfile' , new_callable = AsyncMock )
7161async def test_process_with_stitchfile_failure (
72- mock_create_stitchfile : AsyncMock ,
73- mock_process_file : AsyncMock ,
74- mock_context : DocBuildContext ,
75- capsys : pytest .CaptureFixture ,
62+ mock_create_stitchfile , mock_process_file , mock_context , capsys
7663):
77- """Test process function when create_stitchfile raises a ValueError.
78-
79- This covers the `except ValueError` block during stitch-file validation.
80- """
8164 mock_create_stitchfile .side_effect = ValueError ('Duplicate product IDs found' )
82- xml_files = (Path ('/fake/file1.xml' ),) # Need at least one successful file
65+ xml_files = (Path ('/fake/file1.xml' ),)
8366
8467 exit_code = await process (mock_context , xmlfiles = xml_files )
8568
8669 assert exit_code == 1
8770 captured = capsys .readouterr ()
8871 assert 'Stitch-file validation failed:' in captured .err
8972 assert 'Duplicate product IDs found' in captured .err
73+
74+
75+ @patch .object (process_module , 'validate_rng_lxml' , new_callable = MagicMock , return_value = (True , '' ))
76+ @patch .object (process_module , 'etree' )
77+ @patch .object (process_module , 'run_python_checks' , new_callable = AsyncMock )
78+ async def test_process_file_with_lxml_validation_success (
79+ mock_run_checks , mock_etree , mock_validate_lxml , mock_context , tmp_path
80+ ):
81+ xml_file = tmp_path / 'file.xml'
82+ xml_file .touch ()
83+ mock_context .validation_method = 'lxml'
84+ mock_etree .parse .return_value = etree .ElementTree (etree .Element ('root' ))
85+ mock_run_checks .return_value = [(None , CheckResult (success = True ))]
86+
87+ exit_code = await process_file (xml_file , mock_context , max_len = 20 )
88+
89+ assert exit_code == 0
90+ mock_validate_lxml .assert_called_once ()
91+ mock_run_checks .assert_awaited_once ()
92+
93+
94+ @patch .object (process_module , 'validate_rng_lxml' , new_callable = MagicMock , return_value = (False , 'error message' ))
95+ async def test_process_file_with_lxml_validation_failure (
96+ mock_validate_rng_lxml , mock_context , tmp_path
97+ ):
98+ xml_file = tmp_path / 'file.xml'
99+ xml_file .touch ()
100+ mock_context .validation_method = 'lxml'
101+
102+ exit_code = await process_file (xml_file , mock_context , max_len = 20 )
103+
104+ assert exit_code == 10
105+ mock_validate_rng_lxml .assert_called_once ()
106+
107+
108+ async def test_process_file_with_unknown_validation_method (mock_context , tmp_path ):
109+ xml_file = tmp_path / 'file.xml'
110+ xml_file .touch ()
111+ mock_context .validation_method = 'unknown_method'
112+
113+ exit_code = await process_file (xml_file , mock_context , max_len = 20 )
114+
115+ assert exit_code == 11
0 commit comments