/ tests / gateway / test_sticker_cache.py
test_sticker_cache.py
  1  """Tests for gateway/sticker_cache.py — sticker description cache."""
  2  
  3  import json
  4  import time
  5  from unittest.mock import patch
  6  
  7  from gateway.sticker_cache import (
  8      _load_cache,
  9      _save_cache,
 10      get_cached_description,
 11      cache_sticker_description,
 12      build_sticker_injection,
 13      build_animated_sticker_injection,
 14      STICKER_VISION_PROMPT,
 15  )
 16  
 17  
 18  class TestLoadSaveCache:
 19      def test_load_missing_file(self, tmp_path):
 20          with patch("gateway.sticker_cache.CACHE_PATH", tmp_path / "nope.json"):
 21              assert _load_cache() == {}
 22  
 23      def test_load_corrupt_file(self, tmp_path):
 24          bad_file = tmp_path / "bad.json"
 25          bad_file.write_text("not json{{{")
 26          with patch("gateway.sticker_cache.CACHE_PATH", bad_file):
 27              assert _load_cache() == {}
 28  
 29      def test_save_and_load_roundtrip(self, tmp_path):
 30          cache_file = tmp_path / "cache.json"
 31          data = {"abc123": {"description": "A cat", "emoji": "", "set_name": "", "cached_at": 1.0}}
 32          with patch("gateway.sticker_cache.CACHE_PATH", cache_file):
 33              _save_cache(data)
 34              loaded = _load_cache()
 35          assert loaded == data
 36  
 37      def test_save_creates_parent_dirs(self, tmp_path):
 38          cache_file = tmp_path / "sub" / "dir" / "cache.json"
 39          with patch("gateway.sticker_cache.CACHE_PATH", cache_file):
 40              _save_cache({"key": "value"})
 41          assert cache_file.exists()
 42  
 43  
 44  class TestCacheSticker:
 45      def test_cache_and_retrieve(self, tmp_path):
 46          cache_file = tmp_path / "cache.json"
 47          with patch("gateway.sticker_cache.CACHE_PATH", cache_file):
 48              cache_sticker_description("uid_1", "A happy dog", emoji="🐕", set_name="Dogs")
 49              result = get_cached_description("uid_1")
 50  
 51          assert result is not None
 52          assert result["description"] == "A happy dog"
 53          assert result["emoji"] == "🐕"
 54          assert result["set_name"] == "Dogs"
 55          assert "cached_at" in result
 56  
 57      def test_missing_sticker_returns_none(self, tmp_path):
 58          cache_file = tmp_path / "cache.json"
 59          with patch("gateway.sticker_cache.CACHE_PATH", cache_file):
 60              result = get_cached_description("nonexistent")
 61          assert result is None
 62  
 63      def test_overwrite_existing(self, tmp_path):
 64          cache_file = tmp_path / "cache.json"
 65          with patch("gateway.sticker_cache.CACHE_PATH", cache_file):
 66              cache_sticker_description("uid_1", "Old description")
 67              cache_sticker_description("uid_1", "New description")
 68              result = get_cached_description("uid_1")
 69  
 70          assert result["description"] == "New description"
 71  
 72      def test_multiple_stickers(self, tmp_path):
 73          cache_file = tmp_path / "cache.json"
 74          with patch("gateway.sticker_cache.CACHE_PATH", cache_file):
 75              cache_sticker_description("uid_1", "Cat")
 76              cache_sticker_description("uid_2", "Dog")
 77              r1 = get_cached_description("uid_1")
 78              r2 = get_cached_description("uid_2")
 79  
 80          assert r1["description"] == "Cat"
 81          assert r2["description"] == "Dog"
 82  
 83  
 84  class TestBuildStickerInjection:
 85      def test_exact_format_no_context(self):
 86          result = build_sticker_injection("A cat waving")
 87          assert result == '[The user sent a sticker~ It shows: "A cat waving" (=^.w.^=)]'
 88  
 89      def test_exact_format_emoji_only(self):
 90          result = build_sticker_injection("A cat", emoji="😀")
 91          assert result == '[The user sent a sticker 😀~ It shows: "A cat" (=^.w.^=)]'
 92  
 93      def test_exact_format_emoji_and_set_name(self):
 94          result = build_sticker_injection("A cat", emoji="😀", set_name="MyPack")
 95          assert result == '[The user sent a sticker 😀 from "MyPack"~ It shows: "A cat" (=^.w.^=)]'
 96  
 97      def test_set_name_without_emoji_ignored(self):
 98          """set_name alone (no emoji) produces no context — only emoji+set_name triggers 'from' clause."""
 99          result = build_sticker_injection("A cat", set_name="MyPack")
100          assert result == '[The user sent a sticker~ It shows: "A cat" (=^.w.^=)]'
101          assert "MyPack" not in result
102  
103      def test_description_with_quotes(self):
104          result = build_sticker_injection('A "happy" dog')
105          assert '"A \\"happy\\" dog"' not in result  # no escaping happens
106          assert 'A "happy" dog' in result
107  
108      def test_empty_description(self):
109          result = build_sticker_injection("")
110          assert result == '[The user sent a sticker~ It shows: "" (=^.w.^=)]'
111  
112  
113  class TestBuildAnimatedStickerInjection:
114      def test_exact_format_with_emoji(self):
115          result = build_animated_sticker_injection(emoji="🎉")
116          assert result == (
117              "[The user sent an animated sticker 🎉~ "
118              "I can't see animated ones yet, but the emoji suggests: 🎉]"
119          )
120  
121      def test_exact_format_without_emoji(self):
122          result = build_animated_sticker_injection()
123          assert result == "[The user sent an animated sticker~ I can't see animated ones yet]"
124  
125      def test_empty_emoji_same_as_no_emoji(self):
126          result = build_animated_sticker_injection(emoji="")
127          assert result == build_animated_sticker_injection()