File size: 5,982 Bytes
e9b3659 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 | """
Manual test runner for the Codebase Assistant.
Run with: .venv\Scripts\python.exe test_runner.py [test_name]
Available tests:
mcp - Test GitHub MCP connection & list tools
embed - Test embedding a small text snippet
ingest - Index this repo into the local vector store
query - Run a RAG query against the vector store
bugs - Detect bugs in a given file
complexity - Analyse cyclomatic complexity of a file
all - Run embed -> ingest -> query -> bugs -> complexity (no MCP)
"""
import asyncio
import sys
import os
# ββ helpers ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
def header(title: str):
print(f"\n{'='*60}")
print(f" {title}")
print(f"{'='*60}")
def ok(msg: str): print(f" OK {msg}")
def fail(msg: str): print(f" FAIL {msg}")
ROOT = os.path.dirname(__file__)
# ββ individual tests ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
async def test_mcp():
"""Connect to GitHub MCP and list available tools."""
header("Test: GitHub MCP Connection")
from mcp_client import get_github_mcp_client
client = get_github_mcp_client()
try:
await client.connect()
tools = await client.list_tools()
ok(f"Connected. {len(tools)} tools found:")
for t in tools[:5]:
print(f" - {t['name']}: {t['description'][:60]}")
if len(tools) > 5:
print(f" ... and {len(tools)-5} more")
except Exception as e:
fail(f"MCP connection failed: {e}")
finally:
await client.disconnect()
def test_embed():
"""Embed a small text snippet and verify the vector shape."""
header("Test: Embedding")
from rag.embedding import embed_query, embed_document
try:
vec = embed_query("def hello(): pass")
assert isinstance(vec, list) and len(vec) > 0, "embed_query returned empty"
ok(f"embed_query -> vector of {len(vec)} dims")
fake_chunks = [{"content": "def add(a, b): return a+b"}]
chunks = embed_document(fake_chunks)
assert "embedding" in chunks[0], "embed_document did not add 'embedding' key"
ok(f"embed_document -> chunk embedding of {len(chunks[0]['embedding'])} dims")
except Exception as e:
fail(f"Embedding failed: {e}")
def test_ingest():
"""Load and index this repository into the local ChromaDB."""
header("Test: Repository Ingest")
from rag.repository_loader import load_repository
from rag.splitter import split_code
from rag.embedding import embed_document
from rag.retriever import build_vector_store
try:
docs = load_repository(ROOT)
ok(f"Loaded {len(docs)} files from repo")
all_chunks = []
for doc in docs:
all_chunks.extend(split_code(doc))
ok(f"Split into {len(all_chunks)} chunks")
embedded = embed_document(all_chunks)
ok(f"Embedded {len(embedded)} chunks")
build_vector_store(embedded)
ok("Vector store built / updated (ChromaDB)")
except Exception as e:
fail(f"Ingest failed: {e}")
def test_query():
"""Run a RAG query. Requires the vector store to be populated first."""
header("Test: RAG Query")
from rag.rag_chain import run_rag_query
try:
result = run_rag_query("How does the embedding module work?", k=3)
ok(f"Answer received ({len(result['answer'])} chars)")
ok(f"Sources returned: {len(result['sources'])}")
for s in result["sources"]:
print(f" - {s.get('file_path','?')} lines {s.get('start_line')}-{s.get('end_line')}")
print(f"\n Answer preview:\n {result['answer'][:300]}...")
except Exception as e:
fail(f"RAG query failed: {e}")
def test_bugs():
"""Run bug detection on llm.py."""
header("Test: Bug Detection")
from services.code_analysis import detect_bugs
target = os.path.join(ROOT, "llm.py")
try:
bugs = detect_bugs(target)
ok(f"Bug detection returned {len(bugs)} item(s) for llm.py")
for b in bugs[:3]:
print(f" Line {b.get('line','?')}: [{b.get('severity','?')}] {b.get('issue','?')}")
except Exception as e:
fail(f"Bug detection failed: {e}")
def test_complexity():
"""Analyse cyclomatic complexity of llm.py."""
header("Test: Complexity Analysis")
from services.code_analysis import analyze_complexity
target = os.path.join(ROOT, "llm.py")
try:
result = analyze_complexity(target)
ok(f"Complexity analysis returned {len(result['functions'])} function(s):")
for fn in result["functions"]:
print(f" {fn['name']}: complexity={fn['complexity']}, rank={fn['rank']}")
except Exception as e:
fail(f"Complexity analysis failed: {e}")
# ββ entry point βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
TESTS = {
"mcp": lambda: asyncio.run(test_mcp()),
"embed": test_embed,
"ingest": test_ingest,
"query": test_query,
"bugs": test_bugs,
"complexity": test_complexity,
}
def run_all_non_mcp():
"""Run embed -> ingest -> query -> bugs -> complexity in sequence."""
test_embed()
test_ingest()
test_query()
test_bugs()
test_complexity()
if __name__ == "__main__":
arg = sys.argv[1] if len(sys.argv) > 1 else "all"
if arg == "all":
run_all_non_mcp()
elif arg in TESTS:
TESTS[arg]()
else:
print(f"Unknown test '{arg}'. Choose from: {', '.join(TESTS)} or 'all'")
sys.exit(1)
|