test(ebook): cover protected phrases and migrate suite to async
Add test_protected_phrases.py covering phrase-matching behavior in the RAG engine, and update the existing ebook_search tests to use the async SQLAlchemy engine/session (create_async_engine, AsyncSession) and async HTTP paths.
This commit is contained in:
@@ -24,6 +24,13 @@ def candidates() -> list[SearchResult]:
|
||||
]
|
||||
|
||||
|
||||
def make_async_client(mocker: MockerFixture, fake_post) -> httpx.AsyncClient:
|
||||
"""Build a mock async client whose post call is served by fake_post."""
|
||||
client = mocker.MagicMock(spec=httpx.AsyncClient)
|
||||
client.post = mocker.AsyncMock(side_effect=fake_post)
|
||||
return client
|
||||
|
||||
|
||||
def rerank_response(payload: dict[str, object] | None = None, *, content: bytes | None = None) -> httpx.Response:
|
||||
return httpx.Response(
|
||||
200,
|
||||
@@ -59,7 +66,7 @@ def test_reranking_disabled_returns_original_fused_order() -> None:
|
||||
assert [result.chunk_id for result in response.results] == [1, 2]
|
||||
|
||||
|
||||
def test_reranking_enabled_reorders_candidates(mocker: MockerFixture) -> None:
|
||||
async def test_reranking_enabled_reorders_candidates(mocker: MockerFixture) -> None:
|
||||
def fake_post(_url: str, *, json: dict[str, object], timeout: float) -> httpx.Response:
|
||||
assert timeout == 30
|
||||
assert json == {
|
||||
@@ -77,16 +84,16 @@ def test_reranking_enabled_reorders_candidates(mocker: MockerFixture) -> None:
|
||||
}
|
||||
)
|
||||
|
||||
mocker.patch.object(httpx, "post", side_effect=fake_post)
|
||||
client = make_async_client(mocker, fake_post)
|
||||
|
||||
results = rerank_chunks("query", candidates(), RerankConfig())
|
||||
results = await rerank_chunks(client, "query", candidates(), RerankConfig())
|
||||
|
||||
assert [result.chunk_id for result in results] == [2, 1, 3]
|
||||
assert [round(result.score, 3) for result in results] == [0.78, 0.37, 0.28]
|
||||
assert [result.rerank_score for result in results] == [0.9, 0.1, 0.4]
|
||||
|
||||
|
||||
def test_reranking_cannot_ignore_hybrid_score(mocker: MockerFixture) -> None:
|
||||
async def test_reranking_cannot_ignore_hybrid_score(mocker: MockerFixture) -> None:
|
||||
candidates = [
|
||||
SearchResult(chunk_id=1, text="strong hybrid", source_title="A", score=1.0),
|
||||
SearchResult(chunk_id=2, text="weak hybrid", source_title="B", score=0.1),
|
||||
@@ -102,9 +109,9 @@ def test_reranking_cannot_ignore_hybrid_score(mocker: MockerFixture) -> None:
|
||||
}
|
||||
)
|
||||
|
||||
mocker.patch.object(httpx, "post", side_effect=fake_post)
|
||||
client = make_async_client(mocker, fake_post)
|
||||
|
||||
results = rerank_chunks("query", candidates, RerankConfig())
|
||||
results = await rerank_chunks(client, "query", candidates, RerankConfig())
|
||||
|
||||
assert [result.chunk_id for result in results] == [1, 2]
|
||||
assert results[0].score == pytest.approx(0.79)
|
||||
@@ -112,8 +119,9 @@ def test_reranking_cannot_ignore_hybrid_score(mocker: MockerFixture) -> None:
|
||||
assert results[1].rerank_score == 1.0
|
||||
|
||||
|
||||
def test_vllm_rerank_timeout_raises(mocker: MockerFixture) -> None:
|
||||
async def test_vllm_rerank_timeout_raises(mocker: MockerFixture) -> None:
|
||||
def fake_rerank_chunks(
|
||||
_client: httpx.AsyncClient,
|
||||
_query: str,
|
||||
_candidates: list[SearchResult],
|
||||
_config: RerankConfig,
|
||||
@@ -125,21 +133,21 @@ def test_vllm_rerank_timeout_raises(mocker: MockerFixture) -> None:
|
||||
config = EbookSearchConfig(rerank=RerankConfig(enabled=True), top_k=2)
|
||||
|
||||
with pytest.raises(httpx.TimeoutException, match="timeout"):
|
||||
apply_rerank("query", candidates(), config)
|
||||
await apply_rerank(mocker.Mock(), "query", candidates(), config)
|
||||
|
||||
|
||||
def test_malformed_vllm_rerank_json_does_not_crash_search(mocker: MockerFixture) -> None:
|
||||
async def test_malformed_vllm_rerank_json_does_not_crash_search(mocker: MockerFixture) -> None:
|
||||
def fake_post(_url: str, **_kwargs: object) -> httpx.Response:
|
||||
return rerank_response(content=b"not-json")
|
||||
|
||||
mocker.patch.object(httpx, "post", side_effect=fake_post)
|
||||
client = make_async_client(mocker, fake_post)
|
||||
|
||||
results = rerank_chunks("query", candidates()[:1], RerankConfig())
|
||||
results = await rerank_chunks(client, "query", candidates()[:1], RerankConfig())
|
||||
|
||||
assert results[0].score == 0.3
|
||||
|
||||
|
||||
def test_vllm_rerank_scores_are_clamped(mocker: MockerFixture) -> None:
|
||||
async def test_vllm_rerank_scores_are_clamped(mocker: MockerFixture) -> None:
|
||||
def fake_post(_url: str, **_kwargs: object) -> httpx.Response:
|
||||
return rerank_response(
|
||||
{
|
||||
@@ -150,8 +158,8 @@ def test_vllm_rerank_scores_are_clamped(mocker: MockerFixture) -> None:
|
||||
}
|
||||
)
|
||||
|
||||
mocker.patch.object(httpx, "post", side_effect=fake_post)
|
||||
client = make_async_client(mocker, fake_post)
|
||||
|
||||
results = rerank_chunks("query", candidates()[:2], RerankConfig())
|
||||
results = await rerank_chunks(client, "query", candidates()[:2], RerankConfig())
|
||||
|
||||
assert {result.chunk_id: result.rerank_score for result in results} == {1: 0.0, 2: 1.0}
|
||||
|
||||
Reference in New Issue
Block a user