"""Integration tests for learning and sending user-owned stickers.""" from pathlib import Path import sys import tempfile import unittest from types import SimpleNamespace from unittest.mock import AsyncMock, patch BASE = Path(__file__).resolve().parents[1] sys.path[:0] = [str(BASE / "bot-utility"), str(BASE)] import main from chat import engine class FakeStore: def __init__(self, item=None): self.item = item self.learned = [] self.random_calls = [] def learn(self, scope, source, classification): self.learned.append((scope, source, classification)) return {"id": "learned"} def random_for_scene(self, scope, scene): self.random_calls.append((scope, scene)) return self.item class StickerLearningTests(unittest.IsolatedAsyncioTestCase): async def test_only_marked_stickers_are_learned(self): store = FakeStore() images = [ {"kind": "sticker", "source_id": "s1", "original_path": "/tmp/a.webp"}, {"kind": "photo", "source_id": "s2", "original_path": "/tmp/b.jpg"}, ] parsed = {"learned_stickers": [ {"source_id": "s1", "accept": True, "scenes": ["sad"], "description": "委屈"}, {"source_id": "s2", "accept": True, "scenes": ["happy"], "description": "普通照片"}, ]} with patch.object(engine, "STICKER_STORE", store), \ patch.object(engine, "stickers_enabled", return_value=True): engine._learn_stickers(1001, images, parsed) self.assertEqual(len(store.learned), 1) self.assertEqual(store.learned[0][0], engine.STICKER_GLOBAL_SCOPE) self.assertEqual(store.learned[0][2]["scenes"], ["sad"]) async def test_source_marker_preserves_user_instruction(self): text = engine._with_sticker_sources( "这个适合什么时候用", [{"kind": "sticker", "source_id": "s1"}, {"kind": "photo"}], ) self.assertIn("source_id=s1", text) self.assertTrue(text.endswith("这个适合什么时候用")) async def test_random_candidate_requires_visual_confirmation(self): with tempfile.TemporaryDirectory() as directory: original = Path(directory, "a.webp") preview = Path(directory, "a.png") original.write_bytes(b"sticker") preview.write_bytes(b"preview") item = {"id": "a", "path": str(original), "preview_path": str(preview)} store = FakeStore(item) bot = SimpleNamespace(send_sticker=AsyncMock()) channel = SimpleNamespace(bot=bot, _to_raw_target=lambda raw: raw) with patch.object(engine, "STICKER_STORE", store), \ patch.object(engine, "stickers_enabled", return_value=True), \ patch.object(engine, "_resolve_delivery", return_value=(channel, 10001)), \ patch.object(engine, "_confirm_sticker", new=AsyncMock(return_value=False)): sent = await engine._maybe_send_sticker(channel, 1, "sad", "context") self.assertFalse(sent) self.assertEqual(store.random_calls, [(engine.STICKER_GLOBAL_SCOPE, "sad")]) bot.send_sticker.assert_not_awaited() async def test_confirmation_uses_candidate_preview(self): with tempfile.TemporaryDirectory() as directory: preview = Path(directory, "preview.png") preview.write_bytes(b"preview") model = AsyncMock(return_value='{"send":true}') with patch.object(engine, "call_deepseek", new=model): approved = await engine._confirm_sticker( {"preview_path": str(preview), "description": "轻微委屈"}, "sad", "用户有点失落", ) self.assertTrue(approved) self.assertEqual(model.await_args.kwargs["image_infos"][0]["url"], str(preview)) class TelegramStickerInputTests(unittest.IsolatedAsyncioTestCase): async def test_tg_sticker_is_downloaded_and_forwarded_as_sticker(self): sticker = SimpleNamespace( is_animated=False, is_video=False, file_unique_id="unique-1", emoji="🙂", set_name="set-1", ) message = SimpleNamespace( from_user=SimpleNamespace(id=123, is_bot=False), chat=SimpleNamespace(id=123, type="private"), text=None, caption=None, photo=[], sticker=sticker, entities=[], ) async def download(obj, destination): Path(destination).write_bytes(b"sticker") bot = SimpleNamespace(download=download, id=999) dispatch = AsyncMock() with patch.object(main, "handle_message", new=dispatch): await main.make_tg_handler(SimpleNamespace(tg_username="bot"))(message, bot) images = dispatch.await_args.args[5] self.assertEqual(len(images), 1) self.assertEqual(images[0]["kind"], "sticker") self.assertEqual(images[0]["source_id"], "s1") self.assertEqual(images[0]["file_unique_id"], "unique-1") self.assertEqual(images[0]["format"], "static") self.assertTrue(Path(images[0]["original_path"]).exists()) Path(images[0]["original_path"]).unlink() if __name__ == "__main__": unittest.main()