"""Unit tests for lore_commands' pure compaction transforms. Heavy imports (discord, ai_functions, constants side effects) are stubbed only when genuinely absent, matching the pytest-only unit CI job. """ import sys import types def _stub_if_missing(name, build): if name in sys.modules: return try: __import__(name) except ImportError: sys.modules[name] = build() def _build_discord(): mod = types.ModuleType("discord") class Object: def __init__(self, id=None): # noqa: A002 self.id = id class Member: pass mod.Object = Object mod.Member = Member ext = types.ModuleType("discord.ext") commands = types.ModuleType("discord.ext.commands") class Cog: pass def _passthrough(*_a, **_k): return lambda fn: fn commands.Cog = Cog commands.hybrid_command = _passthrough tasks = types.ModuleType("discord.ext.tasks") commands_tasks = types.ModuleType("_") def loop(**_k): def deco(fn): fn.before_loop = lambda f: f return fn return deco tasks.loop = loop commands.tasks = tasks ext.commands = commands ext.tasks = tasks mod.ext = ext sys.modules["discord.ext"] = ext sys.modules["discord.ext.commands"] = commands sys.modules["discord.ext.tasks"] = tasks return mod def _build_ai_functions(): mod = types.ModuleType("ai_functions") async def handle_response(*_a, **_k): return ("legenda stub", []) mod.handle_response = handle_response return mod _stub_if_missing("discord", _build_discord) _stub_if_missing("ai_functions", _build_ai_functions) import lore_commands # noqa: E402 (after stubbing) def _msgs(n): return [{"role": "user" if i % 2 == 0 else "assistant", "content": f"m{i}"} for i in range(n)] def test_apply_compaction_replaces_prefix_keeps_tail(): messages = _msgs(10) result = lore_commands.apply_compaction(messages, old_count=6, legend_text="LEGENDA") # one legend system-message, then the last 4 verbatim assert result[0]["role"] == "system" assert result[0]["content"].endswith("LEGENDA") assert [m["content"] for m in result[1:]] == ["m6", "m7", "m8", "m9"] assert len(result) == 5 def test_apply_compaction_preserves_appends_made_during_summarize(): # Simulate: we decided old_count on a 10-message file, but by write time two # more messages were appended (a chat happened during the AI call). Those # tail appends must survive. grown = _msgs(10) + [{"role": "user", "content": "new1"}, {"role": "assistant", "content": "new2"}] result = lore_commands.apply_compaction(grown, old_count=6, legend_text="L") tail = [m["content"] for m in result[1:]] assert tail == ["m6", "m7", "m8", "m9", "new1", "new2"] def test_build_transcript_formats_lines(): text = lore_commands.build_transcript( [{"role": "user", "content": "cześć"}, {"role": "assistant", "content": "no elo"}] ) assert text == "user: cześć\nassistant: no elo" def test_build_transcript_skips_empty_and_truncates(): text = lore_commands.build_transcript( [{"role": "user", "content": "x" * 20000}, {"role": "assistant", "content": ""}], max_chars=1000, ) assert "[...urwane...]" in text assert len(text) <= 1000 + len("\n[...urwane...]\n")