docs(milestone): complete v0.2-ai-tutor-architecture
---ci--- phase: 7 milestone: v0.2 status: complete requirements: covered: [REQ-2-001, REQ-2-002, REQ-2-003, REQ-2-004, REQ-2-005, REQ-2-006, REQ-2-007, REQ-2-008, REQ-2-009, REQ-2-010, REQ-2-011, REQ-2-012] partial: [] ---/ci--- Milestone v0.2 (ai-tutor-architecture) merged to main. Escalation record (audit remediation, durable): P1 executor delegation failed twice (empty subagent results, zero files created); auto-resolved at full autonomy to inline execution with identical plan fidelity (commit 3271373, reflog-only after phase branch squash-delete).
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
"""MockProvider tests — determinism, JSON mode, failure modes."""
|
||||
|
||||
import pytest
|
||||
|
||||
from ai_service.llm.mock import MockProvider
|
||||
from ai_service.llm.types import Message
|
||||
|
||||
MSGS = [Message(role="user", content="hello there")]
|
||||
|
||||
|
||||
async def test_stream_is_deterministic():
|
||||
p1, p2 = MockProvider(), MockProvider()
|
||||
out1 = [t async for t in p1.stream_chat(MSGS, model="m")]
|
||||
out2 = [t async for t in p2.stream_chat(MSGS, model="m")]
|
||||
assert "".join(out1) == "".join(out2)
|
||||
assert out1 == out2
|
||||
|
||||
|
||||
async def test_stream_content_differs_for_different_input():
|
||||
p = MockProvider()
|
||||
a = "".join([t async for t in p.stream_chat(MSGS, model="m")])
|
||||
b = "".join(
|
||||
[t async for t in p.stream_chat([Message(role="user", content="other")], model="m")]
|
||||
)
|
||||
assert a != b
|
||||
|
||||
|
||||
async def test_json_object_response_format():
|
||||
import json
|
||||
|
||||
p = MockProvider()
|
||||
out = "".join(
|
||||
[
|
||||
t
|
||||
async for t in p.stream_chat(
|
||||
MSGS, model="m", response_format={"type": "json_object"}
|
||||
)
|
||||
]
|
||||
)
|
||||
assert json.loads(out) == {"summary": "mock structured reply", "confidence": 0.87}
|
||||
|
||||
|
||||
async def test_fail_before_first_token():
|
||||
p = MockProvider()
|
||||
p.fail_before_first_token = True
|
||||
with pytest.raises(RuntimeError):
|
||||
async for _ in p.stream_chat(MSGS, model="m"):
|
||||
pass
|
||||
|
||||
|
||||
async def test_fail_mid_stream():
|
||||
p = MockProvider()
|
||||
p.fail_mid_stream_at_index = 2
|
||||
tokens = []
|
||||
with pytest.raises(RuntimeError):
|
||||
async for t in p.stream_chat(MSGS, model="m"):
|
||||
tokens.append(t)
|
||||
assert len(tokens) == 2
|
||||
|
||||
|
||||
async def test_cancellation_records_abort():
|
||||
p = MockProvider()
|
||||
gen = p.stream_chat(MSGS, model="m")
|
||||
await gen.__anext__()
|
||||
await gen.aclose()
|
||||
assert p.abort_recorded is True
|
||||
|
||||
|
||||
async def test_chat_returns_full_reply():
|
||||
p = MockProvider()
|
||||
reply = await p.chat(MSGS, model="m")
|
||||
streamed = "".join([t async for t in p.stream_chat(MSGS, model="m")])
|
||||
assert reply == streamed
|
||||
Reference in New Issue
Block a user