mirror of
https://github.com/JamesTheGiblet/BuddAI.git
synced 2026-01-08 21:58:40 +00:00
- Introduced 16 additional coverage tests in `test_additional_coverage.py` to enhance overall test coverage. - Added 15 extended feature tests in `test_extended_features.py` to validate new functionalities. - Implemented 27 final coverage tests in `test_final_coverage.py` to achieve a total of 100 tests. - Created 2 fallback logic tests in `test_fallback_logic.py` to ensure proper fallback behavior based on confidence scores. - Each test suite covers various aspects of the BuddAI system, including command handling, database interactions, and hardware detection.
254 lines
No EOL
11 KiB
Python
254 lines
No EOL
11 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Additional Coverage Tests for BuddAI
|
|
Adds 16 tests to improve overall system coverage.
|
|
"""
|
|
|
|
import unittest
|
|
import sys
|
|
import os
|
|
import tempfile
|
|
import sqlite3
|
|
import json
|
|
from pathlib import Path
|
|
from unittest.mock import patch, MagicMock, mock_open
|
|
import importlib.util
|
|
|
|
# Dynamic import setup
|
|
REPO_ROOT = Path(__file__).parent.parent
|
|
if str(REPO_ROOT) not in sys.path:
|
|
sys.path.insert(0, str(REPO_ROOT))
|
|
|
|
MODULE_PATH = REPO_ROOT / "buddai_executive.py"
|
|
spec = importlib.util.spec_from_file_location("buddai_executive", MODULE_PATH)
|
|
buddai_module = importlib.util.module_from_spec(spec)
|
|
sys.modules["buddai_executive"] = buddai_module
|
|
spec.loader.exec_module(buddai_module)
|
|
BuddAI = buddai_module.BuddAI
|
|
|
|
class TestAdditionalCoverage(unittest.TestCase):
|
|
def setUp(self):
|
|
# Create temp DB
|
|
self.db_fd, self.db_path = tempfile.mkstemp(suffix=".db")
|
|
os.close(self.db_fd)
|
|
self.db_path_obj = Path(self.db_path)
|
|
|
|
# Patch DB_PATH
|
|
self.db_patcher = patch.object(buddai_module, 'DB_PATH', self.db_path_obj)
|
|
self.db_patcher.start()
|
|
|
|
# Suppress prints
|
|
self.print_patcher = patch("builtins.print")
|
|
self.print_patcher.start()
|
|
|
|
# Initialize BuddAI
|
|
self.buddai = BuddAI(server_mode=False)
|
|
|
|
# Create tables required for tests
|
|
conn = sqlite3.connect(self.db_path)
|
|
cursor = conn.cursor()
|
|
cursor.execute("CREATE TABLE IF NOT EXISTS repo_index (id INTEGER PRIMARY KEY, file_path TEXT, repo_name TEXT, function_name TEXT, content TEXT, last_modified TIMESTAMP, user_id TEXT)")
|
|
cursor.execute("CREATE TABLE IF NOT EXISTS messages (id INTEGER PRIMARY KEY AUTOINCREMENT, session_id TEXT, role TEXT, content TEXT, timestamp TIMESTAMP)")
|
|
cursor.execute("CREATE TABLE IF NOT EXISTS code_rules (rule_text TEXT, confidence FLOAT, pattern_find TEXT, pattern_replace TEXT, learned_from TEXT)")
|
|
cursor.execute("CREATE TABLE IF NOT EXISTS style_preferences (id INTEGER PRIMARY KEY, user_id TEXT, category TEXT, preference TEXT, confidence FLOAT, extracted_at TIMESTAMP)")
|
|
conn.commit()
|
|
conn.close()
|
|
|
|
def tearDown(self):
|
|
self.db_patcher.stop()
|
|
self.print_patcher.stop()
|
|
try:
|
|
os.unlink(self.db_path)
|
|
except:
|
|
pass
|
|
|
|
# Test 31: Welcome Message Formatting
|
|
def test_welcome_message(self):
|
|
"""Test welcome message includes rule count"""
|
|
with patch('sqlite3.connect') as mock_conn:
|
|
mock_cursor = MagicMock()
|
|
mock_conn.return_value.cursor.return_value = mock_cursor
|
|
mock_cursor.fetchone.return_value = [42]
|
|
|
|
with patch('builtins.print') as mock_print:
|
|
self.buddai.display_welcome_message()
|
|
# Check if any print call contained '42'
|
|
found = any('42' in str(call) for call in mock_print.call_args_list)
|
|
self.assertTrue(found)
|
|
|
|
# Test 32: Scan Style - No Index
|
|
def test_scan_style_no_index(self):
|
|
"""Test scan_style_signature when no code is indexed"""
|
|
with patch('sqlite3.connect') as mock_conn:
|
|
mock_cursor = MagicMock()
|
|
mock_conn.return_value.cursor.return_value = mock_cursor
|
|
mock_cursor.fetchall.return_value = []
|
|
|
|
with patch('builtins.print') as mock_print:
|
|
self.buddai.scan_style_signature()
|
|
mock_print.assert_any_call("❌ No code indexed. Run /index first.")
|
|
|
|
# Test 33: Scan Style - Execution
|
|
def test_scan_style_execution(self):
|
|
"""Test successful style scan and DB insertion"""
|
|
with patch('sqlite3.connect') as mock_conn:
|
|
mock_cursor = MagicMock()
|
|
mock_conn.return_value.cursor.return_value = mock_cursor
|
|
mock_cursor.fetchall.return_value = [("code sample",)]
|
|
|
|
with patch.object(self.buddai, 'call_model', return_value="Naming: camelCase"):
|
|
self.buddai.scan_style_signature()
|
|
|
|
# Verify insertion
|
|
insert_calls = [c for c in mock_cursor.execute.call_args_list if "INSERT INTO style_preferences" in c[0][0]]
|
|
self.assertTrue(len(insert_calls) > 0)
|
|
self.assertIn("camelCase", insert_calls[0][0][1])
|
|
|
|
# Test 34: Get Applicable Rules Filtering
|
|
def test_get_applicable_rules(self):
|
|
"""Test that only high-confidence rules are returned"""
|
|
with patch('sqlite3.connect') as mock_conn:
|
|
mock_cursor = MagicMock()
|
|
mock_conn.return_value.cursor.return_value = mock_cursor
|
|
# Return one high confidence, one low
|
|
mock_cursor.fetchall.return_value = [("Rule 1", 0.8), ("Rule 2", 0.4)]
|
|
|
|
# The method itself filters in SQL usually, but let's verify the SQL query
|
|
self.buddai.get_applicable_rules("msg")
|
|
|
|
call_args = mock_cursor.execute.call_args[0][0]
|
|
self.assertIn("confidence > 0.6", call_args)
|
|
|
|
# Test 35: Teach Rule Persistence
|
|
def test_teach_rule(self):
|
|
"""Test explicit rule teaching persistence"""
|
|
with patch('sqlite3.connect') as mock_conn:
|
|
mock_cursor = MagicMock()
|
|
mock_conn.return_value.cursor.return_value = mock_cursor
|
|
|
|
self.buddai.teach_rule("Always use const")
|
|
|
|
# Verify insert
|
|
call_args = mock_cursor.execute.call_args
|
|
self.assertIn("INSERT INTO code_rules", call_args[0][0])
|
|
self.assertIn("user_taught", call_args[0][1])
|
|
|
|
# Test 36: Regenerate - Invalid ID
|
|
def test_regenerate_invalid_id(self):
|
|
"""Test regeneration with non-existent message ID"""
|
|
with patch('sqlite3.connect') as mock_conn:
|
|
mock_cursor = MagicMock()
|
|
mock_conn.return_value.cursor.return_value = mock_cursor
|
|
mock_cursor.fetchone.return_value = None
|
|
|
|
result = self.buddai.regenerate_response(999)
|
|
self.assertEqual(result, "Error: Message not found.")
|
|
|
|
# Test 37: Regenerate - Success Flow
|
|
def test_regenerate_success(self):
|
|
"""Test successful regeneration flow"""
|
|
with patch('sqlite3.connect') as mock_conn:
|
|
mock_cursor = MagicMock()
|
|
mock_conn.return_value.cursor.return_value = mock_cursor
|
|
# First fetch: session_id, current_id
|
|
# Second fetch: user prompt
|
|
mock_cursor.fetchone.side_effect = [("sess1", 10), ("User Prompt",)]
|
|
|
|
with patch.object(self.buddai, 'chat', return_value="New Response") as mock_chat:
|
|
result = self.buddai.regenerate_response(10, "Better comment")
|
|
|
|
self.assertEqual(result, "New Response")
|
|
mock_chat.assert_called()
|
|
self.assertIn("Feedback: Better comment", mock_chat.call_args[0][0])
|
|
|
|
# Test 38: Slash Command - Reload
|
|
def test_slash_reload(self):
|
|
"""Test /reload command refreshes registry"""
|
|
with patch.object(buddai_module, 'load_registry', return_value={'new': 'skill'}):
|
|
res = self.buddai.handle_slash_command("/reload")
|
|
self.assertIn("Reloaded 1 skills", res)
|
|
self.assertEqual(self.buddai.skills_registry, {'new': 'skill'})
|
|
|
|
# Test 39: Slash Command - Debug Empty
|
|
def test_slash_debug_empty(self):
|
|
"""Test /debug when no prompt has been sent"""
|
|
self.buddai.last_prompt_debug = None
|
|
res = self.buddai.handle_slash_command("/debug")
|
|
self.assertIn("No prompt sent yet", res)
|
|
|
|
# Test 40: Slash Command - Validate No Context
|
|
def test_slash_validate_no_context(self):
|
|
"""Test /validate with no history"""
|
|
self.buddai.context_messages = []
|
|
res = self.buddai.handle_slash_command("/validate")
|
|
self.assertIn("No recent code", res)
|
|
|
|
# Test 41: Slash Command - Validate No Code
|
|
def test_slash_validate_no_code(self):
|
|
"""Test /validate when last message has no code"""
|
|
self.buddai.context_messages = [
|
|
{"role": "user", "content": "hi"},
|
|
{"role": "assistant", "content": "Hello there"}
|
|
]
|
|
res = self.buddai.handle_slash_command("/validate")
|
|
self.assertIn("No code blocks found", res)
|
|
|
|
# Test 42: Import Session - Collision
|
|
def test_import_session_collision(self):
|
|
"""Test importing session with ID collision generates new ID"""
|
|
data = {
|
|
"session_id": "sess1",
|
|
"messages": [{"role": "user", "content": "hi"}]
|
|
}
|
|
|
|
with patch('sqlite3.connect') as mock_conn:
|
|
mock_cursor = MagicMock()
|
|
mock_conn.return_value.cursor.return_value = mock_cursor
|
|
# First check: returns row (collision)
|
|
mock_cursor.fetchone.return_value = [1]
|
|
|
|
new_id = self.buddai.import_session_from_json(data)
|
|
|
|
self.assertNotEqual(new_id, "sess1")
|
|
self.assertTrue("sess1_imp_" in new_id)
|
|
|
|
# Test 43: Export Session to Markdown
|
|
def test_export_markdown(self):
|
|
"""Test markdown export content generation"""
|
|
with patch('sqlite3.connect') as mock_conn:
|
|
mock_cursor = MagicMock()
|
|
mock_conn.return_value.cursor.return_value = mock_cursor
|
|
mock_cursor.fetchall.return_value = [("user", "hi", "2025-01-01")]
|
|
|
|
with patch('builtins.open', mock_open()) as mock_file:
|
|
with patch('buddai_executive.DATA_DIR', Path('/tmp')):
|
|
res = self.buddai.export_session_to_markdown("sess1")
|
|
|
|
self.assertIn("exported to", res)
|
|
handle = mock_file()
|
|
handle.write.assert_any_call("# BuddAI Session: sess1\n\n")
|
|
|
|
# Test 44: Backup Delegation
|
|
def test_backup_delegation(self):
|
|
"""Test backup command delegates to storage manager"""
|
|
with patch.object(self.buddai.storage, 'create_backup', return_value=(True, "path.db")):
|
|
res = self.buddai.handle_slash_command("/backup")
|
|
self.assertIn("backed up to: path.db", res)
|
|
|
|
# Test 45: Metrics Delegation
|
|
def test_metrics_delegation(self):
|
|
"""Test metrics command delegates to metrics component"""
|
|
with patch.object(self.buddai.metrics, 'calculate_accuracy', return_value={'accuracy': 100, 'correction_rate': 0, 'improvement': '0'}):
|
|
res = self.buddai.handle_slash_command("/metrics")
|
|
self.assertIn("100.0%", res)
|
|
|
|
# Test 46: Hardware Detection Flow
|
|
def test_hardware_detection_flow(self):
|
|
"""Test chat flow updates hardware profile"""
|
|
with patch.object(self.buddai.hardware_profile, 'detect_hardware', return_value="Arduino Uno"):
|
|
with patch.object(self.buddai, '_route_request', return_value="Response"):
|
|
self.buddai.chat("Use Arduino Uno")
|
|
self.assertEqual(self.buddai.current_hardware, "Arduino Uno")
|
|
|
|
if __name__ == '__main__':
|
|
unittest.main() |