| import json |
| import unittest |
| from unittest.mock import patch |
|
|
| from training_coach.parser_ollama import ( |
| OLLAMA_MODEL, |
| OLLAMA_TINY_CANDIDATE, |
| build_ollama_prompt, |
| generate_parser_response_ollama, |
| parse_check_in_with_ollama, |
| ) |
|
|
|
|
| class _FakeResponse: |
| def __init__(self, payload): |
| self.payload = payload |
|
|
| def __enter__(self): |
| return self |
|
|
| def __exit__(self, *args): |
| return False |
|
|
| def read(self): |
| return json.dumps(self.payload).encode("utf-8") |
|
|
|
|
| class OllamaParserTest(unittest.TestCase): |
| def test_ollama_model_choices_are_locked(self): |
| self.assertEqual(OLLAMA_MODEL, "qwen3:1.7B") |
| self.assertEqual(OLLAMA_TINY_CANDIDATE, "qwen3:4b") |
|
|
| def test_build_ollama_prompt_includes_schema_and_check_in(self): |
| prompt = build_ollama_prompt("Yesterday I ran a 10k.") |
|
|
| self.assertIn("Expected response JSON schema", prompt) |
| self.assertIn("pain_issues", prompt) |
| self.assertIn("Yesterday I ran a 10k.", prompt) |
|
|
| def test_generate_parser_response_ollama_sends_schema_format(self): |
| fake_payload = { |
| "prompt_eval_count": 120, |
| "eval_count": 40, |
| "total_duration": 1_500_000_000, |
| "load_duration": 100_000_000, |
| "prompt_eval_duration": 400_000_000, |
| "eval_duration": 900_000_000, |
| "response": json.dumps( |
| { |
| "check_in": { |
| "raw_text": "60 min", |
| "time_available_minutes": 60, |
| } |
| } |
| ) |
| } |
|
|
| with patch( |
| "training_coach.parser_ollama.request.urlopen", |
| return_value=_FakeResponse(fake_payload), |
| ) as urlopen, self.assertLogs( |
| "training_coach.parser_ollama", |
| level="INFO", |
| ) as logs: |
| response = generate_parser_response_ollama("60 min") |
|
|
| sent_request = urlopen.call_args.args[0] |
| body = json.loads(sent_request.data.decode("utf-8")) |
| self.assertEqual(body["model"], "qwen3:1.7B") |
| self.assertEqual(body["stream"], False) |
| self.assertEqual(body["think"], False) |
| self.assertEqual(body["format"]["title"], "ParsedCheckIn") |
| self.assertIn("60 min", body["prompt"]) |
| self.assertIn("check_in", response) |
| output = "\n".join(logs.output) |
| self.assertIn("event=parser_ollama_request", output) |
| self.assertIn("event=parser_ollama_response", output) |
| self.assertIn("prompt_eval_count=120", output) |
| self.assertIn("eval_count=40", output) |
|
|
| def test_generate_parser_response_ollama_logs_private_trace_when_enabled(self): |
| fake_payload = {"response": '{"check_in": {"raw_text": "60 min"}}'} |
|
|
| with patch.dict( |
| "os.environ", |
| { |
| "PARSER_LOG_MESSAGES": "1", |
| "PARSER_LOG_RESPONSES": "1", |
| }, |
| clear=True, |
| ), patch( |
| "training_coach.parser_ollama.request.urlopen", |
| return_value=_FakeResponse(fake_payload), |
| ), self.assertLogs("training_coach.parser", level="INFO") as logs: |
| generate_parser_response_ollama("60 min") |
|
|
| output = "\n".join(logs.output) |
| self.assertIn("event=parser_message", output) |
| self.assertIn("event=parser_response_text", output) |
| self.assertIn("60 min", output) |
|
|
| def test_parse_check_in_with_ollama_validates_response(self): |
| fake_payload = { |
| "response": json.dumps( |
| { |
| "check_in": { |
| "raw_text": "60 min", |
| "time_available_minutes": 60, |
| "pain_or_injury": "unsure", |
| } |
| } |
| ) |
| } |
|
|
| with patch( |
| "training_coach.parser_ollama.request.urlopen", |
| return_value=_FakeResponse(fake_payload), |
| ): |
| parsed = parse_check_in_with_ollama("60 min") |
|
|
| self.assertEqual(parsed.check_in.time_available_minutes, 60) |
|
|
|
|
| if __name__ == "__main__": |
| unittest.main() |
|
|