test: structured CannedQueue fixture builder for classifier mocks (T116)
Phase 4.5 carry-over from Phase 3. Tests across test_turn_flow.py, test_meanwhile_turn_flow.py, and the phase3/4 integration suites built positional canned-response arrays for MockLLMClient — adding a new classifier call to a code path required updating the array index in many places. This change ships tests/fixtures.py with a fluent CannedQueue builder that lets tests declare classifier expectations by name and call order instead of by index. Each method appends one item to an internal queue and returns self for chaining; build() emits the flat list[str] queue that MockLLMClient(canned=...) already consumes. The mock's contract is unchanged. Builder methods cover: parse_turn, detect_addressee, state_update (with zero_state alias), detect_interjection, detect_interjection_targeted, detect_scene_close, detect_event_transitions, summarize_scene_pov, detect_threads, meanwhile_digest, score_significance, and a narrative() helper for streaming bot beats. raw() is a passthrough escape hatch. Migration scope: ship the builder + 2 sanity tests + migrate 3 representative tests in test_turn_flow.py as proof of concept (test_single_bot_turn_no_guest_regression, test_turn_with_event_transition_appends_started_event, test_turn_with_no_active_events_skips_classifier). The remaining positional-array tests stay as-is; the builder docstring documents the migration template for Phase 5 work.
This commit is contained in:
+40
-32
@@ -22,6 +22,7 @@ from chat.db.connection import open_db
|
||||
from chat.eventlog.log import append_and_apply, append_event
|
||||
from chat.eventlog.projector import project
|
||||
from chat.llm.mock import MockLLMClient
|
||||
from tests.fixtures import CannedQueue
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@@ -362,14 +363,20 @@ def test_single_bot_turn_no_guest_regression(app_state_setup, tmp_path):
|
||||
the chat has no guest, so ``detect_interjection`` is NOT invoked.
|
||||
Ends with one user_turn, one assistant_turn, two edge_updates, and a
|
||||
single ``memory_written``.
|
||||
|
||||
T116: migrated to :class:`tests.fixtures.CannedQueue` as a proof of
|
||||
concept for the structured canned-queue builder.
|
||||
"""
|
||||
_seed(tmp_path / "test.db")
|
||||
canned_parse = json.dumps(
|
||||
{"segments": [{"kind": "dialogue", "text": "hello"}]}
|
||||
)
|
||||
mock = _override_llm(
|
||||
[canned_parse, "Hi there.", _zero_state(), _zero_state()]
|
||||
canned = (
|
||||
CannedQueue()
|
||||
.parse_turn(segments=[{"kind": "dialogue", "text": "hello"}])
|
||||
.narrative("Hi there.")
|
||||
.state_update()
|
||||
.state_update()
|
||||
.build()
|
||||
)
|
||||
mock = _override_llm(canned)
|
||||
try:
|
||||
response = app_state_setup.post(
|
||||
"/chats/chat_bot_a/turns", data={"prose": "hello"}
|
||||
@@ -979,29 +986,25 @@ def test_turn_with_event_transition_appends_started_event(
|
||||
},
|
||||
)
|
||||
|
||||
canned_parse = json.dumps(
|
||||
{"segments": [{"kind": "dialogue", "text": "they arrived"}]}
|
||||
)
|
||||
canned_event_decision = json.dumps(
|
||||
{
|
||||
"transitions": [
|
||||
{
|
||||
"event_id": "evt_1",
|
||||
"new_status": "active",
|
||||
"reason": "they arrived",
|
||||
}
|
||||
]
|
||||
}
|
||||
)
|
||||
mock = _override_llm(
|
||||
[
|
||||
canned_parse,
|
||||
"They walk in.",
|
||||
_zero_state(),
|
||||
_zero_state(),
|
||||
canned_event_decision,
|
||||
]
|
||||
# T116: migrated to :class:`tests.fixtures.CannedQueue`.
|
||||
canned = (
|
||||
CannedQueue()
|
||||
.parse_turn(segments=[{"kind": "dialogue", "text": "they arrived"}])
|
||||
.narrative("They walk in.")
|
||||
.state_update()
|
||||
.state_update()
|
||||
.detect_event_transitions(
|
||||
[
|
||||
{
|
||||
"event_id": "evt_1",
|
||||
"new_status": "active",
|
||||
"reason": "they arrived",
|
||||
}
|
||||
]
|
||||
)
|
||||
.build()
|
||||
)
|
||||
mock = _override_llm(canned)
|
||||
try:
|
||||
response = app_state_setup.post(
|
||||
"/chats/chat_bot_a/turns", data={"prose": "they arrived"}
|
||||
@@ -1155,18 +1158,23 @@ def test_turn_with_no_active_events_skips_classifier(app_state_setup, tmp_path):
|
||||
short-circuits without an LLM call (per T52). The canned queue must
|
||||
therefore have ZERO event-detection slots — same shape as the
|
||||
Phase 2 no-guest baseline.
|
||||
|
||||
T116: migrated to :class:`tests.fixtures.CannedQueue`.
|
||||
"""
|
||||
_seed(tmp_path / "test.db")
|
||||
|
||||
canned_parse = json.dumps(
|
||||
{"segments": [{"kind": "dialogue", "text": "hello"}]}
|
||||
)
|
||||
# Only 4 slots: parse + narrative + 2 state-updates. NO extra slot for
|
||||
# event-detection — non-existent active_events causes the helper to
|
||||
# short-circuit before pulling from the queue.
|
||||
mock = _override_llm(
|
||||
[canned_parse, "Hi there.", _zero_state(), _zero_state()]
|
||||
canned = (
|
||||
CannedQueue()
|
||||
.parse_turn(segments=[{"kind": "dialogue", "text": "hello"}])
|
||||
.narrative("Hi there.")
|
||||
.state_update()
|
||||
.state_update()
|
||||
.build()
|
||||
)
|
||||
mock = _override_llm(canned)
|
||||
try:
|
||||
response = app_state_setup.post(
|
||||
"/chats/chat_bot_a/turns", data={"prose": "hello"}
|
||||
|
||||
Reference in New Issue
Block a user