test: 评估系统重构 - 更新测试适配新语义
- 集成测试适配 dataset/run API 路径 - 单元测试补充并发数参数验证 - conftest 新增测试辅助方法
This commit is contained in:
parent
a7f894691e
commit
9ab8241f16
@ -11,38 +11,38 @@ import pytest
|
||||
pytestmark = [pytest.mark.asyncio, pytest.mark.integration]
|
||||
|
||||
|
||||
async def _upload_test_benchmark(test_client, admin_headers: dict[str, str], db_id: str) -> tuple[str, str]:
|
||||
benchmark_name = f"pytest_benchmark_{uuid.uuid4().hex[:8]}"
|
||||
async def _upload_test_dataset(test_client, admin_headers: dict[str, str], db_id: str) -> tuple[str, str]:
|
||||
dataset_name = f"pytest_dataset_{uuid.uuid4().hex[:8]}"
|
||||
line = '{"query":"什么是单元测试?","gold_answer":"用于验证代码行为的自动化测试"}\n'
|
||||
|
||||
response = await test_client.post(
|
||||
f"/api/evaluation/databases/{db_id}/benchmarks/upload",
|
||||
data={"name": benchmark_name, "description": "pytest benchmark for download"},
|
||||
files={"file": ("pytest_benchmark.jsonl", line.encode("utf-8"), "application/x-ndjson")},
|
||||
f"/api/evaluation/databases/{db_id}/datasets/upload",
|
||||
data={"name": dataset_name, "description": "pytest dataset for download"},
|
||||
files={"file": ("pytest_dataset.jsonl", line.encode("utf-8"), "application/x-ndjson")},
|
||||
headers=admin_headers,
|
||||
)
|
||||
assert response.status_code == 200, response.text
|
||||
|
||||
payload = response.json()
|
||||
assert payload.get("message") == "success"
|
||||
benchmark_id = payload.get("data", {}).get("benchmark_id")
|
||||
assert benchmark_id
|
||||
return benchmark_id, line
|
||||
dataset_id = payload.get("data", {}).get("dataset_id")
|
||||
assert dataset_id
|
||||
return dataset_id, line
|
||||
|
||||
|
||||
async def test_download_benchmark_requires_admin(test_client, standard_user):
|
||||
async def test_download_dataset_requires_admin(test_client, standard_user):
|
||||
response = await test_client.get(
|
||||
"/api/evaluation/benchmarks/benchmark_fake/download",
|
||||
"/api/evaluation/datasets/dataset_fake/download",
|
||||
headers=standard_user["headers"],
|
||||
)
|
||||
assert response.status_code == 403
|
||||
|
||||
|
||||
async def test_admin_can_download_benchmark(test_client, admin_headers, knowledge_database):
|
||||
benchmark_id, expected_line = await _upload_test_benchmark(test_client, admin_headers, knowledge_database["db_id"])
|
||||
async def test_admin_can_download_dataset(test_client, admin_headers, knowledge_database):
|
||||
dataset_id, expected_line = await _upload_test_dataset(test_client, admin_headers, knowledge_database["db_id"])
|
||||
|
||||
response = await test_client.get(
|
||||
f"/api/evaluation/benchmarks/{benchmark_id}/download",
|
||||
f"/api/evaluation/datasets/{dataset_id}/download",
|
||||
headers=admin_headers,
|
||||
)
|
||||
assert response.status_code == 200, response.text
|
||||
@ -53,9 +53,9 @@ async def test_admin_can_download_benchmark(test_client, admin_headers, knowledg
|
||||
assert expected_line.strip() in content
|
||||
|
||||
|
||||
async def test_download_benchmark_not_found(test_client, admin_headers):
|
||||
async def test_download_dataset_not_found(test_client, admin_headers):
|
||||
response = await test_client.get(
|
||||
f"/api/evaluation/benchmarks/benchmark_not_found_{uuid.uuid4().hex[:8]}/download",
|
||||
f"/api/evaluation/datasets/dataset_not_found_{uuid.uuid4().hex[:8]}/download",
|
||||
headers=admin_headers,
|
||||
)
|
||||
assert response.status_code == 404, response.text
|
||||
|
||||
@ -34,6 +34,22 @@ HTTP_TIMEOUT = httpx.Timeout(60.0, connect=5.0)
|
||||
SANDBOX_CONTAINER_PREFIX = os.getenv("YUXI_SANDBOX_CONTAINER_PREFIX", "yuxi-sandbox")
|
||||
|
||||
|
||||
@pytest.fixture(scope="session", autouse=True)
|
||||
def ensure_live_api_schema():
|
||||
if not ADMIN_LOGIN or not ADMIN_PASSWORD:
|
||||
return
|
||||
|
||||
async def run_schema_setup() -> None:
|
||||
from yuxi.storage.postgres.manager import pg_manager
|
||||
|
||||
pg_manager.initialize()
|
||||
await pg_manager.create_tables()
|
||||
await pg_manager.ensure_business_schema()
|
||||
await pg_manager.ensure_knowledge_schema()
|
||||
|
||||
anyio.run(run_schema_setup)
|
||||
|
||||
|
||||
def _require_admin_credentials() -> tuple[str, str]:
|
||||
if not ADMIN_LOGIN or not ADMIN_PASSWORD:
|
||||
pytest.skip("Integration credentials are not configured via TEST_USERNAME / TEST_PASSWORD.")
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import asyncio
|
||||
import os
|
||||
from types import SimpleNamespace
|
||||
|
||||
@ -11,6 +12,7 @@ from yuxi.knowledge.eval.benchmark_generation import (
|
||||
clamp_neighbors_count,
|
||||
collect_kb_chunks,
|
||||
iter_generated_benchmark_items,
|
||||
normalize_generation_concurrency_count,
|
||||
select_neighbor_chunks_by_kb_query,
|
||||
)
|
||||
|
||||
@ -65,12 +67,40 @@ class NoQueryKnowledgeBase(FakeGenerationKnowledgeBase):
|
||||
raise AssertionError("neighbors_count=1 时不应调用 aquery")
|
||||
|
||||
|
||||
class TrackingLlm:
|
||||
def __init__(self, content=None, delay=0):
|
||||
self.content = content or '{"query":"问题","gold_answer":"答案","gold_chunk_ids":["anchor_chunk"]}'
|
||||
self.delay = delay
|
||||
self.active_calls = 0
|
||||
self.max_active_calls = 0
|
||||
self.calls = 0
|
||||
|
||||
async def call(self, prompt, stream):
|
||||
self.calls += 1
|
||||
self.active_calls += 1
|
||||
self.max_active_calls = max(self.max_active_calls, self.active_calls)
|
||||
try:
|
||||
if self.delay:
|
||||
await asyncio.sleep(self.delay)
|
||||
return SimpleNamespace(content=self.content)
|
||||
finally:
|
||||
self.active_calls -= 1
|
||||
|
||||
|
||||
def test_clamp_neighbors_count():
|
||||
assert clamp_neighbors_count(-1) == 0
|
||||
assert clamp_neighbors_count(3) == 3
|
||||
assert clamp_neighbors_count(11) == 10
|
||||
|
||||
|
||||
def test_normalize_generation_concurrency_count():
|
||||
assert normalize_generation_concurrency_count(None) == 10
|
||||
assert normalize_generation_concurrency_count("") == 10
|
||||
assert normalize_generation_concurrency_count(0) == 1
|
||||
assert normalize_generation_concurrency_count(-5) == 1
|
||||
assert normalize_generation_concurrency_count(10000) == 20
|
||||
|
||||
|
||||
def test_build_benchmark_generation_prompt_contains_required_schema():
|
||||
prompt = build_benchmark_generation_prompt([("chunk_1", "片段内容")])
|
||||
|
||||
@ -188,3 +218,65 @@ async def test_iter_generated_benchmark_items_falls_back_to_anchor_when_query_em
|
||||
|
||||
assert items == [{"query": "问题", "gold_chunk_ids": ["anchor_chunk"], "gold_answer": "答案"}]
|
||||
assert "片段ID=anchor_chunk" in fake_llm.prompts[0]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_iter_generated_benchmark_items_respects_concurrency_count(monkeypatch):
|
||||
fake_llm = TrackingLlm(delay=0.01)
|
||||
monkeypatch.setattr(benchmark_generation, "select_model", lambda model_spec: fake_llm)
|
||||
|
||||
items = [
|
||||
item
|
||||
async for item in iter_generated_benchmark_items(
|
||||
kb_instance=NoQueryKnowledgeBase(),
|
||||
db_id="db_1",
|
||||
count=4,
|
||||
neighbors_count=1,
|
||||
concurrency_count=2,
|
||||
llm_model_spec="test-provider:test-model",
|
||||
)
|
||||
]
|
||||
|
||||
assert len(items) == 4
|
||||
assert fake_llm.max_active_calls == 2
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_iter_generated_benchmark_items_returns_at_most_count(monkeypatch):
|
||||
fake_llm = TrackingLlm(delay=0.01)
|
||||
monkeypatch.setattr(benchmark_generation, "select_model", lambda model_spec: fake_llm)
|
||||
|
||||
items = [
|
||||
item
|
||||
async for item in iter_generated_benchmark_items(
|
||||
kb_instance=NoQueryKnowledgeBase(),
|
||||
db_id="db_1",
|
||||
count=3,
|
||||
neighbors_count=1,
|
||||
concurrency_count=10,
|
||||
llm_model_spec="test-provider:test-model",
|
||||
)
|
||||
]
|
||||
|
||||
assert len(items) == 3
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_iter_generated_benchmark_items_stops_at_max_attempts(monkeypatch):
|
||||
fake_llm = TrackingLlm(content='{"query":"","gold_answer":"答案","gold_chunk_ids":["anchor_chunk"]}')
|
||||
monkeypatch.setattr(benchmark_generation, "select_model", lambda model_spec: fake_llm)
|
||||
|
||||
items = [
|
||||
item
|
||||
async for item in iter_generated_benchmark_items(
|
||||
kb_instance=NoQueryKnowledgeBase(),
|
||||
db_id="db_1",
|
||||
count=2,
|
||||
neighbors_count=1,
|
||||
concurrency_count=10,
|
||||
llm_model_spec="test-provider:test-model",
|
||||
)
|
||||
]
|
||||
|
||||
assert items == []
|
||||
assert fake_llm.calls == 50
|
||||
|
||||
Loading…
Reference in New Issue
Block a user