Files
code_of_conquest_dnd/api/tests/test_call_log.py
Phillip Tarrant f7c51b79da fix(api): best-effort call logging + non-JSON 200 → ModelError (final-review)
- call_log._default_write: swallow any exception from the default file/
  stdout sink and report to stderr, so a log-write failure (disk full,
  bad permissions, misconfigured CALL_LOG_PATH) never turns a successful
  narration into a 500 (charter §13). Injected write= sinks (used by
  tests) are left to surface their own errors.
- ollama_client.chat: catch ValueError alongside httpx.HTTPError in the
  retry loop so a 200 response with a non-JSON body (JSONDecodeError is
  a ValueError subclass) counts as a failed attempt and falls through to
  ModelError after the one retry, instead of escaping chat() uncaught
  (charter §12 — one retry, then ModelError, nothing else escapes).
- main.py: update stale docstrings — /dm/narrate is now fully wired
  (routing + model call + logging); the other four roles remain stubs.

Regression tests added for both fixes (TDD: watched RED, then GREEN).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-10 08:45:39 -05:00

57 lines
1.9 KiB
Python

import json
from app.call_log import record
def test_success_record_shape():
captured = []
rec = record(
role="narrator", model="qwen3.5:latest", options={"seed": 7},
messages=[{"role": "user", "content": "x"}], canon_log={"a": 1},
ok=True, latency_ms=120, response="prose", write=captured.append,
)
assert len(captured) == 1
parsed = json.loads(captured[0])
assert parsed["ok"] is True
assert parsed["response"] == "prose"
assert parsed["options"]["seed"] == 7
assert parsed["role"] == "narrator"
assert parsed["canon_log"] == {"a": 1}
assert "error" not in parsed
assert "ts" in parsed
assert rec["response"] == "prose"
def test_failure_record_shape():
captured = []
record(
role="narrator", model="m", options={}, messages=[], canon_log={},
ok=False, latency_ms=5, error="ModelError: down", write=captured.append,
)
parsed = json.loads(captured[0])
assert parsed["ok"] is False
assert parsed["error"] == "ModelError: down"
assert "response" not in parsed
def test_default_write_failure_does_not_propagate(monkeypatch, capsys):
"""§13 — a good narration must never become a 500 because the log write
failed. Point CALL_LOG_PATH at a path whose parent directory does not
exist, so the default sink's `open()` raises. record() must swallow it,
report to stderr (never stdout — stdout may be the log sink itself), and
still return the record.
"""
monkeypatch.setenv(
"CALL_LOG_PATH", "/nonexistent-dir-for-test/does/not/exist.log"
)
rec = record(
role="narrator", model="qwen3.5:latest", options={"seed": 7},
messages=[{"role": "user", "content": "x"}], canon_log={"a": 1},
ok=True, latency_ms=120, response="prose",
)
assert rec["response"] == "prose"
assert rec["ok"] is True
out, err = capsys.readouterr()
assert out == ""
assert err != ""