Skip to content

Commit c140df8

Browse files
authored
test: run the README processing example against PostgreSQL (#4)
1 parent da4fc86 commit c140df8

1 file changed

Lines changed: 76 additions & 2 deletions

File tree

‎tests/test_readme_examples.py‎

Lines changed: 76 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,85 @@
22
import datetime as dt
33

44
from db_retry import Transaction, postgres_retry
5-
from sqlalchemy import func, insert, select
5+
from sqlalchemy import Table, func, insert, select, text
66
from sqlalchemy.ext.asyncio import AsyncEngine, AsyncSession, async_sessionmaker
77

88
from sqlalchemy_inbox import Inbox
9-
from tests.conftest import Tables
9+
from tests.conftest import Tables, count_rows
10+
11+
12+
@dataclasses.dataclass(kw_only=True, frozen=True, slots=True)
13+
class CreateChat:
14+
idempotency_id: str
15+
16+
17+
class ChatsRepository:
18+
def __init__(self, session: AsyncSession, table: Table, *, failures: int = 0) -> None:
19+
self._session = session
20+
self._table = table
21+
self._failures = failures
22+
self.calls = 0
23+
24+
async def create(self, command: CreateChat) -> None:
25+
self.calls += 1
26+
await self._session.execute(insert(self._table).values(message_id=command.idempotency_id))
27+
if self.calls <= self._failures:
28+
await self._session.execute(
29+
text("DO $$ BEGIN RAISE EXCEPTION 'injected' USING ERRCODE = 'serialization_failure'; END $$")
30+
)
31+
32+
33+
@dataclasses.dataclass(kw_only=True, frozen=True, slots=True)
34+
class CreateChatUseCase:
35+
transaction: Transaction
36+
inbox: Inbox
37+
chats_repository: ChatsRepository
38+
39+
@postgres_retry
40+
async def __call__(self, *, command: CreateChat) -> None:
41+
async with self.transaction:
42+
if not await self.inbox.claim(scope="chats.create-chat.v1", message_id=command.idempotency_id):
43+
return
44+
await self.chats_repository.create(command)
45+
await self.transaction.commit()
46+
47+
48+
def _create_chat_use_case(
49+
session_maker: async_sessionmaker[AsyncSession], tables: Tables, *, failures: int = 0
50+
) -> CreateChatUseCase:
51+
session = session_maker()
52+
return CreateChatUseCase(
53+
transaction=Transaction(session=session),
54+
inbox=Inbox(session, tables.inbox),
55+
chats_repository=ChatsRepository(session, tables.business, failures=failures),
56+
)
57+
58+
59+
async def test_readme_processing_use_case_retries_on_the_same_session(
60+
pg_engine: AsyncEngine, tables: Tables, session_maker: async_sessionmaker[AsyncSession]
61+
) -> None:
62+
use_case = _create_chat_use_case(session_maker, tables, failures=1)
63+
64+
await use_case(command=CreateChat(idempotency_id="m-1"))
65+
66+
assert use_case.chats_repository.calls == 2
67+
assert await count_rows(pg_engine, tables.inbox) == 1
68+
assert await count_rows(pg_engine, tables.business) == 1
69+
70+
71+
async def test_readme_processing_use_case_skips_a_duplicate_and_handles_the_next_message(
72+
pg_engine: AsyncEngine, tables: Tables, session_maker: async_sessionmaker[AsyncSession]
73+
) -> None:
74+
use_case = _create_chat_use_case(session_maker, tables)
75+
76+
await use_case(command=CreateChat(idempotency_id="m-1"))
77+
await use_case(command=CreateChat(idempotency_id="m-1"))
78+
await use_case(command=CreateChat(idempotency_id="m-2"))
79+
80+
assert use_case.chats_repository.calls == 2
81+
assert not use_case.transaction.session.in_transaction()
82+
assert await count_rows(pg_engine, tables.inbox) == 2
83+
assert await count_rows(pg_engine, tables.business) == 2
1084

1185

1286
@dataclasses.dataclass(kw_only=True, frozen=True, slots=True)

0 commit comments

Comments
 (0)