@@ -416,6 +416,70 @@ def test_query_auto_falls_back_to_duck_when_google_list_answer_is_too_short(
416416 assert "too short for the requested list" in (recent [1 ].error_message or "" )
417417
418418
419+ def test_query_auto_falls_back_to_duck_when_google_json_results_are_empty (
420+ test_app ,
421+ ) -> None :
422+ prompt = (
423+ "只返回一个 JSON 对象,输出格式固定为 "
424+ '{"results":[{"title":"","content":"","source":"","url":"","published_date":"YYYY-MM-DD"}]}。'
425+ '若找不到足够直接相关的结果,返回 {"results": []}。'
426+ "问题:PingAn 000001.SZ 最新公告 新闻 催化 风险 最多返回 5 条"
427+ )
428+ duck_answer = (
429+ '{"results":[{"title":"平安银行公告","content":"平安银行发布公告。",'
430+ '"source":"证券时报","url":"https://www.stcn.com/article/detail/1234567.html",'
431+ '"published_date":"2026-05-27"}]}'
432+ )
433+ with TestClient (test_app ) as client :
434+ _set_search_engine (test_app , "auto" )
435+ google_pool = _install_fake_pool (test_app , answer_text = '{"results": []}' )
436+ duck_pool = _install_fake_duck_pool (test_app , answer_text = duck_answer )
437+ response = client .post (
438+ "/query" ,
439+ headers = _auth_headers (),
440+ json = {"model" : "google-search" , "query" : prompt },
441+ )
442+ recent = test_app .state .services .store .list_recent_requests (limit = 2 )
443+
444+ assert response .status_code == 200
445+ assert response .json ()["answer" ] == duck_answer
446+ assert google_pool .prompts == [f"User request:\n { prompt } " ]
447+ assert duck_pool .prompts == [f"User request:\n { prompt } " ]
448+ assert [record .engine for record in recent ] == ["duck" , "google" ]
449+ assert [record .status for record in recent ] == ["ok" , "error" ]
450+ assert "empty JSON results" in (recent [1 ].error_message or "" )
451+
452+
453+ def test_query_auto_rejects_when_both_engines_return_empty_json_results (
454+ test_app ,
455+ ) -> None :
456+ prompt = (
457+ "只返回一个 JSON 对象,输出格式固定为 "
458+ '{"results":[{"title":"","content":"","source":"","url":"","published_date":"YYYY-MM-DD"}]}。'
459+ '若找不到足够直接相关的结果,返回 {"results": []}。'
460+ "问题:PingAn 000001.SZ 最新公告 新闻 催化 风险 最多返回 5 条"
461+ )
462+ with TestClient (test_app ) as client :
463+ _set_search_engine (test_app , "auto" )
464+ google_pool = _install_fake_pool (test_app , answer_text = '{"results": []}' )
465+ duck_pool = _install_fake_duck_pool (test_app , answer_text = '{"results": []}' )
466+ response = client .post (
467+ "/query" ,
468+ headers = _auth_headers (),
469+ json = {"model" : "google-search" , "query" : prompt },
470+ )
471+ recent = test_app .state .services .store .list_recent_requests (limit = 2 )
472+
473+ assert response .status_code == 502
474+ assert "Both search engines failed" in response .json ()["detail" ]
475+ assert "Google returned empty JSON results in auto mode" in response .json ()["detail" ]
476+ assert "Duck.ai returned empty JSON results in auto mode" in response .json ()["detail" ]
477+ assert google_pool .prompts == [f"User request:\n { prompt } " ]
478+ assert duck_pool .prompts == [f"User request:\n { prompt } " ]
479+ assert [record .engine for record in recent ] == ["duck" , "google" ]
480+ assert [record .status for record in recent ] == ["error" , "error" ]
481+
482+
419483def test_query_uses_active_sticky_proxy_session_when_enabled (test_app ) -> None :
420484 with TestClient (test_app ) as client :
421485 test_app .state .services .store .update_config (
0 commit comments