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)