import json import httpx import respx import pytest from journal_bot.processor_lmstudio import LMStudioProcessor from journal_bot.processor_protocol import ProcessorInput @pytest.fixture def processor(): return LMStudioProcessor( base_url="http://localhost:1234/v1", model="qwen/qwen3-vl-8b", system_prompt="SYS", ) def make_input(text="Hallo Welt"): return ProcessorInput( today="2026-06-14", weekday="Sonntag", received_time="14:32", persons=[], projects=[], text=text, ) @respx.mock def test_health_check_ok(processor): respx.get("http://localhost:1234/v1/models").mock( return_value=httpx.Response(200, json={"data": [{"id": "qwen/qwen3-vl-8b"}]}) ) assert processor.health_check() is True @respx.mock def test_health_check_down(processor): respx.get("http://localhost:1234/v1/models").mock( side_effect=httpx.ConnectError("no") ) assert processor.health_check() is False @respx.mock def test_process_returns_validated_output(processor): payload = { "target_date": "2026-06-14", "target_path": "05 Daily Notes/2026-06-14.md", "entry_markdown": "## 14:32\nHallo Welt", "clarifications": [], "raw_excluded": [], } respx.post("http://localhost:1234/v1/chat/completions").mock( return_value=httpx.Response(200, json={ "choices": [{"message": {"content": json.dumps(payload)}}], }) ) out = processor.process(make_input()) assert out.target_date == "2026-06-14" assert out.entry_markdown.startswith("## 14:32") @respx.mock def test_process_retries_on_schema_mismatch(processor): bad = {"choices": [{"message": {"content": json.dumps({"target_date": "bad"})}}]} good = {"choices": [{"message": {"content": json.dumps({ "target_date": "2026-06-14", "target_path": "05 Daily Notes/2026-06-14.md", "entry_markdown": "## 14:32\nHallo", "clarifications": [], "raw_excluded": [], })}}]} respx.post("http://localhost:1234/v1/chat/completions").mock( side_effect=[httpx.Response(200, json=bad), httpx.Response(200, json=good)] ) out = processor.process(make_input()) assert out.target_date == "2026-06-14" @respx.mock def test_process_raises_after_max_retries(processor): bad = {"choices": [{"message": {"content": "not json"}}]} respx.post("http://localhost:1234/v1/chat/completions").mock( return_value=httpx.Response(200, json=bad) ) with pytest.raises(Exception): processor.process(make_input())