=== TASK 2 REVIEW PACKAGE ===
Uncommitted. Scope: search endpoint + controller tests only.

=== CONTROLLER ENDPOINT ===
@router.post("/volcano_kb/search", summary="Search Volcano KB slices by keyword")
def search_knowledge(request: Request, payload: dict = Body(...)):
    if not volcano_kb.is_enabled():
        raise HttpException(
            "",
            status_code=400,
            message="volcano_kb is not enabled; configure volcengine_kb_*",
        )
    query = ((payload or {}).get("query") or "").strip()
    if not query:
        raise HttpException("", status_code=400, message="query is required")
    try:
        limit = int((payload or {}).get("limit") or 10)
    except (TypeError, ValueError):
        limit = 10
    limit = max(1, min(limit, 50))
    try:
        hits = volcano_kb.search_slices(query, limit=limit)
    except volcano_kb.VolcanoKBError as exc:
        raise HttpException("", status_code=502, message=str(exc))
    return utils.get_response(200, {"query": query, "hits": hits, "count": len(hits)})


=== TEST FILE (from first test_search*) ===
def test_search_requires_query(self):
        with patch("app.services.volcano_kb.is_enabled", return_value=True):
            r = self.client.post(
                "/api/v1/volcano_kb/search", json={"query": "  ", "limit": 5}
            )
        self.assertEqual(r.status_code, 400)

    def test_search_rejects_when_disabled(self):
        with patch("app.services.volcano_kb.is_enabled", return_value=False):
            r = self.client.post(
                "/api/v1/volcano_kb/search", json={"query": "流量计", "limit": 5}
            )
        self.assertEqual(r.status_code, 400)

    def test_search_returns_hits(self):
        hits = [{
            "point_id": "p1", "doc_id": "D1", "start_ms": 0, "end_ms": 1000,
            "summary": "s", "keyframe_url": "", "score": 0.5, "filename": "a.mp4",
        }]
        with patch("app.services.volcano_kb.is_enabled", return_value=True), \
             patch("app.services.volcano_kb.search_slices", return_value=hits) as m:
            r = self.client.post(
                "/api/v1/volcano_kb/search", json={"query": "流量计", "limit": 3}
            )
        m.assert_called_once_with("流量计", limit=3)
        self.assertEqual(r.status_code, 200)
        data = r.json()["data"]
        self.assertEqual(data["query"], "流量计")
        self.assertEqual(data["count"], 1)
        self.assertEqual(data["hits"][0]["score"], 0.5)
        self.assertEqual(data["hits"][0]["filename"], "a.mp4")

    def test_search_volcano_error_returns_502(self):
        from app.services.volcano_kb import VolcanoKBError

        with patch("app.services.volcano_kb.is_enabled", return_value=True), \
             patch("app.services.volcano_kb.search_slices",
                   side_effect=VolcanoKBError("upstream failed")):
            r = self.client.post(
                "/api/v1/volcano_kb/search", json={"query": "流量计", "limit": 5}
            )
        self.assertEqual(r.status_code, 502)


if __name__ == "__main__":
    unittest.main()

