123 lines
3.9 KiB
Python
123 lines
3.9 KiB
Python
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
import asyncio
|
||
|
|
|
||
|
|
import pytest
|
||
|
|
|
||
|
|
from yuxi.channels.adapters.wechat.debounce import WeChatQueueDebounce
|
||
|
|
from yuxi.channels.models import (
|
||
|
|
ChannelIdentity,
|
||
|
|
ChannelMessage,
|
||
|
|
ChannelType,
|
||
|
|
ChatType,
|
||
|
|
)
|
||
|
|
|
||
|
|
|
||
|
|
def _make_msg(content: str) -> ChannelMessage:
|
||
|
|
return ChannelMessage(
|
||
|
|
identity=ChannelIdentity(
|
||
|
|
channel_id="wechat",
|
||
|
|
channel_type=ChannelType.WECHAT,
|
||
|
|
channel_user_id="u1",
|
||
|
|
channel_chat_id="c1",
|
||
|
|
),
|
||
|
|
content=content,
|
||
|
|
)
|
||
|
|
|
||
|
|
|
||
|
|
class TestWeChatQueueDebounce:
|
||
|
|
@pytest.fixture
|
||
|
|
def debouncer(self) -> WeChatQueueDebounce:
|
||
|
|
return WeChatQueueDebounce(debounce_ms=100)
|
||
|
|
|
||
|
|
@pytest.mark.asyncio
|
||
|
|
async def test_single_message_flushed(self, debouncer):
|
||
|
|
results: list[list[ChannelMessage]] = []
|
||
|
|
|
||
|
|
async def handler(msgs):
|
||
|
|
results.append(msgs)
|
||
|
|
|
||
|
|
msg = _make_msg("hello")
|
||
|
|
await debouncer.enqueue("session-1", msg, handler)
|
||
|
|
await asyncio.sleep(0.3)
|
||
|
|
|
||
|
|
assert len(results) == 1
|
||
|
|
assert len(results[0]) == 1
|
||
|
|
assert results[0][0].content == "hello"
|
||
|
|
|
||
|
|
@pytest.mark.asyncio
|
||
|
|
async def test_multiple_messages_merged(self, debouncer):
|
||
|
|
results: list[list[ChannelMessage]] = []
|
||
|
|
|
||
|
|
async def handler(msgs):
|
||
|
|
results.append(msgs)
|
||
|
|
|
||
|
|
for content in ["m1", "m2", "m3"]:
|
||
|
|
await debouncer.enqueue("session-1", _make_msg(content), handler)
|
||
|
|
await asyncio.sleep(0.02)
|
||
|
|
|
||
|
|
await asyncio.sleep(0.3)
|
||
|
|
assert len(results) == 1
|
||
|
|
assert [m.content for m in results[0]] == ["m1", "m2", "m3"]
|
||
|
|
|
||
|
|
@pytest.mark.asyncio
|
||
|
|
async def test_different_sessions_not_merged(self, debouncer):
|
||
|
|
results: list[tuple[str, str]] = []
|
||
|
|
|
||
|
|
async def make_handler(sid: str):
|
||
|
|
async def handler(msgs):
|
||
|
|
for m in msgs:
|
||
|
|
results.append((sid, m.content))
|
||
|
|
return handler
|
||
|
|
|
||
|
|
await debouncer.enqueue("session-a", _make_msg("a1"), await make_handler("session-a"))
|
||
|
|
await debouncer.enqueue("session-b", _make_msg("b1"), await make_handler("session-b"))
|
||
|
|
await debouncer.enqueue("session-a", _make_msg("a2"), await make_handler("session-a"))
|
||
|
|
await asyncio.sleep(0.3)
|
||
|
|
|
||
|
|
a_msgs = [c for s, c in results if s == "session-a"]
|
||
|
|
b_msgs = [c for s, c in results if s == "session-b"]
|
||
|
|
assert a_msgs == ["a1", "a2"]
|
||
|
|
assert b_msgs == ["b1"]
|
||
|
|
|
||
|
|
@pytest.mark.asyncio
|
||
|
|
async def test_consecutive_enqueues_reset_timer(self, debouncer):
|
||
|
|
debouncer_long = WeChatQueueDebounce(debounce_ms=300)
|
||
|
|
results: list[list[ChannelMessage]] = []
|
||
|
|
|
||
|
|
async def handler(msgs):
|
||
|
|
results.append(msgs)
|
||
|
|
|
||
|
|
await debouncer_long.enqueue("s1", _make_msg("first"), handler)
|
||
|
|
await asyncio.sleep(0.1)
|
||
|
|
await debouncer_long.enqueue("s1", _make_msg("second"), handler)
|
||
|
|
await asyncio.sleep(0.1)
|
||
|
|
await debouncer_long.enqueue("s1", _make_msg("third"), handler)
|
||
|
|
await asyncio.sleep(0.15)
|
||
|
|
assert len(results) == 0
|
||
|
|
|
||
|
|
await asyncio.sleep(0.3)
|
||
|
|
assert len(results) == 1
|
||
|
|
assert [m.content for m in results[0]] == ["first", "second", "third"]
|
||
|
|
|
||
|
|
@pytest.mark.asyncio
|
||
|
|
async def test_flush_all_clears_pending(self, debouncer):
|
||
|
|
results: list[list[ChannelMessage]] = []
|
||
|
|
|
||
|
|
async def handler(msgs):
|
||
|
|
results.append(msgs)
|
||
|
|
|
||
|
|
await debouncer.enqueue("session-1", _make_msg("m1"), handler)
|
||
|
|
await debouncer.enqueue("session-2", _make_msg("m2"), handler)
|
||
|
|
await debouncer.flush_all()
|
||
|
|
assert len(results) == 0
|
||
|
|
|
||
|
|
@pytest.mark.asyncio
|
||
|
|
async def test_debounce_ms_property(self):
|
||
|
|
debouncer = WeChatQueueDebounce(debounce_ms=500)
|
||
|
|
assert debouncer.debounce_ms == 500
|
||
|
|
|
||
|
|
@pytest.mark.asyncio
|
||
|
|
async def test_default_debounce_value(self):
|
||
|
|
debouncer = WeChatQueueDebounce()
|
||
|
|
assert debouncer.debounce_ms == 500
|