@@ -19,62 +19,116 @@ def _pdf(tmp_path):
1919 return pdf
2020
2121
22- def test_pdf_to_markdown_writes_md_and_localises_figures (tmp_path ):
22+ def _post (pdf_id = "abc123" , error = None ):
23+ post = MagicMock (status_code = 200 )
24+ post .json .return_value = {"error" : error } if error else {"pdf_id" : pdf_id }
25+ return post
26+
27+
28+ def _status (status , error = None ):
29+ body = {"status" : status }
30+ if error :
31+ body ["error" ] = error
32+ resp = MagicMock (status_code = 200 )
33+ resp .json .return_value = body
34+ return resp
35+
36+
37+ def test_pdf_to_markdown_returns_markdown_and_localises_figures (tmp_path ):
2338 pdf = _pdf (tmp_path )
2439 out_dir = tmp_path / "out"
2540
26- post = MagicMock (status_code = 200 )
27- post .json .return_value = {"pdf_id" : "abc123" }
41+ completed = _status ("completed" )
2842 md = MagicMock (
2943 status_code = 200 ,
3044 text = "# Heading\n \n  done\n " ,
3145 )
3246 image = MagicMock (status_code = 200 , content = b"PNGBYTES" )
3347
3448 with patch ("in2lambda.wizard.mathpix.requests" ) as req :
35- req .post .return_value = post
36- req .get .side_effect = [md , image ]
37- md_path = pdf_to_markdown (str (pdf ), str (out_dir ), poll_interval = 0.0 )
49+ req .post .return_value = _post ()
50+ req .get .side_effect = [completed , md , image ]
51+ markdown = pdf_to_markdown (str (pdf ), str (out_dir ), poll_interval = 0.0 )
3852
39- assert md_path == out_dir / "paper.md"
40- text = md_path .read_text ()
41- assert "" in text
53+ assert "" in markdown
54+ assert not (out_dir / "paper.md" ).exists ()
4255 assert (out_dir / "media" / "0_fig.png" ).read_bytes () == b"PNGBYTES"
4356
4457
4558def test_pdf_to_markdown_polls_until_ready (tmp_path ):
4659 pdf = _pdf (tmp_path )
4760
48- post = MagicMock (status_code = 200 )
49- post .json .return_value = {"pdf_id" : "abc123" }
50- not_ready = MagicMock (status_code = 202 )
51- ready = MagicMock (status_code = 200 , text = "# Only text, no figures\n " )
61+ processing = _status ("processing" )
62+ completed = _status ("completed" )
63+ md = MagicMock (status_code = 200 , text = "# Only text, no figures\n " )
5264
5365 with patch ("in2lambda.wizard.mathpix.requests" ) as req :
54- req .post .return_value = post
55- req .get .side_effect = [not_ready , not_ready , ready ]
56- md_path = pdf_to_markdown (
66+ req .post .return_value = _post ()
67+ req .get .side_effect = [processing , processing , completed , md ]
68+ markdown = pdf_to_markdown (
5769 str (pdf ), str (tmp_path / "out" ), poll_interval = 0.0 , max_polls = 5
5870 )
5971
60- assert md_path . read_text () .startswith ("# Only text" )
72+ assert markdown .startswith ("# Only text" )
6173
6274
6375def test_pdf_to_markdown_times_out (tmp_path ):
6476 pdf = _pdf (tmp_path )
6577
66- post = MagicMock (status_code = 200 )
67- post .json .return_value = {"pdf_id" : "abc123" }
68-
6978 with patch ("in2lambda.wizard.mathpix.requests" ) as req :
70- req .post .return_value = post
71- req .get .return_value = MagicMock ( status_code = 202 )
79+ req .post .return_value = _post ()
80+ req .get .return_value = _status ( "processing" )
7281 with pytest .raises (RuntimeError , match = "did not finish" ):
7382 pdf_to_markdown (
7483 str (pdf ), str (tmp_path / "out" ), poll_interval = 0.0 , max_polls = 3
7584 )
7685
7786
87+ def test_pdf_to_markdown_raises_on_rejected_upload (tmp_path ):
88+ pdf = _pdf (tmp_path )
89+
90+ with patch ("in2lambda.wizard.mathpix.requests" ) as req :
91+ req .post .return_value = _post (error = "Invalid file type" )
92+ with pytest .raises (RuntimeError , match = "Mathpix rejected the PDF" ):
93+ pdf_to_markdown (str (pdf ), str (tmp_path / "out" ))
94+
95+
96+ def test_pdf_to_markdown_raises_immediately_on_conversion_error (tmp_path ):
97+ pdf = _pdf (tmp_path )
98+
99+ with patch ("in2lambda.wizard.mathpix.requests" ) as req :
100+ req .post .return_value = _post ()
101+ req .get .return_value = _status ("error" , error = "conversion failed" )
102+ with pytest .raises (RuntimeError , match = "conversion failed" ):
103+ pdf_to_markdown (
104+ str (pdf ), str (tmp_path / "out" ), poll_interval = 0.0 , max_polls = 60
105+ )
106+
107+ # Only the single status poll should have happened, not all 60.
108+ assert req .get .call_count == 1
109+
110+
111+ def test_pdf_to_markdown_warns_on_failed_figure_download (tmp_path ):
112+ pdf = _pdf (tmp_path )
113+ out_dir = tmp_path / "out"
114+
115+ completed = _status ("completed" )
116+ md = MagicMock (
117+ status_code = 200 ,
118+ text = " done\n " ,
119+ )
120+ image = MagicMock (status_code = 404 , content = b"" )
121+
122+ with patch ("in2lambda.wizard.mathpix.requests" ) as req :
123+ req .post .return_value = _post ()
124+ req .get .side_effect = [completed , md , image ]
125+ with pytest .warns (UserWarning , match = "figure download failed" ):
126+ markdown = pdf_to_markdown (str (pdf ), str (out_dir ), poll_interval = 0.0 )
127+
128+ assert "https://cdn.mathpix.com/x/fig.png" in markdown
129+ assert not (out_dir / "media" / "0_fig.png" ).exists ()
130+
131+
78132def test_missing_credentials_raise (tmp_path , monkeypatch ):
79133 monkeypatch .delenv ("MATHPIX_APP_ID" , raising = False )
80134 monkeypatch .delenv ("MATHPIX_API_KEY" , raising = False )
0 commit comments