Files
2026-07-13 13:12:33 +08:00

201 lines
6.5 KiB
Python

"""CLI tests for `opensquilla search benchmark`."""
from __future__ import annotations
import json
from typing import cast
from typer.testing import CliRunner
import opensquilla.cli.search_cmd as search_cmd # type: ignore[import-untyped]
from opensquilla.cli.main import app # type: ignore[import-untyped]
from opensquilla.search.types import DEFAULT_SEARCH_MAX_RESULTS
runner = CliRunner()
def _benchmark_payload() -> dict[str, object]:
return {
"profile": "smoke",
"measurement_kind": "synthetic_smoke",
"live": False,
"v1": {
"p50_latency_ms": 10,
"p95_latency_ms": 20,
"success_at_k": 0.7,
"external_tool_calls_per_question": 2,
"avg_returned_chars": 500,
"duplicate_url_rate": 0.2,
"fetch_success_rate": 0.8,
"provider_fallback_count": 1,
},
"v2": {
"p50_latency_ms": 12,
"p95_latency_ms": 24,
"success_at_k": 0.9,
"external_tool_calls_per_question": 1,
"avg_returned_chars": 300,
"duplicate_url_rate": 0.0,
"fetch_success_rate": 1.0,
"provider_fallback_count": 0,
},
"delta": {
"success_at_k": 0.2,
"external_tool_calls_per_question": -1,
"avg_returned_chars": -200,
},
"cases": [
{"id": "official_docs", "query": "python release", "kind": "technical_docs"}
],
}
def _numeric_metric(metrics: dict[str, object], key: str) -> int | float:
value = metrics[key]
assert isinstance(value, int | float)
return value
def _assert_benchmark_metrics(payload: dict[str, object]) -> None:
assert set(payload) >= {"v1", "v2", "delta"}
for arm in ("v1", "v2"):
assert isinstance(payload[arm], dict)
metrics = cast(dict[str, object], payload[arm])
assert set(metrics) >= {
"p50_latency_ms",
"p95_latency_ms",
"success_at_k",
"external_tool_calls_per_question",
"avg_returned_chars",
"duplicate_url_rate",
"fetch_success_rate",
"provider_fallback_count",
}
assert isinstance(payload["delta"], dict)
delta = cast(dict[str, object], payload["delta"])
assert set(delta) >= {
"external_tool_calls_per_question",
"avg_returned_chars",
"success_at_k",
}
assert isinstance(payload.get("cases"), list)
assert payload["cases"]
for row in cast(list[dict[str, object]], payload["cases"]):
assert set(row) >= {"id", "query", "kind"}
v1 = cast(dict[str, object], payload["v1"])
v2 = cast(dict[str, object], payload["v2"])
assert _numeric_metric(v2, "external_tool_calls_per_question") < _numeric_metric(
v1, "external_tool_calls_per_question"
)
assert _numeric_metric(v2, "avg_returned_chars") < _numeric_metric(v1, "avg_returned_chars")
def test_search_benchmark_smoke_json_uses_real_synthetic_output():
result = runner.invoke(app, ["search", "benchmark", "--profile", "smoke", "--json"])
assert result.exit_code == 0, result.stdout
payload = json.loads(result.stdout)
assert payload["profile"] == "smoke"
assert payload["measurement_kind"] == "synthetic_smoke"
assert payload["live"] is False
_assert_benchmark_metrics(payload)
assert payload["query_count"] == len(payload["cases"])
def test_search_benchmark_passes_options_to_helper(monkeypatch):
seen: list[tuple[str, int, int, bool]] = []
def fake_run_search_benchmark(
profile: str,
fetch_top_k: int,
max_results: int,
live: bool = False,
) -> dict[str, object]:
seen.append((profile, fetch_top_k, max_results, live))
return _benchmark_payload()
monkeypatch.setattr(search_cmd, "_run_search_benchmark", fake_run_search_benchmark)
result = runner.invoke(app, ["search", "benchmark", "--profile", "smoke", "--json"])
assert result.exit_code == 0, result.stdout
payload = json.loads(result.stdout)
assert payload["profile"] == "smoke"
assert payload["measurement_kind"] == "synthetic_smoke"
assert payload["live"] is False
_assert_benchmark_metrics(payload)
assert payload["delta"]["external_tool_calls_per_question"] == -1
assert payload["delta"]["avg_returned_chars"] == -200
assert seen == [("smoke", 3, DEFAULT_SEARCH_MAX_RESULTS, False)]
def test_search_benchmark_live_json_rejects_without_env(monkeypatch):
monkeypatch.delenv("OPENSQUILLA_LIVE_SEARCH", raising=False)
result = runner.invoke(
app,
["search", "benchmark", "--profile", "smoke", "--live", "--json"],
)
assert result.exit_code == 2
payload = json.loads(result.stdout)
assert payload["ok"] is False
assert payload["error_kind"] == "invalid_request"
assert "live benchmark" in payload["error"]
def test_search_benchmark_live_json_rejects_even_with_env(monkeypatch):
monkeypatch.setenv("OPENSQUILLA_LIVE_SEARCH", "1")
result = runner.invoke(
app,
["search", "benchmark", "--profile", "smoke", "--live", "--json"],
)
assert result.exit_code == 2
payload = json.loads(result.stdout)
assert payload["ok"] is False
assert payload["error_kind"] == "invalid_request"
assert "live gate test suite" in payload["error"]
assert "measurement_kind" not in payload
assert "v1" not in payload
assert "v2" not in payload
def test_search_benchmark_rejects_zero_max_results_json():
result = runner.invoke(
app,
["search", "benchmark", "--profile", "smoke", "--max-results", "0", "--json"],
)
assert result.exit_code == 2
payload = json.loads(result.stdout)
assert payload["ok"] is False
assert payload["error_kind"] == "invalid_request"
assert "max_results" in payload["error"]
def test_search_benchmark_rejects_negative_fetch_top_k_json():
result = runner.invoke(
app,
["search", "benchmark", "--profile", "smoke", "--fetch-top-k", "-1", "--json"],
)
assert result.exit_code == 2
payload = json.loads(result.stdout)
assert payload["ok"] is False
assert payload["error_kind"] == "invalid_request"
assert "fetch_top_k" in payload["error"]
def test_search_benchmark_rejects_unknown_profile_json():
result = runner.invoke(
app,
["search", "benchmark", "--profile", "unknown", "--json"],
)
assert result.exit_code == 2
payload = json.loads(result.stdout)
assert payload["ok"] is False
assert payload["error_kind"] == "invalid_request"