mirror of
https://github.com/hansjone/oclaw.git
synced 2026-10-09 03:30:48 +08:00
Claim pending rows atomically and serialize the sidecar poller so overlapping polls cannot send the same reply twice; also stop inbound queue delivery from falling through to a second enqueue. Co-authored-by: Cursor <cursoragent@cursor.com>
79 lines
3 KiB
Python
79 lines
3 KiB
Python
from __future__ import annotations
|
|
|
|
from svc.persistence.sqlite_store import SqliteStore
|
|
|
|
|
|
def _row(store: SqliteStore, msg_id: str) -> dict:
|
|
with store._connect() as conn: # noqa: SLF001 - test introspection
|
|
got = conn.execute(
|
|
"""
|
|
SELECT id, status, error, send_attempts, next_attempt_at, last_attempt_at
|
|
FROM channel_outbound_message
|
|
WHERE id = ?
|
|
""",
|
|
(msg_id,),
|
|
).fetchone()
|
|
assert got is not None
|
|
return dict(got)
|
|
|
|
|
|
def test_channel_outbound_retry_transitions(tmp_path) -> None:
|
|
store = SqliteStore(str(tmp_path / "ops.sqlite"))
|
|
msg_id = store.enqueue_channel_outbound_message(
|
|
channel="whatsapp",
|
|
account_id="wa-default",
|
|
chat_id="8615601877957@s.whatsapp.net",
|
|
text="hello",
|
|
)
|
|
|
|
assert store.ack_channel_outbound_message(message_id=msg_id, ok=False, error="net down") is True
|
|
first = _row(store, msg_id)
|
|
assert first["status"] == "pending"
|
|
assert int(first["send_attempts"] or 0) == 1
|
|
assert str(first["next_attempt_at"] or "").strip()
|
|
|
|
assert store.ack_channel_outbound_message(message_id=msg_id, ok=False, error="still down") is True
|
|
assert store.ack_channel_outbound_message(message_id=msg_id, ok=False, error="final fail") is True
|
|
final = _row(store, msg_id)
|
|
assert final["status"] == "failed"
|
|
assert int(final["send_attempts"] or 0) == 3
|
|
assert not str(final["next_attempt_at"] or "").strip()
|
|
|
|
|
|
def test_channel_outbound_retry_success_clears_schedule(tmp_path) -> None:
|
|
store = SqliteStore(str(tmp_path / "ops.sqlite"))
|
|
msg_id = store.enqueue_channel_outbound_message(
|
|
channel="whatsapp",
|
|
account_id="wa-default",
|
|
chat_id="8615601877957@s.whatsapp.net",
|
|
text="hello",
|
|
)
|
|
assert store.ack_channel_outbound_message(message_id=msg_id, ok=False, error="temp") is True
|
|
assert store.ack_channel_outbound_message(message_id=msg_id, ok=True, stanza_id="stanza-1") is True
|
|
row = _row(store, msg_id)
|
|
assert row["status"] == "sent"
|
|
assert int(row["send_attempts"] or 0) == 2
|
|
assert not str(row["next_attempt_at"] or "").strip()
|
|
|
|
|
|
def test_claim_pending_channel_outbound_messages_is_exclusive(tmp_path) -> None:
|
|
store = SqliteStore(str(tmp_path / "ops.sqlite"))
|
|
msg_id = store.enqueue_channel_outbound_message(
|
|
channel="whatsapp",
|
|
account_id="wa-default",
|
|
chat_id="120363012345678@g.us",
|
|
text="once only",
|
|
)
|
|
first = store.claim_pending_channel_outbound_messages(
|
|
channel="whatsapp", account_id="wa-default", limit=5
|
|
)
|
|
second = store.claim_pending_channel_outbound_messages(
|
|
channel="whatsapp", account_id="wa-default", limit=5
|
|
)
|
|
assert len(first) == 1
|
|
assert first[0]["id"] == msg_id
|
|
assert second == []
|
|
row = _row(store, msg_id)
|
|
assert row["status"] == "sending"
|
|
assert store.ack_channel_outbound_message(message_id=msg_id, ok=True, stanza_id="S1") is True
|
|
assert _row(store, msg_id)["status"] == "sent"
|