-
-
Notifications
You must be signed in to change notification settings - Fork 511
Expand file tree
/
Copy pathconftest.py
More file actions
146 lines (127 loc) · 5.37 KB
/
conftest.py
File metadata and controls
146 lines (127 loc) · 5.37 KB
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
import subprocess
import json
import os
import time
import pytest
# Path to the sample project used in tests
SAMPLE_PROJECT_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), "sample_project"))
# SAMPLE_PROJECT_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), "sample_project_javascript"))
# Helper function to call a tool, now shared across all tests
def call_tool(server, name, args):
request = {
"jsonrpc": "2.0",
"id": int(time.time()),
"method": "tools/call",
"params": {"name": name, "arguments": args}
}
response = server(request)
if "result" in response:
content = json.loads(response["result"]["content"][0]["text"])
return content
elif "error" in response:
return response
else:
raise ValueError(f"Unexpected response format: {response}")
@pytest.fixture(scope="module")
def server():
"""
A module-scoped fixture that starts the cgc server once for all tests
in this file and provides a communication helper function.
"""
print("\n--- Setting up server fixture ---")
process = None
try:
print("Starting cgc server process...")
process = subprocess.Popen(
["cgc", "start"],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
cwd=os.path.join(os.path.dirname(__file__), ".."))
print("Waiting for server to be ready...")
for line in iter(process.stderr.readline, ''):
print(f"STDERR: {line.strip()}")
if "MCP Server is running" in line:
print("Server is ready.")
break
def send_receive(request):
print(f"--> Sending request: {json.dumps(request)}")
process.stdin.write(json.dumps(request) + "\n")
process.stdin.flush()
while True:
response_line = process.stdout.readline()
print(f"<-- Received line: {response_line.strip()}")
try:
return json.loads(response_line)
except json.JSONDecodeError:
continue
print("Initializing server connection...")
init_request = {"jsonrpc": "2.0", "id": 1, "method": "initialize", "params": {}}
init_response = send_receive(init_request)
assert init_response.get("id") == 1 and "result" in init_response, "Initialization failed"
print("Server connection initialized.")
yield send_receive
finally:
print("\n--- Tearing down server fixture ---")
if process:
print("Terminating server process.")
process.terminate()
process.wait()
print("Server process terminated.")
def pytest_addoption(parser):
parser.addoption(
"--no-reindex", action="store_true", default=False, help="Skip re-indexing the project for tests"
)
@pytest.fixture(scope="module")
def indexed_project(server, request):
"""
Ensures the sample project is indexed before running tests.
"""
if not request.config.getoption("--no-reindex"):
print("\n--- Ensuring project is indexed ---")
delete_result = call_tool(server, "delete_repository", {"repo_path": SAMPLE_PROJECT_PATH})
print(f"Delete result: {delete_result}")
add_result = call_tool(server, "add_code_to_graph", {"path": SAMPLE_PROJECT_PATH})
assert add_result.get("success") is True, f"add_code_to_graph failed: {add_result.get('error')}"
job_id = add_result.get("job_id")
assert job_id is not None, "add_code_to_graph did not return a job_id"
print(f"Started indexing job with ID: {job_id}")
start_time = time.time()
timeout = 180
while True:
if time.time() - start_time > timeout:
pytest.fail(f"Job {job_id} did not complete within {timeout} seconds.")
status_result = call_tool(server, "check_job_status", {"job_id": job_id})
job_status = status_result.get("job", {}).get("status")
print(f"Current job status: {job_status}")
if job_status == "completed":
print("Job completed successfully.")
break
assert job_status not in ["failed", "cancelled"], f"Job failed with status: {job_status}"
time.sleep(2)
else:
print("\n--- Skipping re-indexing as per --no-reindex flag ---")
return server
class CodeGraph:
"""
A wrapper class that provides a .query() method to execute Cypher
against the indexed graph, compatible with our tests.
"""
def __init__(self, server_communicator):
self.server = server_communicator
def query(self, cypher_query: str):
response = call_tool(self.server, "execute_cypher_query", {"cypher_query": cypher_query})
if response.get("success"):
return response.get("results", [])
else:
error_details = response.get('error', 'Unknown error')
raise RuntimeError(f"Cypher query failed: {error_details}\nQuery was: {cypher_query}")
@pytest.fixture(scope="module")
def graph(indexed_project):
"""
Provides a CodeGraph object to query the indexed project.
Depends on indexed_project to ensure the graph is ready.
"""
print("\n--- Creating CodeGraph query wrapper ---")
return CodeGraph(indexed_project)