CheddahBot/tests/test_db.py

113 lines
3.7 KiB
Python

"""Tests for the Database kv_scan and notifications methods."""
from __future__ import annotations
import json
class TestKvScan:
"""kv_scan is used to find all tracked ClickUp task states efficiently.
If it breaks, the scheduler can't find tasks to execute or recover."""
def test_returns_matching_pairs(self, tmp_db):
tmp_db.kv_set("clickup:task:abc:state", '{"state": "discovered"}')
tmp_db.kv_set("clickup:task:def:state", '{"state": "approved"}')
tmp_db.kv_set("other:key", "unrelated")
results = tmp_db.kv_scan("clickup:task:")
assert len(results) == 2
keys = {k for k, _ in results}
assert keys == {"clickup:task:abc:state", "clickup:task:def:state"}
def test_returns_empty_on_no_match(self, tmp_db):
tmp_db.kv_set("other:key", "value")
results = tmp_db.kv_scan("clickup:")
assert results == []
def test_prefix_is_exact_not_substring(self, tmp_db):
"""'click' should not match 'clickup:' prefix."""
tmp_db.kv_set("clickup:task:1:state", "data")
tmp_db.kv_set("clicked:something", "other")
results = tmp_db.kv_scan("clickup:")
assert len(results) == 1
assert results[0][0] == "clickup:task:1:state"
def test_values_are_returned_correctly(self, tmp_db):
state = json.dumps({"state": "completed", "task_name": "Test"})
tmp_db.kv_set("clickup:task:x:state", state)
results = tmp_db.kv_scan("clickup:task:x:")
assert len(results) == 1
parsed = json.loads(results[0][1])
assert parsed["state"] == "completed"
assert parsed["task_name"] == "Test"
class TestNotifications:
"""Notifications back the NotificationBus. If these break, no UI
gets informed about ClickUp task discoveries, completions, or failures."""
def test_add_and_retrieve(self, tmp_db):
nid = tmp_db.add_notification("Task discovered", "clickup")
assert nid >= 1
notifs = tmp_db.get_notifications_after(0)
assert len(notifs) == 1
assert notifs[0]["message"] == "Task discovered"
assert notifs[0]["category"] == "clickup"
def test_after_id_filters_correctly(self, tmp_db):
id1 = tmp_db.add_notification("First", "clickup")
_id2 = tmp_db.add_notification("Second", "clickup")
_id3 = tmp_db.add_notification("Third", "clickup")
# Should only get notifications after id1
notifs = tmp_db.get_notifications_after(id1)
assert len(notifs) == 2
assert notifs[0]["message"] == "Second"
assert notifs[1]["message"] == "Third"
def test_after_latest_returns_empty(self, tmp_db):
id1 = tmp_db.add_notification("Only one", "clickup")
notifs = tmp_db.get_notifications_after(id1)
assert notifs == []
def test_limit_is_respected(self, tmp_db):
for i in range(10):
tmp_db.add_notification(f"Msg {i}", "clickup")
notifs = tmp_db.get_notifications_after(0, limit=3)
assert len(notifs) == 3
def test_default_category(self, tmp_db):
tmp_db.add_notification("No category specified")
notifs = tmp_db.get_notifications_after(0)
assert notifs[0]["category"] == "clickup"
def test_created_at_is_populated(self, tmp_db):
tmp_db.add_notification("Timestamped")
notifs = tmp_db.get_notifications_after(0)
assert notifs[0]["created_at"] is not None
assert len(notifs[0]["created_at"]) > 10 # ISO format
def test_ids_are_monotonically_increasing(self, tmp_db):
id1 = tmp_db.add_notification("A")
id2 = tmp_db.add_notification("B")
id3 = tmp_db.add_notification("C")
assert id1 < id2 < id3