@@ -43,16 +43,28 @@ async def _fake_success(collection, out_dir):
4343
4444@pytest .fixture ()
4545def patched_pipeline (monkeypatch , sample_papers ):
46- async def fake_run_search (query : Query , ** _kwargs ) -> PaperCollection :
46+ """Stub cli's run_search + shutdown_clients and return a dict that captures
47+ the Query passed to run_search (``patched_pipeline["query"]``).
48+
49+ Tests that only need the pipeline stubbed ignore the return value; tests
50+ that assert on the constructed Query read ``patched_pipeline["query"]``
51+ instead of each re-rolling their own ``fake_run_search`` / ``fake_shutdown``
52+ boilerplate.
53+ """
54+ captured : dict [str , Query ] = {}
55+
56+ async def fake_run_search (query , ** _kwargs ): # NOSONAR async stub
57+ captured ["query" ] = query
4758 return PaperCollection (query = query , papers = tuple (sample_papers ))
4859
49- async def fake_shutdown () -> None :
60+ async def fake_shutdown (): # NOSONAR async stub
5061 return None
5162
5263 monkeypatch .setattr (cli_module , "run_search" , fake_run_search )
5364 monkeypatch .setattr (cli_module , "shutdown_clients" , fake_shutdown )
5465 # Leave the autouse ``_stub_download_pdfs`` in place so the new per-paper
5566 # PPT gate sees every paper as downloadable.
67+ return captured
5668
5769
5870def test_cli_runs_end_to_end (tmp_path : Path , patched_pipeline , capsys ):
@@ -248,28 +260,16 @@ def test_cli_rejects_doi_identifier_until_resolver_lands(tmp_path):
248260 assert code == 2
249261
250262
251- def test_cli_source_default_is_multi_source (tmp_path , monkeypatch , sample_papers ):
263+ def test_cli_source_default_is_multi_source (tmp_path , patched_pipeline ):
252264 """When --source is omitted, run_search must be invoked across the
253265 DEFAULT_SOURCES mix, not just arxiv."""
254266 from thesisagents .core .constants import DEFAULT_SOURCES
255267
256- captured : dict [str , Query ] = {}
257-
258- async def fake_run_search (query : Query , ** _kwargs ) -> PaperCollection :
259- captured ["query" ] = query
260- return PaperCollection (query = query , papers = tuple (sample_papers ))
261-
262- async def fake_shutdown () -> None :
263- return None
264-
265- monkeypatch .setattr (cli_module , "run_search" , fake_run_search )
266- monkeypatch .setattr (cli_module , "shutdown_clients" , fake_shutdown )
267-
268268 code = cli_module .main (
269269 ["--query" , "x" , "--out" , str (tmp_path ), "--export" , "bib" ]
270270 )
271271 assert code == 0
272- assert captured ["query" ].sources == DEFAULT_SOURCES
272+ assert patched_pipeline ["query" ].sources == DEFAULT_SOURCES
273273
274274
275275def test_cli_list_sources (capsys ):
@@ -293,53 +293,30 @@ def test_cli_list_exports(capsys):
293293 assert "pptx" in out
294294
295295
296- def test_cli_exclude_source_prunes_default_mix (tmp_path , monkeypatch , sample_papers ):
296+ def test_cli_exclude_source_prunes_default_mix (tmp_path , patched_pipeline ):
297297 """--exclude-source subtracts from the resolved mix; the no-VPN path drops
298298 only ieee and keeps every other default source."""
299299 from thesisagents .core .constants import DEFAULT_SOURCES
300300
301- captured : dict [str , Query ] = {}
302-
303- async def fake_run_search (query : Query , ** _kwargs ) -> PaperCollection : # NOSONAR async stub
304- captured ["query" ] = query
305- return PaperCollection (query = query , papers = tuple (sample_papers ))
306-
307- async def fake_shutdown () -> None : # NOSONAR async stub
308- return None
309-
310- monkeypatch .setattr (cli_module , "run_search" , fake_run_search )
311- monkeypatch .setattr (cli_module , "shutdown_clients" , fake_shutdown )
312-
313301 code = cli_module .main (
314302 ["--query" , "x" , "--out" , str (tmp_path ), "--export" , "bib" ,
315303 "--exclude-source" , "ieee" ]
316304 )
317305 assert code == 0
318- assert "ieee" not in captured ["query" ].sources
306+ assert "ieee" not in patched_pipeline ["query" ].sources
319307 expected = tuple (s for s in DEFAULT_SOURCES if s != "ieee" )
320- assert captured ["query" ].sources == expected
308+ assert patched_pipeline ["query" ].sources == expected
321309
322310
323- def test_cli_min_citations_flows_into_query (tmp_path , monkeypatch , sample_papers ):
311+ def test_cli_min_citations_flows_into_query (tmp_path , patched_pipeline ):
324312 """--min-citations is parsed and passed through to the Query (it was
325313 previously unreachable from the CLI)."""
326- captured : dict [str , Query ] = {}
327-
328- async def fake_run_search (query : Query , ** _kwargs ) -> PaperCollection : # NOSONAR async stub
329- captured ["query" ] = query
330- return PaperCollection (query = query , papers = tuple (sample_papers ))
331-
332- async def fake_shutdown () -> None : # NOSONAR async stub
333- return None
334-
335- monkeypatch .setattr (cli_module , "run_search" , fake_run_search )
336- monkeypatch .setattr (cli_module , "shutdown_clients" , fake_shutdown )
337314 code = cli_module .main (
338315 ["--query" , "x" , "--out" , str (tmp_path ), "--export" , "bib" ,
339316 "--min-citations" , "50" ]
340317 )
341318 assert code == 0
342- assert captured ["query" ].min_citations == 50
319+ assert patched_pipeline ["query" ].min_citations == 50
343320
344321
345322def test_cli_exclude_unknown_source_errors (tmp_path ):
@@ -359,33 +336,21 @@ def test_cli_exclude_all_sources_errors(tmp_path):
359336 )
360337
361338
362- def test_cli_top_tier_filter_off_by_default (tmp_path , monkeypatch , sample_papers ):
339+ def test_cli_top_tier_filter_off_by_default (tmp_path , patched_pipeline ):
363340 """top_tier_only is OFF by default (broader coverage including IEEE / ACM
364341 workshops); --top-tier-only flips it on."""
365- captured : dict [str , Query ] = {}
366-
367- async def fake_run_search (query : Query , ** _kwargs ) -> PaperCollection :
368- captured ["query" ] = query
369- return PaperCollection (query = query , papers = tuple (sample_papers ))
370-
371- async def fake_shutdown () -> None :
372- return None
373-
374- monkeypatch .setattr (cli_module , "run_search" , fake_run_search )
375- monkeypatch .setattr (cli_module , "shutdown_clients" , fake_shutdown )
376-
377342 code = cli_module .main (
378343 ["--query" , "x" , "--out" , str (tmp_path ), "--export" , "bib" ]
379344 )
380345 assert code == 0
381- assert captured ["query" ].top_tier_only is False
346+ assert patched_pipeline ["query" ].top_tier_only is False
382347
383- captured .clear ()
348+ patched_pipeline .clear ()
384349 code = cli_module .main (
385350 ["--query" , "x" , "--top-tier-only" , "--out" , str (tmp_path ), "--export" , "bib" ]
386351 )
387352 assert code == 0
388- assert captured ["query" ].top_tier_only is True
353+ assert patched_pipeline ["query" ].top_tier_only is True
389354
390355
391356def test_cli_default_triggers_pdf_download (tmp_path , monkeypatch , sample_papers ):
0 commit comments