from datetime import datetime, timezone import pytest from app import crud def _now(): return datetime.now(timezone.utc) def test_create_item_sets_defaults(db_session): item, created = crud.create_item( db_session, modality="bh_email", raw_content="Hi, I have books to donate", captured_at=_now(), discord_message_id="msg-1", ) assert created is True assert item.id is not None assert item.stage == "new" assert item.type is None def test_create_item_is_idempotent_by_discord_message_id(db_session): first, first_created = crud.create_item( db_session, modality="bh_email", raw_content="a", captured_at=_now(), discord_message_id="dup-1", ) second, second_created = crud.create_item( db_session, modality="bh_email", raw_content="b (should be ignored)", captured_at=_now(), discord_message_id="dup-1", ) assert first_created is True assert second_created is False assert second.id == first.id assert second.raw_content == "a" def test_list_items_excludes_done_by_default(db_session): crud.create_item(db_session, modality="voice", raw_content="x", captured_at=_now(), discord_message_id="m1") done_item, _ = crud.create_item(db_session, modality="voice", raw_content="y", captured_at=_now(), discord_message_id="m2") crud.update_item(db_session, done_item.id, type="other", stage="new") crud.update_item(db_session, done_item.id, stage="done") results = crud.list_items(db_session) assert done_item.id not in [i.id for i in results] results_with_done = crud.list_items(db_session, include_done=True) assert done_item.id in [i.id for i in results_with_done] def test_update_item_rejects_invalid_stage_for_type(db_session): item, _ = crud.create_item(db_session, modality="voice", raw_content="x", captured_at=_now(), discord_message_id="m3") crud.update_item(db_session, item.id, type="other") with pytest.raises(ValueError): crud.update_item(db_session, item.id, stage="pickup_scheduled") def test_update_item_returns_none_for_missing_id(db_session): assert crud.update_item(db_session, 9999, notes="x") is None def test_create_item_defaults_direction_to_inbound(db_session): item, _ = crud.create_item( db_session, modality="bh_email", raw_content="x", captured_at=_now(), discord_message_id="dir-1", ) assert item.direction == "inbound" def test_create_item_accepts_outbound_direction(db_session): item, _ = crud.create_item( db_session, modality="lead_found", raw_content="found post", captured_at=_now(), discord_message_id="dir-2", direction="outbound", ) assert item.direction == "outbound" def test_update_item_validates_stage_against_outbound_direction(db_session): item, _ = crud.create_item( db_session, modality="lead_found", raw_content="x", captured_at=_now(), discord_message_id="dir-3", direction="outbound", ) updated = crud.update_item(db_session, item.id, stage="outreach_sent") assert updated.stage == "outreach_sent" def test_update_item_rejects_inbound_only_stage_for_outbound_item(db_session): item, _ = crud.create_item( db_session, modality="lead_found", raw_content="x", captured_at=_now(), discord_message_id="dir-4", direction="outbound", ) with pytest.raises(ValueError): crud.update_item(db_session, item.id, stage="done")