from datetime import UTC, datetime, timedelta import pytest from app.db.models import ChangeOp, User from app.db.session import SessionMaker from app.repo.drafts import MAX_CHANGES, DraftFull, DraftService UID = 7 SHA = "a" * 40 class Clock: now = datetime(2026, 10, 15, tzinfo=UTC) def __call__(self) -> datetime: return self.now @pytest.fixture async def svc(sessions: SessionMaker) -> DraftService: async with sessions.begin() as s: s.add(User(tg_id=UID, full_name="u")) return DraftService(sessions, clock=Clock()) async def test_put_get_and_order(svc: DraftService) -> None: await svc.put(UID, SHA, "b.py", ChangeOp.ADD, b"b") await svc.put(UID, "other-sha", "a.py", ChangeOp.MODIFY, b"a") draft = await svc.get(UID) assert draft is not None and draft.base_sha == SHA # base fixed when the draft started assert [(c.path, c.op) for c in draft.changes] == [("b.py", ChangeOp.ADD), ("a.py", ChangeOp.MODIFY)] async def test_editing_a_new_file_keeps_it_added_and_deleting_it_drops_it(svc: DraftService) -> None: await svc.put(UID, SHA, "n.py", ChangeOp.ADD, b"1") await svc.put(UID, SHA, "n.py", ChangeOp.MODIFY, b"2") draft = await svc.get(UID) assert draft is not None and draft.changes[0].op is ChangeOp.ADD and draft.changes[0].content == b"2" await svc.put(UID, SHA, "n.py", ChangeOp.DELETE, None) draft = await svc.get(UID) assert draft is not None and draft.changes == [] async def test_drop_message_clear(svc: DraftService) -> None: await svc.put(UID, SHA, "a.py", ChangeOp.ADD, b"1") await svc.put(UID, SHA, "b.py", ChangeOp.DELETE, None) await svc.drop(UID, "a.py") await svc.set_message(UID, "feat: x") draft = await svc.get(UID) assert draft is not None and [c.path for c in draft.changes] == ["b.py"] and draft.message == "feat: x" await svc.clear(UID) assert await svc.get(UID) is None async def test_expired_draft_is_gone(svc: DraftService) -> None: await svc.put(UID, SHA, "a.py", ChangeOp.ADD, b"1") Clock.now += timedelta(hours=25) try: assert await svc.get(UID) is None finally: Clock.now -= timedelta(hours=25) async def test_draft_size_limit(svc: DraftService) -> None: for i in range(MAX_CHANGES): await svc.put(UID, SHA, f"f{i}.py", ChangeOp.ADD, b"") with pytest.raises(DraftFull): await svc.put(UID, SHA, "one-more.py", ChangeOp.ADD, b"")