Fastwhisper / tests /test_chat_plan.py
Mbonea's picture
Add ChatPlan paste/export loop with check-off and FSE annotations.
fb29daa
Raw
History Blame Contribute Delete
5.8 kB
"""ChatPlan mapper and import/export round-trip tests."""
from __future__ import annotations
from datetime import date
from app.chat_plan import (
ChatPlanError,
chat_plan_to_day_put,
plan_and_feedback_to_chat_plan,
)
from app.schedule_store import DayPlan, DayReview, ScheduledBlock
SAMPLE = {
"schema_version": 1,
"date": "2026-07-24",
"title": "Spain pack day",
"intention": "AXA + stay proof + hold PDF. No embassy. No flight pay.",
"constraints": ["wallet home on walk", "Weeknd only after proof"],
"blocks": [
{
"start": "08:00",
"end": "08:25",
"title": "Interrupt walk (no headphones)",
"priority": "P0",
"kind": "body_care",
"intent": "measure",
"notes": "Eyes outside.",
"status": "planned",
"review": {
"did": None,
"minutes": None,
"comment": "",
"emotions": [],
"fse_event": "",
"intensity": None,
},
},
{
"start": "09:00",
"end": "10:30",
"title": "Fix AXA residence Tanzania",
"priority": "P0",
"kind": "admin_spain",
"intent": "duty",
"notes": "26 Aug–5 Sep 2026; PDF",
"locked": True,
"status": "planned",
"review": {
"did": None,
"minutes": None,
"comment": "",
"emotions": [],
"fse_event": "",
"intensity": None,
},
},
],
"day_review": {
"comment": "",
"emotions": [],
"fse_events": "",
"what_moved": "",
"what_avoided": "",
"tomorrow_change": "",
},
}
def test_chat_to_put_basic() -> None:
put, fbs, warnings = chat_plan_to_day_put(SAMPLE, "2026-07-24")
assert put.title == "Spain pack day"
assert put.intention.startswith("AXA")
assert put.constraints == ["wallet home on walk", "Weeknd only after proof"]
assert len(put.blocks) == 2
assert put.blocks[0].planned_min == 25
assert put.blocks[1].kind == "admin_spain"
assert put.source == "chat"
assert fbs == []
assert isinstance(warnings, list)
def test_date_mismatch() -> None:
try:
chat_plan_to_day_put(SAMPLE, "2026-07-25")
assert False, "expected error"
except ChatPlanError as exc:
assert any("date mismatch" in e for e in exc.errors)
def test_empty_blocks() -> None:
try:
chat_plan_to_day_put({"blocks": []}, "2026-07-24")
assert False
except ChatPlanError:
pass
def test_unknown_kind_coerced() -> None:
chat = {
"blocks": [
{
"start": "10:00",
"end": "10:30",
"title": "Weird",
"kind": "embassy_run",
"priority": "P1",
}
]
}
put, _, warnings = chat_plan_to_day_put(chat, "2026-07-24")
assert put.blocks[0].kind == "other"
assert "[kind:embassy_run]" in put.blocks[0].notes
assert any("embassy_run" in w for w in warnings)
def test_overlap_validation() -> None:
chat = {
"blocks": [
{"start": "09:00", "end": "10:00", "title": "A", "kind": "other"},
{"start": "09:30", "end": "10:30", "title": "B", "kind": "other"},
]
}
try:
chat_plan_to_day_put(chat, "2026-07-24")
assert False
except ChatPlanError as exc:
assert any("overlap" in e for e in exc.errors)
def test_export_round_trip_fields() -> None:
put, _, _ = chat_plan_to_day_put(SAMPLE, "2026-07-24")
plan = DayPlan(
date="2026-07-24",
blocks=put.blocks,
title=put.title or "",
intention=put.intention or "",
constraints=put.constraints or [],
day_review=put.day_review or DayReview(),
source="chat",
)
# Annotate first block
fb = {
put.blocks[0].id: {
"block_id": put.blocks[0].id,
"did": "done",
"actual_min": 20,
"note": "walked",
"emotions": ["calm"],
"fse_event": "",
"intensity": 3,
}
}
plan.blocks[0] = plan.blocks[0].model_copy(update={"status": "done"})
out = plan_and_feedback_to_chat_plan(plan, fb)
assert out["title"] == "Spain pack day"
assert out["intention"].startswith("AXA")
assert out["blocks"][0]["review"]["comment"] == "walked"
assert out["blocks"][0]["review"]["emotions"] == ["calm"]
assert out["summary"]["blocks_total"] == 2
assert out["summary"]["blocks_done"] == 1
assert out["summary"]["p0_total"] == 2
assert out["summary"]["p0_done"] == 1
assert out["summary"]["actual_min_sum"] == 20
def test_old_plan_without_day_review_loads() -> None:
plan = DayPlan.model_validate(
{
"date": "2026-01-01",
"blocks": [],
"source": "user",
}
)
assert plan.day_review.comment == ""
assert plan.title == ""
assert plan.constraints == []
def test_review_did_sets_status() -> None:
chat = {
"blocks": [
{
"start": "11:00",
"end": "11:30",
"title": "Done already",
"kind": "other",
"status": "planned",
"review": {"did": "done", "minutes": 25, "comment": "ok", "emotions": ["hope"]},
}
]
}
put, fbs, _ = chat_plan_to_day_put(chat, "2026-07-24")
assert put.blocks[0].status == "done"
assert len(fbs) == 1
assert fbs[0].emotions == ["hope"]
assert fbs[0].actual_min == 25