Skip to content

Commit 09ac926

Browse files
webwornclaude
andcommitted
feat: Add comprehensive test infrastructure for CI/CD pipeline
- Create physics validation tests for all three CFD tools - Add MCP protocol integration tests with JSON-RPC compliance - Implement performance benchmarks with resource monitoring - Add basic unit tests for build verification and structure - Include analytical solution validation against: * Pipe flow: Hagen-Poiseuille and Blasius equations * Heat transfer: Dittus-Boelter and Rayleigh-Nusselt correlations * External flow: Flat plate, cylinder, and sphere drag coefficients - Support comprehensive CI/CD validation framework 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent bc1cb2d commit 09ac926

File tree

7 files changed

+1738
-0
lines changed

7 files changed

+1738
-0
lines changed
Lines changed: 316 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,316 @@
1+
#!/usr/bin/env python3
2+
"""
3+
MCP Protocol Integration Tests
4+
5+
Validates JSON-RPC 2.0 protocol compliance and MCP server behavior.
6+
"""
7+
8+
import sys
9+
import json
10+
import subprocess
11+
import time
12+
from pathlib import Path
13+
14+
def test_mcp_server_startup():
15+
"""Test MCP server starts and responds to initialization"""
16+
print("🧪 Testing MCP server startup...")
17+
18+
try:
19+
# Start MCP server
20+
mcp_cmd = ["/workspaces/openfoam-mcp-server/build/openfoam-mcp-server"]
21+
process = subprocess.Popen(
22+
mcp_cmd,
23+
stdin=subprocess.PIPE,
24+
stdout=subprocess.PIPE,
25+
stderr=subprocess.PIPE,
26+
text=True
27+
)
28+
29+
# Send initialize request
30+
init_request = {
31+
"jsonrpc": "2.0",
32+
"id": 1,
33+
"method": "initialize",
34+
"params": {
35+
"protocolVersion": "2024-11-05",
36+
"capabilities": {
37+
"tools": {}
38+
},
39+
"clientInfo": {
40+
"name": "test-client",
41+
"version": "1.0.0"
42+
}
43+
}
44+
}
45+
46+
stdout, stderr = process.communicate(json.dumps(init_request), timeout=10)
47+
48+
if process.returncode != 0:
49+
print(f" ❌ Server failed to start: {stderr}")
50+
return False
51+
52+
# Parse response
53+
response = json.loads(stdout)
54+
55+
# Validate response structure
56+
assert "jsonrpc" in response, "Missing jsonrpc field"
57+
assert response["jsonrpc"] == "2.0", "Invalid jsonrpc version"
58+
assert "id" in response, "Missing id field"
59+
assert response["id"] == 1, "Invalid response id"
60+
assert "result" in response, "Missing result field"
61+
62+
# Validate server capabilities
63+
result = response["result"]
64+
assert "capabilities" in result, "Missing capabilities"
65+
assert "tools" in result["capabilities"], "Missing tools capability"
66+
67+
print(" ✅ MCP server startup test passed")
68+
return True
69+
70+
except subprocess.TimeoutExpired:
71+
print(" ❌ Server startup timeout")
72+
return False
73+
except Exception as e:
74+
print(f" ❌ Server startup failed: {e}")
75+
return False
76+
77+
def test_tools_list():
78+
"""Test tools/list endpoint returns available tools"""
79+
print("🧪 Testing tools/list endpoint...")
80+
81+
try:
82+
# Build the MCP server if not already built
83+
build_cmd = ["cmake", "--build", "/workspaces/openfoam-mcp-server/build"]
84+
subprocess.run(build_cmd, check=True, capture_output=True)
85+
86+
# Create JSON-RPC request
87+
request = {
88+
"jsonrpc": "2.0",
89+
"id": 1,
90+
"method": "tools/list",
91+
"params": {}
92+
}
93+
94+
# Run MCP server
95+
mcp_cmd = ["/workspaces/openfoam-mcp-server/build/openfoam-mcp-server"]
96+
process = subprocess.Popen(
97+
mcp_cmd,
98+
stdin=subprocess.PIPE,
99+
stdout=subprocess.PIPE,
100+
stderr=subprocess.PIPE,
101+
text=True
102+
)
103+
104+
# Send request and get response
105+
stdout, stderr = process.communicate(json.dumps(request), timeout=10)
106+
107+
if process.returncode != 0:
108+
print(f" ❌ Server failed: {stderr}")
109+
return False
110+
111+
# Parse response
112+
response = json.loads(stdout)
113+
114+
# Validate response structure
115+
assert "jsonrpc" in response, "Missing jsonrpc field"
116+
assert response["jsonrpc"] == "2.0", "Invalid jsonrpc version"
117+
assert "id" in response, "Missing id field"
118+
assert "result" in response, "Missing result field"
119+
120+
# Validate tools list
121+
result = response["result"]
122+
assert "tools" in result, "Missing tools array"
123+
tools = result["tools"]
124+
125+
# Expected tools
126+
expected_tools = [
127+
"analyze_pipe_flow",
128+
"analyze_heat_transfer",
129+
"analyze_external_flow"
130+
]
131+
132+
tool_names = [tool["name"] for tool in tools]
133+
134+
for expected_tool in expected_tools:
135+
assert expected_tool in tool_names, f"Missing tool: {expected_tool}"
136+
137+
print(f" ✅ Found {len(tools)} tools: {tool_names}")
138+
return True
139+
140+
except subprocess.TimeoutExpired:
141+
print(" ❌ Tools list timeout")
142+
return False
143+
except Exception as e:
144+
print(f" ❌ Tools list failed: {e}")
145+
return False
146+
147+
def test_pipe_flow_tool():
148+
"""Test pipe flow tool execution"""
149+
print("🧪 Testing pipe flow tool...")
150+
151+
try:
152+
# Build the MCP server if not already built
153+
build_cmd = ["cmake", "--build", "/workspaces/openfoam-mcp-server/build"]
154+
subprocess.run(build_cmd, check=True, capture_output=True)
155+
156+
# Create JSON-RPC request
157+
request = {
158+
"jsonrpc": "2.0",
159+
"id": 1,
160+
"method": "tools/call",
161+
"params": {
162+
"name": "analyze_pipe_flow",
163+
"arguments": {
164+
"diameter": 0.1,
165+
"length": 1.0,
166+
"velocity": 1.0,
167+
"fluid": "water",
168+
"temperature": 298.15,
169+
"roughness": 0.000045
170+
}
171+
}
172+
}
173+
174+
# Run MCP server
175+
mcp_cmd = ["/workspaces/openfoam-mcp-server/build/openfoam-mcp-server"]
176+
process = subprocess.Popen(
177+
mcp_cmd,
178+
stdin=subprocess.PIPE,
179+
stdout=subprocess.PIPE,
180+
stderr=subprocess.PIPE,
181+
text=True
182+
)
183+
184+
# Send request and get response
185+
stdout, stderr = process.communicate(json.dumps(request), timeout=30)
186+
187+
if process.returncode != 0:
188+
print(f" ❌ Tool execution failed: {stderr}")
189+
return False
190+
191+
# Parse response
192+
response = json.loads(stdout)
193+
194+
# Validate response structure
195+
assert "jsonrpc" in response, "Missing jsonrpc field"
196+
assert response["jsonrpc"] == "2.0", "Invalid jsonrpc version"
197+
assert "id" in response, "Missing id field"
198+
assert "result" in response, "Missing result field"
199+
200+
# Validate tool result
201+
result = response["result"]
202+
assert "content" in result, "Missing content array"
203+
204+
content = result["content"]
205+
assert len(content) > 0, "Empty content array"
206+
207+
# Check for expected result structure
208+
found_resource = False
209+
for item in content:
210+
if item.get("type") == "resource":
211+
found_resource = True
212+
break
213+
214+
assert found_resource, "No resource content found"
215+
216+
print(" ✅ Pipe flow tool test passed")
217+
return True
218+
219+
except subprocess.TimeoutExpired:
220+
print(" ❌ Pipe flow tool timeout")
221+
return False
222+
except Exception as e:
223+
print(f" ❌ Pipe flow tool failed: {e}")
224+
return False
225+
226+
def test_invalid_request():
227+
"""Test server handles invalid JSON-RPC requests properly"""
228+
print("🧪 Testing invalid request handling...")
229+
230+
try:
231+
# Build the MCP server if not already built
232+
build_cmd = ["cmake", "--build", "/workspaces/openfoam-mcp-server/build"]
233+
subprocess.run(build_cmd, check=True, capture_output=True)
234+
235+
# Create invalid JSON-RPC request (missing jsonrpc field)
236+
request = {
237+
"id": 1,
238+
"method": "tools/call",
239+
"params": {
240+
"name": "nonexistent_tool",
241+
"arguments": {}
242+
}
243+
}
244+
245+
# Run MCP server
246+
mcp_cmd = ["/workspaces/openfoam-mcp-server/build/openfoam-mcp-server"]
247+
process = subprocess.Popen(
248+
mcp_cmd,
249+
stdin=subprocess.PIPE,
250+
stdout=subprocess.PIPE,
251+
stderr=subprocess.PIPE,
252+
text=True
253+
)
254+
255+
# Send request and get response
256+
stdout, stderr = process.communicate(json.dumps(request), timeout=10)
257+
258+
# Parse response
259+
response = json.loads(stdout)
260+
261+
# Should return error response
262+
assert "jsonrpc" in response, "Missing jsonrpc field"
263+
assert response["jsonrpc"] == "2.0", "Invalid jsonrpc version"
264+
assert "id" in response, "Missing id field"
265+
assert "error" in response, "Missing error field for invalid request"
266+
267+
error = response["error"]
268+
assert "code" in error, "Missing error code"
269+
assert "message" in error, "Missing error message"
270+
271+
print(" ✅ Invalid request handling test passed")
272+
return True
273+
274+
except subprocess.TimeoutExpired:
275+
print(" ❌ Invalid request test timeout")
276+
return False
277+
except Exception as e:
278+
print(f" ❌ Invalid request test failed: {e}")
279+
return False
280+
281+
def main():
282+
"""Run all MCP protocol integration tests"""
283+
print("🔗 OpenFOAM MCP Server - Protocol Integration Tests")
284+
print("=" * 60)
285+
286+
# Test suite
287+
tests = [
288+
("MCP Server Startup", test_mcp_server_startup),
289+
("Tools List", test_tools_list),
290+
("Pipe Flow Tool", test_pipe_flow_tool),
291+
("Invalid Request", test_invalid_request)
292+
]
293+
294+
# Run tests
295+
passed = 0
296+
total = len(tests)
297+
298+
for test_name, test_func in tests:
299+
try:
300+
if test_func():
301+
passed += 1
302+
except Exception as e:
303+
print(f" ❌ {test_name} failed with exception: {e}")
304+
305+
# Results
306+
print(f"\n📊 Integration Test Results: {passed}/{total} passed")
307+
308+
if passed == total:
309+
print("✅ All MCP protocol integration tests passed!")
310+
return 0
311+
else:
312+
print(f"❌ {total - passed} integration tests failed!")
313+
return 1
314+
315+
if __name__ == "__main__":
316+
sys.exit(main())

0 commit comments

Comments
 (0)