#!/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()