add spec system: sdd/bdd/tdd loops, trace enforcement, envelope+config specs
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
"""Unit coverage for SPEC-001 test-class requirements (ENV-10..12)."""
|
||||
|
||||
import json
|
||||
import time
|
||||
import unittest
|
||||
|
||||
from fjerkroa_bot.ai_responder import AIMessage, AIResponder
|
||||
|
||||
from .test_bdd_envelope import FakeModelResponder
|
||||
|
||||
|
||||
def entry(channel: str, text: str = "x"):
|
||||
return {"role": "user", "content": json.dumps({"message": text, "channel": channel})}
|
||||
|
||||
|
||||
class TestSystemPromptTemplate(unittest.TestCase):
|
||||
def test_template_substitution(self):
|
||||
"""ENV-10: {date}/{memory} substituted; missing news file leaves {news} literal."""
|
||||
config = {"system": "Date {date} memory {memory} news {news}", "history-limit": 5, "news": "/nonexistent/news.txt"}
|
||||
responder = AIResponder(config, "chat")
|
||||
responder.memory = "MEMSTR"
|
||||
messages = responder.message(AIMessage("alice", "hei"))
|
||||
system = messages[0]["content"]
|
||||
self.assertIn(time.strftime("%Y-%m-%d"), system)
|
||||
self.assertIn("MEMSTR", system)
|
||||
self.assertIn("{news}", system) # left literal — file does not exist
|
||||
|
||||
|
||||
class TestHistoryShrink(unittest.TestCase):
|
||||
def test_shrink_prefers_busy_channel(self):
|
||||
"""ENV-11: entry from the channel exceeding history-per-channel is removed first."""
|
||||
config = {"system": "s", "history-limit": 4}
|
||||
responder = AIResponder(config, "chat")
|
||||
responder.history = [entry("a", "a0"), entry("a", "a1"), entry("a", "a2"), entry("a", "a3"), entry("b", "b0")]
|
||||
responder.shrink_history_by_one()
|
||||
self.assertEqual(len(responder.history), 4)
|
||||
self.assertNotIn("a0", responder.history[0]["content"]) # oldest busy-channel entry gone
|
||||
|
||||
def test_shrink_falls_back_to_oldest(self):
|
||||
"""ENV-11: when no channel exceeds the cap, the oldest entry overall is removed."""
|
||||
config = {"system": "s", "history-limit": 4}
|
||||
responder = AIResponder(config, "chat")
|
||||
responder.history = [entry("a", "a0"), entry("b", "b0"), entry("c", "c0")]
|
||||
responder.shrink_history_by_one()
|
||||
self.assertEqual(len(responder.history), 2)
|
||||
self.assertNotIn("a0", responder.history[0]["content"])
|
||||
|
||||
|
||||
class TestRetriesExhausted(unittest.IsolatedAsyncioTestCase):
|
||||
async def test_send_raises_after_three_failures(self):
|
||||
"""ENV-12: three model failures -> RuntimeError (revision pending in FDB-004/D1)."""
|
||||
responder = FakeModelResponder({"system": "s", "history-limit": 5}, "chat")
|
||||
# scripted list empty -> chat() returns None every attempt
|
||||
with self.assertRaises(RuntimeError):
|
||||
await responder.send(AIMessage("alice", "hei", "chat"))
|
||||
self.assertEqual(responder.chat_calls, 3)
|
||||
Reference in New Issue
Block a user