mlgym-deploy 3.3.41 → 3.3.43
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ADD_TO_CURSOR_SETTINGS.json +1 -1
- package/ADD_TO_CURSOR_SETTINGS.json.example +28 -0
- package/CHANGELOG-v3.3.42.md +707 -0
- package/Dockerfile +7 -0
- package/README.md +35 -0
- package/claude-desktop-config.json +1 -1
- package/claude-desktop-config.json.example +8 -0
- package/cursor-config.json +1 -1
- package/cursor-config.json.example +13 -0
- package/docs/CURSOR_SETUP.md +204 -0
- package/index.js +263 -5
- package/mcp.json.example +13 -0
- package/package.json +11 -3
- package/tests/README.md +7 -2
- package/tests/TEST_RESULTS.md +518 -0
- package/tests/archived/README.md +71 -0
- package/{deploy-hello-world.sh → tests/archived/deploy-hello-world.sh} +9 -6
- package/tests/deploy_dollie_test.sh +203 -0
- package/tests/misc/check-sdk-version.js +50 -0
- package/tests/mlgym_auth_login_test.sh +13 -9
- package/tests/mlgym_deploy_logs_test.sh +339 -0
- package/tests/mlgym_deploy_test.sh +341 -0
- package/tests/mlgym_status_test.sh +281 -0
- package/tests/mlgym_user_create_test.sh +35 -29
- package/tests/run-all-tests.sh +135 -41
- package/CURSOR_SETUP.md +0 -119
- package/index.js.backup-atomic +0 -1358
- /package/{DEBUG.md → docs/DEBUG.md} +0 -0
- /package/{SECURITY-UPDATE-v2.4.0.md → tests/archived/SECURITY-UPDATE-v2.4.0.md} +0 -0
- /package/{cursor-integration.js → tests/archived/cursor-integration.js} +0 -0
- /package/tests/{mlgym_auth_logout_test.sh → archived/mlgym_auth_logout_test.sh} +0 -0
- /package/tests/{mlgym_deployments_test.sh → archived/mlgym_deployments_test.sh} +0 -0
- /package/tests/{mlgym_project_init_test.sh → archived/mlgym_project_init_test.sh} +0 -0
- /package/tests/{mlgym_projects_get_test.sh → archived/mlgym_projects_get_test.sh} +0 -0
- /package/tests/{mlgym_projects_list_test.sh → archived/mlgym_projects_list_test.sh} +0 -0
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Comprehensive test suite for mlgym_deploy (v3.0.0+ monolithic deployment tool)
|
|
3
|
+
# Tests functional requirements, edge cases, and security
|
|
4
|
+
|
|
5
|
+
set -e
|
|
6
|
+
|
|
7
|
+
PASSED=0
|
|
8
|
+
FAILED=0
|
|
9
|
+
|
|
10
|
+
# Get script directory to find index.js
|
|
11
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
12
|
+
cd "$SCRIPT_DIR/.."
|
|
13
|
+
|
|
14
|
+
echo "=== mlgym_deploy Comprehensive Test Suite ==="
|
|
15
|
+
echo ""
|
|
16
|
+
|
|
17
|
+
# Helper function to send MCP request
|
|
18
|
+
send_mcp_request() {
|
|
19
|
+
local tool_name="$1"
|
|
20
|
+
local arguments="$2"
|
|
21
|
+
local request_id="${3:-99}"
|
|
22
|
+
|
|
23
|
+
local init_request='{"jsonrpc":"2.0","method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"deploy-test","version":"1.0.0"}},"id":0}'
|
|
24
|
+
local compact_args=$(echo "$arguments" | jq -c .)
|
|
25
|
+
local request="{\"jsonrpc\":\"2.0\",\"method\":\"tools/call\",\"params\":{\"name\":\"$tool_name\",\"arguments\":$compact_args},\"id\":$request_id}"
|
|
26
|
+
|
|
27
|
+
local temp_input="/tmp/mcp_deploy_test_$$.txt"
|
|
28
|
+
printf "%s\n%s\n" "$init_request" "$request" > "$temp_input"
|
|
29
|
+
|
|
30
|
+
local response=$(cat "$temp_input" | node index.js 2>/dev/null | jq -c "select(.id == $request_id)")
|
|
31
|
+
rm -f "$temp_input"
|
|
32
|
+
|
|
33
|
+
echo "$response"
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
# Helper function to extract response text
|
|
37
|
+
get_response_text() {
|
|
38
|
+
local response="$1"
|
|
39
|
+
echo "$response" | jq -r '.result.content[0].text // empty' 2>/dev/null
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
# Helper function to check if response is an error
|
|
43
|
+
is_error() {
|
|
44
|
+
local text="$1"
|
|
45
|
+
local status=$(echo "$text" | jq -r '.status' 2>/dev/null)
|
|
46
|
+
if [ "$status" = "error" ]; then
|
|
47
|
+
return 0
|
|
48
|
+
fi
|
|
49
|
+
return 1
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
# =============================================================================
|
|
53
|
+
# Phase 1: Input Validation Tests (no authentication required)
|
|
54
|
+
# =============================================================================
|
|
55
|
+
|
|
56
|
+
echo "PHASE 1: Input Validation Tests"
|
|
57
|
+
echo "==============================="
|
|
58
|
+
echo ""
|
|
59
|
+
|
|
60
|
+
# Test 1: Missing project_name
|
|
61
|
+
echo "Test 1: Missing project_name parameter"
|
|
62
|
+
ARGS='{"project_description":"Test description"}'
|
|
63
|
+
RESP=$(send_mcp_request "mlgym_deploy" "$ARGS" 1)
|
|
64
|
+
TEXT=$(get_response_text "$RESP")
|
|
65
|
+
if is_error "$TEXT"; then
|
|
66
|
+
echo "✅ Test 1: Correctly rejects missing project_name"
|
|
67
|
+
PASSED=$((PASSED + 1))
|
|
68
|
+
else
|
|
69
|
+
echo "❌ Test 1: Should reject missing project_name"
|
|
70
|
+
FAILED=$((FAILED + 1))
|
|
71
|
+
fi
|
|
72
|
+
|
|
73
|
+
# Test 2: Missing project_description
|
|
74
|
+
echo "Test 2: Missing project_description parameter"
|
|
75
|
+
ARGS='{"project_name":"test-project"}'
|
|
76
|
+
RESP=$(send_mcp_request "mlgym_deploy" "$ARGS" 2)
|
|
77
|
+
TEXT=$(get_response_text "$RESP")
|
|
78
|
+
if is_error "$TEXT"; then
|
|
79
|
+
echo "✅ Test 2: Correctly rejects missing project_description"
|
|
80
|
+
PASSED=$((PASSED + 1))
|
|
81
|
+
else
|
|
82
|
+
echo "❌ Test 2: Should reject missing project_description"
|
|
83
|
+
FAILED=$((FAILED + 1))
|
|
84
|
+
fi
|
|
85
|
+
|
|
86
|
+
# Test 3: Invalid project name (uppercase)
|
|
87
|
+
echo "Test 3: Invalid project name (uppercase)"
|
|
88
|
+
ARGS='{"project_name":"INVALID-NAME","project_description":"Test deployment"}'
|
|
89
|
+
RESP=$(send_mcp_request "mlgym_deploy" "$ARGS" 3)
|
|
90
|
+
TEXT=$(get_response_text "$RESP")
|
|
91
|
+
# Backend may or may not reject this - test is informational
|
|
92
|
+
if echo "$TEXT" | grep -qiE "error|invalid"; then
|
|
93
|
+
echo "✅ Test 3: Invalid name rejected"
|
|
94
|
+
PASSED=$((PASSED + 1))
|
|
95
|
+
else
|
|
96
|
+
echo "⚠️ Test 3: Invalid name may have been accepted (informational)"
|
|
97
|
+
PASSED=$((PASSED + 1))
|
|
98
|
+
fi
|
|
99
|
+
|
|
100
|
+
# Test 4: Short project name (< 3 chars)
|
|
101
|
+
echo "Test 4: Short project name"
|
|
102
|
+
ARGS='{"project_name":"ab","project_description":"Test deployment with short name"}'
|
|
103
|
+
RESP=$(send_mcp_request "mlgym_deploy" "$ARGS" 4)
|
|
104
|
+
TEXT=$(get_response_text "$RESP")
|
|
105
|
+
if is_error "$TEXT"; then
|
|
106
|
+
echo "✅ Test 4: Correctly rejects short project name"
|
|
107
|
+
PASSED=$((PASSED + 1))
|
|
108
|
+
else
|
|
109
|
+
echo "❌ Test 4: Should reject short project name"
|
|
110
|
+
FAILED=$((FAILED + 1))
|
|
111
|
+
fi
|
|
112
|
+
|
|
113
|
+
# Test 5: Short description (< 10 chars)
|
|
114
|
+
echo "Test 5: Short project description"
|
|
115
|
+
ARGS='{"project_name":"test-project","project_description":"Short"}'
|
|
116
|
+
RESP=$(send_mcp_request "mlgym_deploy" "$ARGS" 5)
|
|
117
|
+
TEXT=$(get_response_text "$RESP")
|
|
118
|
+
if is_error "$TEXT"; then
|
|
119
|
+
echo "✅ Test 5: Correctly rejects short description"
|
|
120
|
+
PASSED=$((PASSED + 1))
|
|
121
|
+
else
|
|
122
|
+
echo "❌ Test 5: Should reject short description"
|
|
123
|
+
FAILED=$((FAILED + 1))
|
|
124
|
+
fi
|
|
125
|
+
|
|
126
|
+
# Test 6: SQL injection in project_name
|
|
127
|
+
echo "Test 6: SQL injection attempt (project_name)"
|
|
128
|
+
ARGS='{"project_name":"test-project'\''--DROP","project_description":"Test deployment"}'
|
|
129
|
+
RESP=$(send_mcp_request "mlgym_deploy" "$ARGS" 6)
|
|
130
|
+
TEXT=$(get_response_text "$RESP")
|
|
131
|
+
if is_error "$TEXT"; then
|
|
132
|
+
echo "✅ Test 6: SQL injection prevented"
|
|
133
|
+
PASSED=$((PASSED + 1))
|
|
134
|
+
else
|
|
135
|
+
echo "⚠️ Test 6: SQL injection may not be fully validated"
|
|
136
|
+
FAILED=$((FAILED + 1))
|
|
137
|
+
fi
|
|
138
|
+
|
|
139
|
+
# Test 7: Path traversal in local_path
|
|
140
|
+
echo "Test 7: Path traversal attempt (local_path)"
|
|
141
|
+
ARGS='{"project_name":"test-project","project_description":"Test deployment","local_path":"../../../etc/passwd"}'
|
|
142
|
+
RESP=$(send_mcp_request "mlgym_deploy" "$ARGS" 7)
|
|
143
|
+
TEXT=$(get_response_text "$RESP")
|
|
144
|
+
# Path traversal might be caught later in the workflow
|
|
145
|
+
echo "⚠️ Test 7: Path traversal - result: $(echo "$TEXT" | jq -r '.status')"
|
|
146
|
+
PASSED=$((PASSED + 1))
|
|
147
|
+
|
|
148
|
+
echo ""
|
|
149
|
+
|
|
150
|
+
# =============================================================================
|
|
151
|
+
# Phase 2: Authentication Tests
|
|
152
|
+
# =============================================================================
|
|
153
|
+
|
|
154
|
+
echo "PHASE 2: Authentication Tests"
|
|
155
|
+
echo "============================="
|
|
156
|
+
echo ""
|
|
157
|
+
|
|
158
|
+
# Setup: Create test user for authenticated tests
|
|
159
|
+
TEST_EMAIL="deploy-test-$(date +%s)@ezb.net"
|
|
160
|
+
TEST_NAME="Deploy Test User"
|
|
161
|
+
TEST_PASSWORD="Xy9$Qm7!Bnz2@Kp4#2024"
|
|
162
|
+
|
|
163
|
+
echo "[SETUP] Creating test user: $TEST_EMAIL"
|
|
164
|
+
CREATE_ARGS="{\"email\":\"$TEST_EMAIL\",\"name\":\"$TEST_NAME\",\"password\":\"$TEST_PASSWORD\",\"accept_terms\":true}"
|
|
165
|
+
CREATE_RESP=$(send_mcp_request "mlgym_user_create" "$CREATE_ARGS" 100)
|
|
166
|
+
CREATE_TEXT=$(get_response_text "$CREATE_RESP")
|
|
167
|
+
USER_ID=$(echo "$CREATE_TEXT" | jq -r '.user_id' 2>/dev/null)
|
|
168
|
+
|
|
169
|
+
if [ -n "$USER_ID" ] && [ "$USER_ID" != "null" ]; then
|
|
170
|
+
echo "✅ Test user created (ID: $USER_ID)"
|
|
171
|
+
echo ""
|
|
172
|
+
else
|
|
173
|
+
echo "❌ Failed to create test user - some tests will be skipped"
|
|
174
|
+
echo ""
|
|
175
|
+
fi
|
|
176
|
+
|
|
177
|
+
# Test 8: Deployment without authentication (should fail or prompt for auth)
|
|
178
|
+
echo "Test 8: Deployment without authentication"
|
|
179
|
+
# Clear any existing auth
|
|
180
|
+
rm -f ~/.mlgym/mcp_config.json 2>/dev/null
|
|
181
|
+
ARGS='{"project_name":"test-deploy-noauth","project_description":"Test deployment without authentication"}'
|
|
182
|
+
RESP=$(send_mcp_request "mlgym_deploy" "$ARGS" 8)
|
|
183
|
+
TEXT=$(get_response_text "$RESP")
|
|
184
|
+
STATUS=$(echo "$TEXT" | jq -r '.status' 2>/dev/null)
|
|
185
|
+
if [ "$STATUS" = "error" ] || echo "$TEXT" | grep -qiE "authentication|not authenticated|login required"; then
|
|
186
|
+
echo "✅ Test 8: Correctly requires authentication"
|
|
187
|
+
PASSED=$((PASSED + 1))
|
|
188
|
+
else
|
|
189
|
+
echo "❌ Test 8: Should require authentication"
|
|
190
|
+
FAILED=$((FAILED + 1))
|
|
191
|
+
fi
|
|
192
|
+
|
|
193
|
+
# Test 9: Deployment with embedded authentication
|
|
194
|
+
echo "Test 9: Deployment with embedded authentication (email + password)"
|
|
195
|
+
if [ -n "$USER_ID" ] && [ "$USER_ID" != "null" ]; then
|
|
196
|
+
ARGS="{\"project_name\":\"test-deploy-auth\",\"project_description\":\"Test deployment with embedded auth\",\"email\":\"$TEST_EMAIL\",\"password\":\"$TEST_PASSWORD\"}"
|
|
197
|
+
RESP=$(send_mcp_request "mlgym_deploy" "$ARGS" 9)
|
|
198
|
+
TEXT=$(get_response_text "$RESP")
|
|
199
|
+
STATUS=$(echo "$TEXT" | jq -r '.status' 2>/dev/null)
|
|
200
|
+
|
|
201
|
+
# Deployment might fail for other reasons (no local project), but should authenticate
|
|
202
|
+
if [ "$STATUS" = "success" ] || [ "$STATUS" = "error" ]; then
|
|
203
|
+
# Check if error is about missing files, not authentication
|
|
204
|
+
if echo "$TEXT" | grep -qiE "authentication|not authenticated"; then
|
|
205
|
+
echo "❌ Test 9: Authentication with embedded creds failed"
|
|
206
|
+
FAILED=$((FAILED + 1))
|
|
207
|
+
else
|
|
208
|
+
echo "✅ Test 9: Authentication successful (deployment may have other issues)"
|
|
209
|
+
PASSED=$((PASSED + 1))
|
|
210
|
+
fi
|
|
211
|
+
else
|
|
212
|
+
echo "⚠️ Test 9: Unexpected response status: $STATUS"
|
|
213
|
+
FAILED=$((FAILED + 1))
|
|
214
|
+
fi
|
|
215
|
+
else
|
|
216
|
+
echo "⚠️ Test 9: Skipped (no test user)"
|
|
217
|
+
fi
|
|
218
|
+
|
|
219
|
+
echo ""
|
|
220
|
+
|
|
221
|
+
# =============================================================================
|
|
222
|
+
# Phase 3: Response Format Tests
|
|
223
|
+
# =============================================================================
|
|
224
|
+
|
|
225
|
+
echo "PHASE 3: Response Format Validation"
|
|
226
|
+
echo "==================================="
|
|
227
|
+
echo ""
|
|
228
|
+
|
|
229
|
+
# Test 10: Check response structure (using a deployment that will fail gracefully)
|
|
230
|
+
echo "Test 10: Response format validation"
|
|
231
|
+
if [ -n "$USER_ID" ] && [ "$USER_ID" != "null" ]; then
|
|
232
|
+
ARGS="{\"project_name\":\"format-test\",\"project_description\":\"Test response format\",\"email\":\"$TEST_EMAIL\",\"password\":\"$TEST_PASSWORD\"}"
|
|
233
|
+
RESP=$(send_mcp_request "mlgym_deploy" "$ARGS" 10)
|
|
234
|
+
TEXT=$(get_response_text "$RESP")
|
|
235
|
+
|
|
236
|
+
HAS_STATUS=$(echo "$TEXT" | jq -e '.status' > /dev/null 2>&1 && echo "yes" || echo "no")
|
|
237
|
+
HAS_MESSAGE=$(echo "$TEXT" | jq -e '.message' > /dev/null 2>&1 && echo "yes" || echo "no")
|
|
238
|
+
HAS_WORKFLOW=$(echo "$TEXT" | jq -e '.workflow_steps' > /dev/null 2>&1 && echo "yes" || echo "no")
|
|
239
|
+
|
|
240
|
+
if [ "$HAS_STATUS" = "yes" ] && [ "$HAS_MESSAGE" = "yes" ] && [ "$HAS_WORKFLOW" = "yes" ]; then
|
|
241
|
+
echo "✅ Test 10: Response format valid (status, message, workflow_steps)"
|
|
242
|
+
PASSED=$((PASSED + 1))
|
|
243
|
+
else
|
|
244
|
+
echo "❌ Test 10: Response format invalid (status:$HAS_STATUS, message:$HAS_MESSAGE, workflow:$HAS_WORKFLOW)"
|
|
245
|
+
FAILED=$((FAILED + 1))
|
|
246
|
+
fi
|
|
247
|
+
else
|
|
248
|
+
echo "⚠️ Test 10: Skipped (no test user)"
|
|
249
|
+
fi
|
|
250
|
+
|
|
251
|
+
echo ""
|
|
252
|
+
|
|
253
|
+
# =============================================================================
|
|
254
|
+
# Phase 4: Security Tests
|
|
255
|
+
# =============================================================================
|
|
256
|
+
|
|
257
|
+
echo "PHASE 4: Security Tests"
|
|
258
|
+
echo "======================="
|
|
259
|
+
echo ""
|
|
260
|
+
|
|
261
|
+
# Test 11: XSS attempt in project_description
|
|
262
|
+
echo "Test 11: XSS attempt (project_description)"
|
|
263
|
+
ARGS='{"project_name":"xss-test","project_description":"<script>alert('"'"'XSS'"'"')</script>"}'
|
|
264
|
+
RESP=$(send_mcp_request "mlgym_deploy" "$ARGS" 11)
|
|
265
|
+
TEXT=$(get_response_text "$RESP")
|
|
266
|
+
# XSS should be sanitized or escaped - test is informational
|
|
267
|
+
echo "⚠️ Test 11: XSS test - status: $(echo "$TEXT" | jq -r '.status')"
|
|
268
|
+
PASSED=$((PASSED + 1))
|
|
269
|
+
|
|
270
|
+
# Test 12: Buffer overflow (very long project_name)
|
|
271
|
+
echo "Test 12: Buffer overflow (very long project_name)"
|
|
272
|
+
LONG_NAME=$(python3 -c "print('a' * 1000)")
|
|
273
|
+
ARGS="{\"project_name\":\"$LONG_NAME\",\"project_description\":\"Test buffer overflow\"}"
|
|
274
|
+
RESP=$(send_mcp_request "mlgym_deploy" "$ARGS" 12)
|
|
275
|
+
TEXT=$(get_response_text "$RESP")
|
|
276
|
+
if is_error "$TEXT"; then
|
|
277
|
+
echo "✅ Test 12: Long input rejected"
|
|
278
|
+
PASSED=$((PASSED + 1))
|
|
279
|
+
else
|
|
280
|
+
echo "⚠️ Test 12: Long input may have been accepted"
|
|
281
|
+
FAILED=$((FAILED + 1))
|
|
282
|
+
fi
|
|
283
|
+
|
|
284
|
+
# Test 13: Invalid project_type override
|
|
285
|
+
echo "Test 13: Invalid project_type override"
|
|
286
|
+
ARGS='{"project_name":"invalid-type","project_description":"Test invalid type","project_type":"invalid"}'
|
|
287
|
+
RESP=$(send_mcp_request "mlgym_deploy" "$ARGS" 13)
|
|
288
|
+
TEXT=$(get_response_text "$RESP")
|
|
289
|
+
# Should be validated by inputSchema, but check
|
|
290
|
+
if is_error "$TEXT"; then
|
|
291
|
+
echo "✅ Test 13: Invalid project_type rejected"
|
|
292
|
+
PASSED=$((PASSED + 1))
|
|
293
|
+
else
|
|
294
|
+
echo "⚠️ Test 13: Invalid project_type handling unclear"
|
|
295
|
+
FAILED=$((FAILED + 1))
|
|
296
|
+
fi
|
|
297
|
+
|
|
298
|
+
# Test 14: Invalid framework override
|
|
299
|
+
echo "Test 14: Invalid framework override"
|
|
300
|
+
ARGS='{"project_name":"invalid-framework","project_description":"Test invalid framework","framework":"invalid"}'
|
|
301
|
+
RESP=$(send_mcp_request "mlgym_deploy" "$ARGS" 14)
|
|
302
|
+
TEXT=$(get_response_text "$RESP")
|
|
303
|
+
if is_error "$TEXT"; then
|
|
304
|
+
echo "✅ Test 14: Invalid framework rejected"
|
|
305
|
+
PASSED=$((PASSED + 1))
|
|
306
|
+
else
|
|
307
|
+
echo "⚠️ Test 14: Invalid framework handling unclear"
|
|
308
|
+
FAILED=$((FAILED + 1))
|
|
309
|
+
fi
|
|
310
|
+
|
|
311
|
+
echo ""
|
|
312
|
+
|
|
313
|
+
# =============================================================================
|
|
314
|
+
# Summary
|
|
315
|
+
# =============================================================================
|
|
316
|
+
|
|
317
|
+
echo "==============================================="
|
|
318
|
+
echo "RESULTS: $PASSED passed, $FAILED failed (Total: $((PASSED + FAILED)))"
|
|
319
|
+
echo "==============================================="
|
|
320
|
+
echo ""
|
|
321
|
+
echo "NOTE: mlgym_deploy is a complex end-to-end tool."
|
|
322
|
+
echo "Full integration testing requires:"
|
|
323
|
+
echo " - Valid project directory with code"
|
|
324
|
+
echo " - GitLab connectivity"
|
|
325
|
+
echo " - Coolify connectivity"
|
|
326
|
+
echo " - Fresh test account"
|
|
327
|
+
echo ""
|
|
328
|
+
echo "These tests validate:"
|
|
329
|
+
echo " ✓ Input validation"
|
|
330
|
+
echo " ✓ Authentication flow"
|
|
331
|
+
echo " ✓ Response format"
|
|
332
|
+
echo " ✓ Security (XSS, injection, overflow)"
|
|
333
|
+
echo ""
|
|
334
|
+
|
|
335
|
+
if [ $FAILED -eq 0 ]; then
|
|
336
|
+
echo "✅ ALL TESTS PASSED"
|
|
337
|
+
exit 0
|
|
338
|
+
else
|
|
339
|
+
echo "⚠️ SOME TESTS FAILED"
|
|
340
|
+
exit 1
|
|
341
|
+
fi
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Comprehensive test suite for mlgym_status
|
|
3
|
+
# Tests status checking functionality for authentication and project configuration
|
|
4
|
+
|
|
5
|
+
set -e
|
|
6
|
+
|
|
7
|
+
PASSED=0
|
|
8
|
+
FAILED=0
|
|
9
|
+
|
|
10
|
+
# Get script directory to find index.js
|
|
11
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
12
|
+
cd "$SCRIPT_DIR/.."
|
|
13
|
+
|
|
14
|
+
echo "=== mlgym_status Comprehensive Test Suite ==="
|
|
15
|
+
echo ""
|
|
16
|
+
|
|
17
|
+
# Helper function to send MCP request
|
|
18
|
+
send_mcp_request() {
|
|
19
|
+
local tool_name="$1"
|
|
20
|
+
local arguments="$2"
|
|
21
|
+
local request_id="${3:-99}"
|
|
22
|
+
|
|
23
|
+
local init_request='{"jsonrpc":"2.0","method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"status-test","version":"1.0.0"}},"id":0}'
|
|
24
|
+
local compact_args=$(echo "$arguments" | jq -c .)
|
|
25
|
+
local request="{\"jsonrpc\":\"2.0\",\"method\":\"tools/call\",\"params\":{\"name\":\"$tool_name\",\"arguments\":$compact_args},\"id\":$request_id}"
|
|
26
|
+
|
|
27
|
+
local temp_input="/tmp/mcp_status_test_$$.txt"
|
|
28
|
+
printf "%s\n%s\n" "$init_request" "$request" > "$temp_input"
|
|
29
|
+
|
|
30
|
+
local response=$(cat "$temp_input" | node index.js 2>/dev/null | jq -c "select(.id == $request_id)")
|
|
31
|
+
rm -f "$temp_input"
|
|
32
|
+
|
|
33
|
+
echo "$response"
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
# Helper function to extract response text
|
|
37
|
+
get_response_text() {
|
|
38
|
+
local response="$1"
|
|
39
|
+
echo "$response" | jq -r '.result.content[0].text // empty' 2>/dev/null
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
# =============================================================================
|
|
43
|
+
# Phase 1: Basic Functionality Tests
|
|
44
|
+
# =============================================================================
|
|
45
|
+
|
|
46
|
+
echo "PHASE 1: Basic Functionality Tests"
|
|
47
|
+
echo "==================================="
|
|
48
|
+
echo ""
|
|
49
|
+
|
|
50
|
+
# Test 1: Status check without authentication
|
|
51
|
+
echo "Test 1: Status check without authentication"
|
|
52
|
+
rm -f ~/.mlgym/mcp_config.json 2>/dev/null
|
|
53
|
+
ARGS='{}'
|
|
54
|
+
RESP=$(send_mcp_request "mlgym_status" "$ARGS" 1)
|
|
55
|
+
TEXT=$(get_response_text "$RESP")
|
|
56
|
+
|
|
57
|
+
STATUS=$(echo "$TEXT" | jq -r '.status' 2>/dev/null)
|
|
58
|
+
HAS_AUTH=$(echo "$TEXT" | jq -e '.authentication' > /dev/null 2>&1 && echo "yes" || echo "no")
|
|
59
|
+
IS_AUTHENTICATED=$(echo "$TEXT" | jq -r '.authentication.authenticated' 2>/dev/null)
|
|
60
|
+
|
|
61
|
+
if [ "$STATUS" = "ok" ] && [ "$HAS_AUTH" = "yes" ] && [ "$IS_AUTHENTICATED" = "false" ]; then
|
|
62
|
+
echo "✅ Test 1: Status shows not authenticated correctly"
|
|
63
|
+
PASSED=$((PASSED + 1))
|
|
64
|
+
else
|
|
65
|
+
echo "❌ Test 1: Status should show not authenticated (status:$STATUS, has_auth:$HAS_AUTH, authenticated:$IS_AUTHENTICATED)"
|
|
66
|
+
FAILED=$((FAILED + 1))
|
|
67
|
+
fi
|
|
68
|
+
|
|
69
|
+
# Test 2: Response format validation (unauthenticated)
|
|
70
|
+
echo "Test 2: Response format validation (unauthenticated)"
|
|
71
|
+
HAS_STATUS=$(echo "$TEXT" | jq -e '.status' > /dev/null 2>&1 && echo "yes" || echo "no")
|
|
72
|
+
HAS_AUTH=$(echo "$TEXT" | jq -e '.authentication' > /dev/null 2>&1 && echo "yes" || echo "no")
|
|
73
|
+
HAS_PROJECT=$(echo "$TEXT" | jq -e '.project' > /dev/null 2>&1 && echo "yes" || echo "no")
|
|
74
|
+
|
|
75
|
+
if [ "$HAS_STATUS" = "yes" ] && [ "$HAS_AUTH" = "yes" ] && [ "$HAS_PROJECT" = "yes" ]; then
|
|
76
|
+
echo "✅ Test 2: Response format valid (status, authentication, project)"
|
|
77
|
+
PASSED=$((PASSED + 1))
|
|
78
|
+
else
|
|
79
|
+
echo "❌ Test 2: Response format invalid (status:$HAS_STATUS, auth:$HAS_AUTH, project:$HAS_PROJECT)"
|
|
80
|
+
FAILED=$((FAILED + 1))
|
|
81
|
+
fi
|
|
82
|
+
|
|
83
|
+
# Test 3: Project shows not configured
|
|
84
|
+
echo "Test 3: Project shows not configured when no git remote"
|
|
85
|
+
PROJECT_CONFIGURED=$(echo "$TEXT" | jq -r '.project.configured' 2>/dev/null)
|
|
86
|
+
if [ "$PROJECT_CONFIGURED" = "false" ]; then
|
|
87
|
+
echo "✅ Test 3: Project correctly shows not configured"
|
|
88
|
+
PASSED=$((PASSED + 1))
|
|
89
|
+
else
|
|
90
|
+
echo "⚠️ Test 3: Project may show configured (could be in a project directory)"
|
|
91
|
+
PASSED=$((PASSED + 1))
|
|
92
|
+
fi
|
|
93
|
+
|
|
94
|
+
echo ""
|
|
95
|
+
|
|
96
|
+
# =============================================================================
|
|
97
|
+
# Phase 2: Authenticated Status Tests
|
|
98
|
+
# =============================================================================
|
|
99
|
+
|
|
100
|
+
echo "PHASE 2: Authenticated Status Tests"
|
|
101
|
+
echo "===================================="
|
|
102
|
+
echo ""
|
|
103
|
+
|
|
104
|
+
# Setup: Create test user
|
|
105
|
+
TEST_EMAIL="test-$(date +%s)@ezb.net"
|
|
106
|
+
TEST_NAME="Status Test User"
|
|
107
|
+
TEST_PASSWORD="Xy9$Qm7!Bnz2@Kp4#2024"
|
|
108
|
+
|
|
109
|
+
echo "[SETUP] Creating test user: $TEST_EMAIL"
|
|
110
|
+
CREATE_ARGS="{\"email\":\"$TEST_EMAIL\",\"name\":\"$TEST_NAME\",\"password\":\"$TEST_PASSWORD\",\"accept_terms\":true}"
|
|
111
|
+
CREATE_RESP=$(send_mcp_request "mlgym_user_create" "$CREATE_ARGS" 100)
|
|
112
|
+
CREATE_TEXT=$(get_response_text "$CREATE_RESP")
|
|
113
|
+
USER_ID=$(echo "$CREATE_TEXT" | jq -r '.user_id' 2>/dev/null)
|
|
114
|
+
|
|
115
|
+
if [ -n "$USER_ID" ] && [ "$USER_ID" != "null" ]; then
|
|
116
|
+
echo "✅ Test user created (ID: $USER_ID)"
|
|
117
|
+
echo ""
|
|
118
|
+
|
|
119
|
+
# Test 4: Status check with authentication
|
|
120
|
+
echo "Test 4: Status check with authentication"
|
|
121
|
+
ARGS='{}'
|
|
122
|
+
RESP=$(send_mcp_request "mlgym_status" "$ARGS" 4)
|
|
123
|
+
TEXT=$(get_response_text "$RESP")
|
|
124
|
+
|
|
125
|
+
IS_AUTHENTICATED=$(echo "$TEXT" | jq -r '.authentication.authenticated' 2>/dev/null)
|
|
126
|
+
AUTH_EMAIL=$(echo "$TEXT" | jq -r '.authentication.email' 2>/dev/null)
|
|
127
|
+
|
|
128
|
+
if [ "$IS_AUTHENTICATED" = "true" ] && [ "$AUTH_EMAIL" = "$TEST_EMAIL" ]; then
|
|
129
|
+
echo "✅ Test 4: Status shows authenticated with correct email"
|
|
130
|
+
PASSED=$((PASSED + 1))
|
|
131
|
+
else
|
|
132
|
+
echo "❌ Test 4: Status should show authenticated (authenticated:$IS_AUTHENTICATED, email:$AUTH_EMAIL)"
|
|
133
|
+
FAILED=$((FAILED + 1))
|
|
134
|
+
fi
|
|
135
|
+
|
|
136
|
+
# Test 5: Status includes user email
|
|
137
|
+
echo "Test 5: Status includes user email"
|
|
138
|
+
if [ "$AUTH_EMAIL" = "$TEST_EMAIL" ]; then
|
|
139
|
+
echo "✅ Test 5: User email correct"
|
|
140
|
+
PASSED=$((PASSED + 1))
|
|
141
|
+
else
|
|
142
|
+
echo "❌ Test 5: User email mismatch (expected: $TEST_EMAIL, got: $AUTH_EMAIL)"
|
|
143
|
+
FAILED=$((FAILED + 1))
|
|
144
|
+
fi
|
|
145
|
+
else
|
|
146
|
+
echo "❌ Failed to create test user - skipping authenticated tests"
|
|
147
|
+
echo ""
|
|
148
|
+
fi
|
|
149
|
+
|
|
150
|
+
echo ""
|
|
151
|
+
|
|
152
|
+
# =============================================================================
|
|
153
|
+
# Phase 3: Optional Parameters Tests
|
|
154
|
+
# =============================================================================
|
|
155
|
+
|
|
156
|
+
echo "PHASE 3: Optional Parameters Tests"
|
|
157
|
+
echo "==================================="
|
|
158
|
+
echo ""
|
|
159
|
+
|
|
160
|
+
# Test 6: Status with custom local_path
|
|
161
|
+
echo "Test 6: Status with custom local_path"
|
|
162
|
+
ARGS='{"local_path":"."}'
|
|
163
|
+
RESP=$(send_mcp_request "mlgym_status" "$ARGS" 6)
|
|
164
|
+
TEXT=$(get_response_text "$RESP")
|
|
165
|
+
STATUS=$(echo "$TEXT" | jq -r '.status' 2>/dev/null)
|
|
166
|
+
|
|
167
|
+
if [ "$STATUS" = "ok" ]; then
|
|
168
|
+
echo "✅ Test 6: Status accepts custom local_path"
|
|
169
|
+
PASSED=$((PASSED + 1))
|
|
170
|
+
else
|
|
171
|
+
echo "❌ Test 6: Status should accept custom local_path"
|
|
172
|
+
FAILED=$((FAILED + 1))
|
|
173
|
+
fi
|
|
174
|
+
|
|
175
|
+
# Test 7: Status with non-existent path
|
|
176
|
+
echo "Test 7: Status with non-existent path"
|
|
177
|
+
ARGS='{"local_path":"/non/existent/path"}'
|
|
178
|
+
RESP=$(send_mcp_request "mlgym_status" "$ARGS" 7)
|
|
179
|
+
TEXT=$(get_response_text "$RESP")
|
|
180
|
+
STATUS=$(echo "$TEXT" | jq -r '.status' 2>/dev/null)
|
|
181
|
+
|
|
182
|
+
# Should still return status (ok or error)
|
|
183
|
+
if [ "$STATUS" = "ok" ] || [ "$STATUS" = "error" ]; then
|
|
184
|
+
echo "✅ Test 7: Status handles non-existent path gracefully"
|
|
185
|
+
PASSED=$((PASSED + 1))
|
|
186
|
+
else
|
|
187
|
+
echo "❌ Test 7: Status should handle non-existent path"
|
|
188
|
+
FAILED=$((FAILED + 1))
|
|
189
|
+
fi
|
|
190
|
+
|
|
191
|
+
# Test 8: Status with path traversal attempt
|
|
192
|
+
echo "Test 8: Status with path traversal attempt"
|
|
193
|
+
ARGS='{"local_path":"../../../etc/passwd"}'
|
|
194
|
+
RESP=$(send_mcp_request "mlgym_status" "$ARGS" 8)
|
|
195
|
+
TEXT=$(get_response_text "$RESP")
|
|
196
|
+
STATUS=$(echo "$TEXT" | jq -r '.status' 2>/dev/null)
|
|
197
|
+
|
|
198
|
+
# Should handle gracefully (either ok or error, not crash)
|
|
199
|
+
if [ "$STATUS" = "ok" ] || [ "$STATUS" = "error" ]; then
|
|
200
|
+
echo "✅ Test 8: Path traversal handled gracefully"
|
|
201
|
+
PASSED=$((PASSED + 1))
|
|
202
|
+
else
|
|
203
|
+
echo "❌ Test 8: Should handle path traversal"
|
|
204
|
+
FAILED=$((FAILED + 1))
|
|
205
|
+
fi
|
|
206
|
+
|
|
207
|
+
echo ""
|
|
208
|
+
|
|
209
|
+
# =============================================================================
|
|
210
|
+
# Phase 4: Project Analysis Tests
|
|
211
|
+
# =============================================================================
|
|
212
|
+
|
|
213
|
+
echo "PHASE 4: Project Analysis Tests"
|
|
214
|
+
echo "================================"
|
|
215
|
+
echo ""
|
|
216
|
+
|
|
217
|
+
# Test 9: Status includes analysis section
|
|
218
|
+
echo "Test 9: Status includes analysis section"
|
|
219
|
+
ARGS='{"local_path":"."}'
|
|
220
|
+
RESP=$(send_mcp_request "mlgym_status" "$ARGS" 9)
|
|
221
|
+
TEXT=$(get_response_text "$RESP")
|
|
222
|
+
|
|
223
|
+
HAS_ANALYSIS=$(echo "$TEXT" | jq -e '.analysis' > /dev/null 2>&1 && echo "yes" || echo "no")
|
|
224
|
+
ANALYSIS_VALUE=$(echo "$TEXT" | jq -r '.analysis' 2>/dev/null)
|
|
225
|
+
|
|
226
|
+
if [ "$HAS_ANALYSIS" = "yes" ]; then
|
|
227
|
+
if [ "$ANALYSIS_VALUE" = "null" ]; then
|
|
228
|
+
echo "✅ Test 9: Analysis section present (null for non-project directory)"
|
|
229
|
+
PASSED=$((PASSED + 1))
|
|
230
|
+
else
|
|
231
|
+
echo "✅ Test 9: Analysis section present and populated"
|
|
232
|
+
PASSED=$((PASSED + 1))
|
|
233
|
+
fi
|
|
234
|
+
else
|
|
235
|
+
echo "❌ Test 9: Analysis section missing from response"
|
|
236
|
+
FAILED=$((FAILED + 1))
|
|
237
|
+
fi
|
|
238
|
+
|
|
239
|
+
# Test 10: Read-only operation (no side effects)
|
|
240
|
+
echo "Test 10: Read-only operation verification"
|
|
241
|
+
# mlgym_status should never modify anything - just check it doesn't error
|
|
242
|
+
BEFORE_FILES=$(ls -la ~/.mlgym/ 2>/dev/null | wc -l)
|
|
243
|
+
ARGS='{}'
|
|
244
|
+
RESP=$(send_mcp_request "mlgym_status" "$ARGS" 10)
|
|
245
|
+
TEXT=$(get_response_text "$RESP")
|
|
246
|
+
AFTER_FILES=$(ls -la ~/.mlgym/ 2>/dev/null | wc -l)
|
|
247
|
+
|
|
248
|
+
# File count should be same (or minimal difference for logging)
|
|
249
|
+
FILE_DIFF=$((AFTER_FILES - BEFORE_FILES))
|
|
250
|
+
if [ $FILE_DIFF -le 1 ]; then
|
|
251
|
+
echo "✅ Test 10: No unexpected file modifications (diff: $FILE_DIFF)"
|
|
252
|
+
PASSED=$((PASSED + 1))
|
|
253
|
+
else
|
|
254
|
+
echo "⚠️ Test 10: Unexpected files created (diff: $FILE_DIFF)"
|
|
255
|
+
FAILED=$((FAILED + 1))
|
|
256
|
+
fi
|
|
257
|
+
|
|
258
|
+
echo ""
|
|
259
|
+
|
|
260
|
+
# =============================================================================
|
|
261
|
+
# Summary
|
|
262
|
+
# =============================================================================
|
|
263
|
+
|
|
264
|
+
echo "==============================================="
|
|
265
|
+
echo "RESULTS: $PASSED passed, $FAILED failed (Total: $((PASSED + FAILED)))"
|
|
266
|
+
echo "==============================================="
|
|
267
|
+
echo ""
|
|
268
|
+
echo "NOTE: mlgym_status is a read-only status tool."
|
|
269
|
+
echo "It checks:"
|
|
270
|
+
echo " ✓ Authentication status"
|
|
271
|
+
echo " ✓ Project configuration (git remote)"
|
|
272
|
+
echo " ✓ Project analysis (type, framework)"
|
|
273
|
+
echo ""
|
|
274
|
+
|
|
275
|
+
if [ $FAILED -eq 0 ]; then
|
|
276
|
+
echo "✅ ALL TESTS PASSED"
|
|
277
|
+
exit 0
|
|
278
|
+
else
|
|
279
|
+
echo "⚠️ SOME TESTS FAILED"
|
|
280
|
+
exit 1
|
|
281
|
+
fi
|