@@ -96,6 +96,7 @@ def webpy_client(mock_work_search, mock_fulltext_search, mock_run_solr_query):
9696 """
9797 from openlibrary .plugins .inside .code import search_inside_json
9898 from openlibrary .plugins .worksearch .code import (
99+ author_search_json ,
99100 list_search_json ,
100101 search_json ,
101102 subject_search_json ,
@@ -111,6 +112,8 @@ def webpy_client(mock_work_search, mock_fulltext_search, mock_run_solr_query):
111112 'subject_search_json' ,
112113 '/search/lists' ,
113114 'list_search_json' ,
115+ '/search/authors' ,
116+ 'author_search_json' ,
114117 )
115118
116119 # Create app with all handlers in the global namespace
@@ -121,6 +124,7 @@ def webpy_client(mock_work_search, mock_fulltext_search, mock_run_solr_query):
121124 'search_inside_json' : search_inside_json ,
122125 'subject_search_json' : subject_search_json ,
123126 'list_search_json' : list_search_json ,
127+ 'author_search_json' : author_search_json ,
124128 },
125129 )
126130
@@ -306,6 +310,79 @@ def test_both_search_inside_endpoints_call_search_with_same_params(
306310 f"FastAPI={ fastapi_val } , webpy={ webpy_val } "
307311 )
308312
313+ @pytest .mark .parametrize (
314+ ('params' , 'description' ),
315+ [
316+ ({'q' : 'shakespeare' }, 'basic query' ),
317+ ({'q' : 'twain' , 'offset' : '5' , 'limit' : '10' }, 'pagination' ),
318+ ],
319+ )
320+ def test_both_authors_endpoints_call_search_with_same_params (
321+ self ,
322+ fastapi_client ,
323+ webpy_client ,
324+ mock_async_run_solr_query ,
325+ mock_run_solr_query ,
326+ params ,
327+ description ,
328+ ):
329+ """Verify both webpy and FastAPI search/authors endpoints pass equivalent parameters.
330+
331+ This test makes real HTTP requests to both FastAPI and webpy search/authors endpoints,
332+ then compares the arguments passed to their respective solr query functions.
333+ """
334+ query_string = urlencode (params , doseq = True )
335+
336+ # === Call FastAPI endpoint ===
337+ fastapi_response = fastapi_client .get (f'/search/authors.json?{ query_string } ' )
338+ assert fastapi_response .status_code == 200 , f"FastAPI failed for: { description } "
339+
340+ mock_async_run_solr_query .assert_called_once ()
341+ fastapi_call_args = mock_async_run_solr_query .call_args
342+
343+ # === Call webpy endpoint ===
344+ webpy_response = webpy_client .get (f'/search/authors?{ query_string } ' )
345+ assert webpy_response .status_code == 200 , f"webpy failed for: { description } "
346+
347+ mock_run_solr_query .assert_called_once ()
348+ webpy_call_args = mock_run_solr_query .call_args
349+
350+ # === Compare the call arguments ===
351+ # Both call their respective solr query functions with scheme, param dict, and kwargs
352+
353+ # Compare param dict (second positional arg)
354+ fastapi_param = fastapi_call_args [0 ][1 ]
355+ webpy_param = webpy_call_args [0 ][1 ]
356+
357+ for key in ['q' ]:
358+ fastapi_val = fastapi_param .get (key )
359+ webpy_val = webpy_param .get (key )
360+ assert fastapi_val == webpy_val , (
361+ f"Parameter '{ key } ' mismatch for { description } : "
362+ f"FastAPI={ fastapi_val } , webpy={ webpy_val } "
363+ )
364+
365+ # Compare kwargs
366+ fastapi_kwargs = {
367+ k : v for k , v in fastapi_call_args [1 ].items () if k not in ['scheme' ]
368+ }
369+ webpy_kwargs = {
370+ k : v for k , v in webpy_call_args [1 ].items () if k not in ['scheme' ]
371+ }
372+
373+ for key in ['offset' , 'rows' , 'fields' , 'sort' , 'request_label' ]:
374+ fastapi_val = fastapi_kwargs .get (key )
375+ webpy_val = webpy_kwargs .get (key )
376+
377+ # Handle offset: FastAPI may pass None, webpy defaults to 0
378+ if key == 'offset' and fastapi_val is None and webpy_val == 0 :
379+ continue
380+
381+ assert fastapi_val == webpy_val , (
382+ f"Parameter '{ key } ' mismatch for { description } : "
383+ f"FastAPI={ fastapi_val } , webpy={ webpy_val } "
384+ )
385+
309386 @pytest .mark .parametrize (
310387 ('params' , 'description' ),
311388 [
0 commit comments