Files
box/tests/test_agent_ranking.py
operator b7e45010c3 feat(tui): clean transcript style, right-click context menus, rate limits & dual copy
- Add clean line-by-line transcript style toggle ('b' key / /clean / /boxed)
- Implement right-click context menus for chat list, fleet agent panel, and transcript
- Add cooldown mode lock bypass (2-second double-confirm force sync)
- Implement dual copy support: clean text (strip reply metadata) vs full context
- Add clickable [📋 Copy] and [📑+ Context] buttons to message headers
- Add unit test suites for transcript cleaning, context menus, rate limits, and copy actions
2026-10-06 07:55:27 +00:00

123 lines
5.3 KiB
Python

#!/usr/bin/env python3
"""
test_agent_ranking.py — Unit test suite for Fleet Agent interaction ranking & sorting:
1. Agent interaction recording on true input / chat [insert].
2. Fleet agent sorting/ranking by last interaction descending.
3. Stable fallback ordering for un-interacted agents.
4. Persistence and seeding of interaction timestamps.
5. format_recency helper accuracy.
"""
import unittest
from unittest.mock import MagicMock, patch
import importlib.util
from pathlib import Path
import time
import tempfile
import json
REPO_ROOT = Path("/home/super/Projects/NetVM")
TUI_PATH = REPO_ROOT / "bin" / "muse-tui.py"
spec = importlib.util.spec_from_file_location("muse_tui", str(TUI_PATH))
muse_mod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(muse_mod)
class TestAgentRanking(unittest.TestCase):
"""Test suite covering fleet agent interaction sorting & recency tracking."""
def setUp(self):
self.mock_stdscr = MagicMock()
self.mock_stdscr.getmaxyx.return_value = (30, 100)
self.tui = muse_mod.MuseTUI(self.mock_stdscr, initial_mode="muse", initial_node="muse")
self.tui.safe_addstr = MagicMock()
self.tui.data.history_cache = {}
self.tui.data.read_msg_counts = {}
def test_format_recency(self):
"""Verify format_recency produces compact, accurate recency strings."""
now = time.time()
self.assertEqual(muse_mod.format_recency(0.0), "never")
self.assertEqual(muse_mod.format_recency(None), "never")
self.assertEqual(muse_mod.format_recency(now - 10), "10s ago")
self.assertEqual(muse_mod.format_recency(now - 120), "2m ago")
self.assertEqual(muse_mod.format_recency(now - 7200), "2h ago")
self.assertEqual(muse_mod.format_recency(now - 90000), "1d ago")
def test_sort_nodes_by_interaction(self):
"""Verify sort_nodes ranks nodes by last interaction timestamp descending."""
data = self.tui.data
now = time.time()
# Set specific timestamps
data.agent_interactions["muse"] = now - 500
data.agent_interactions["pip"] = now - 100
data.agent_interactions["646"] = now - 10 # Most recent
data.agent_interactions["opm"] = 0.0 # Never
data.agent_interactions["def"] = 0.0 # Never
data.sort_nodes()
# Most recent first: 646, then pip, then muse
self.assertEqual(data.nodes[0], "646")
self.assertEqual(data.nodes[1], "pip")
self.assertEqual(data.nodes[2], "muse")
# Un-interacted agents should be at the end, retaining baseline order
self.assertIn("opm", data.nodes[3:])
self.assertIn("def", data.nodes[3:])
def test_record_interaction_promotes_node_to_top(self):
"""Verify record_interaction immediately updates timestamp and promotes node to rank 1."""
data = self.tui.data
# Ensure 'pip' is not at index 0 initially
data.record_interaction("646")
self.assertEqual(data.nodes[0], "646")
# Now operator directly interacts with 'pip'
t0 = time.time()
data.record_interaction("pip")
self.assertEqual(data.nodes[0], "pip", "'pip' must be promoted to rank 1 (index 0)")
self.assertEqual(data.nodes[1], "646", "'646' must be rank 2")
self.assertGreaterEqual(data.agent_interactions["pip"], t0)
def test_chat_message_submission_triggers_ranking(self):
"""Verify submitting chat message in INSERT mode promotes the active node."""
self.tui.editor_mode = "INSERT"
self.tui.data.active_node = "def"
self.tui.data.active_thread_id = "test-session-123"
self.tui.data.active_thread_title = "Def Task Session"
# Mock async send to avoid subprocess
with patch.object(self.tui, "_async_send_message"):
self.tui._execute_input_line("Deploy updated service configuration")
# 'def' was chatted with directly; it must now be rank 1 at index 0
self.assertEqual(self.tui.data.nodes[0], "def", "Chat message send must promote target node to top of list")
def test_persistence_of_interactions(self):
"""Verify interaction records persist and load from json correctly."""
with tempfile.TemporaryDirectory() as tmpdir:
fake_int_file = Path(tmpdir) / "agent_interactions.json"
now = time.time()
fake_int_file.write_text(json.dumps({"opm": now - 50, "dev": now - 10}))
with patch("pathlib.Path.home", return_value=Path(tmpdir)), \
patch.object(muse_mod.Path, "home", return_value=Path(tmpdir)):
# Adjust path to match ~/.config/muse-cli/
cfg_dir = Path(tmpdir) / ".config" / "muse-cli"
cfg_dir.mkdir(parents=True, exist_ok=True)
(cfg_dir / "agent_interactions.json").write_text(json.dumps({"opm": now - 50, "dev": now - 10}))
mgr = muse_mod.FleetDataManager()
mgr.stop() # Stop background poller
self.assertAlmostEqual(mgr.agent_interactions.get("dev", 0.0), now - 10, delta=1.0)
self.assertAlmostEqual(mgr.agent_interactions.get("opm", 0.0), now - 50, delta=1.0)
self.assertEqual(mgr.nodes[0], "dev", "Persisted recent node 'dev' must be rank 1")
self.assertEqual(mgr.nodes[1], "opm", "'opm' must be rank 2")
if __name__ == "__main__":
unittest.main()