"""Unit tests for LlmPipeline — mock LLM client and embedding provider.""" from __future__ import annotations from unittest.mock import MagicMock, patch import json import pytest def _make_pipeline(): with patch("app.infrastructure.perception.llm_pipeline.get_llm_client") as mock_llm_fn, \ patch("app.infrastructure.perception.llm_pipeline.OpenAICompatibleEmbeddingProvider") as mock_emb_cls: mock_client = MagicMock() mock_client.chat.return_value = MagicMock(content='{"obligations":[{"text":"test obligation","deontic":"must","subject":"OEM","object":"system","condition":""}],"deadlines":[{"date":"2026-07-01","description":"实施截止"}],"scope":"适用于M1类车辆","penalties":"罚款","impact_level":"high"}') mock_llm_fn.return_value = mock_client mock_emb = MagicMock() mock_emb.embed_texts.return_value = [[0.1] * 1024, [0.9] * 1024] mock_emb_cls.return_value = mock_emb from app.infrastructure.perception.llm_pipeline import LlmPipeline return LlmPipeline(), mock_client, mock_emb def test_extract_structure_returns_dict(): pipeline, mock_client, _ = _make_pipeline() event = { "id": "evt-001", "standard_code": "GB 18384-2025", "title": "电动汽车安全要求", "summary": "新增 IP67 级别防护", "source_label": "CATARC", "tags": ["电池安全"], } result = pipeline.extract_structure(event) assert isinstance(result, dict) assert "obligations" in result assert "impact_level" in result def test_assess_impact_returns_list(): pipeline, mock_client, _ = _make_pipeline() mock_client.chat.return_value = MagicMock(content='[{"doc_id":"d1","doc_name":"Safety Manual","score":0.85,"key_clauses":"§4.2","recommendation":"更新第4章"}]') mock_retrieval = MagicMock() chunk = MagicMock() chunk.doc_id = "d1" chunk.doc_title = "Safety Manual" chunk.score = 0.85 chunk.text = "relevant text" chunk.section_title = "§4.2" mock_retrieval.retrieve.return_value = [chunk] event = { "standard_code": "GB 18384-2025", "title": "电动汽车安全要求", "obligations": [{"text": "OEM shall comply"}], } result = pipeline.assess_impact(event, mock_retrieval) assert isinstance(result, list) def test_compute_diff_no_change(): pipeline, _, mock_emb = _make_pipeline() mock_emb.embed_texts.return_value = [[0.5] * 1024, [0.5] * 1024] result = pipeline.compute_diff("paragraph one", "paragraph one") assert isinstance(result, dict) assert "changed_sections" in result assert "change_summary" in result def test_compute_diff_detects_change(): pipeline, mock_client, mock_emb = _make_pipeline() mock_emb.embed_texts.return_value = [ [1.0] + [0.0] * 1023, [0.0] + [1.0] + [0.0] * 1022, ] mock_client.chat.return_value = MagicMock(content='{"change_type":"tightened","summary":"Requirement tightened"}') result = pipeline.compute_diff("old paragraph text", "new tighter requirement text") assert isinstance(result["changed_sections"], list)