mlgym-deploy 2.0.0

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/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "mlgym-deploy",
3
+ "version": "2.0.0",
4
+ "description": "MCP server for GitLab Backend - User creation and project deployment",
5
+ "main": "index.js",
6
+ "type": "module",
7
+ "bin": {
8
+ "mlgym-mcp": "./index.js"
9
+ },
10
+ "scripts": {
11
+ "start": "node index.js"
12
+ },
13
+ "keywords": [
14
+ "mcp",
15
+ "gitlab",
16
+ "coolify",
17
+ "deployment",
18
+ "model-context-protocol",
19
+ "mlgym"
20
+ ],
21
+ "author": "MLGym Team",
22
+ "license": "MIT",
23
+ "repository": {
24
+ "type": "git",
25
+ "url": "git@code.stratus5.com:chka/gitlab_backend.git"
26
+ },
27
+ "dependencies": {
28
+ "@modelcontextprotocol/sdk": "^0.6.0",
29
+ "axios": "^1.6.0"
30
+ },
31
+ "mcp": {
32
+ "name": "GitLab Backend MCP",
33
+ "description": "Deploy applications to GitLab and Coolify with two simple commands",
34
+ "tools": [
35
+ {
36
+ "name": "mlgym_user_create",
37
+ "description": "Create a new user with GitLab, Coolify, and SSH key setup"
38
+ },
39
+ {
40
+ "name": "mlgym_project_init",
41
+ "description": "Initialize a project with GitLab repository and Coolify deployment"
42
+ }
43
+ ]
44
+ }
45
+ }
@@ -0,0 +1,90 @@
1
+ # MCP Server Test Suite
2
+
3
+ Comprehensive tests for all MCP server functions.
4
+
5
+ ## Directory Structure
6
+
7
+ ```
8
+ tests/
9
+ ├── README.md # This file
10
+ ├── run-all-tests.sh # Master test runner
11
+ ├── mlgym_auth_login_test.sh # ✅ 15 tests (auth_login)
12
+ ├── mlgym_user_create_test.sh # ✅ 20 tests (user_create)
13
+ ├── mlgym_auth_logout_test.sh # ✅ 8 tests (auth_logout)
14
+ ├── mlgym_project_init_test.sh # ✅ 6 tests (project_init)
15
+ ├── mlgym_projects_list_test.sh # ✅ 6 tests (projects_list)
16
+ └── mlgym_projects_get_test.sh # ✅ 7 tests (projects_get)
17
+ ```
18
+
19
+ ## Running Tests
20
+
21
+ ### Run All Tests
22
+ ```bash
23
+ cd mcp-server
24
+ ./tests/run-all-tests.sh
25
+ ```
26
+
27
+ ### Run Individual Test
28
+ ```bash
29
+ cd mcp-server
30
+ ./tests/mlgym_auth_login_test.sh
31
+ ```
32
+
33
+ ## Test Coverage
34
+
35
+ ### Completed (6 functions, 62 total tests)
36
+ - ✅ **mlgym_auth_login** (15 tests)
37
+ - Valid/invalid credentials, SQL injection, XSS, buffer overflow, token storage
38
+ - ✅ **mlgym_user_create** (20 tests)
39
+ - Required fields, validation, security, SSH key generation, duplicates
40
+ - ✅ **mlgym_auth_logout** (8 tests)
41
+ - Session cleanup, double logout, config file deletion
42
+ - ✅ **mlgym_project_init** (6 tests)
43
+ - Project creation, deployment setup, webhook configuration
44
+ - ✅ **mlgym_projects_list** (6 tests)
45
+ - List projects, user isolation, response format
46
+ - ✅ **mlgym_projects_get** (7 tests)
47
+ - Get project details, validation, non-existent projects
48
+
49
+ ### Remaining (37 functions)
50
+ See TODO.md for priority order and testing plan.
51
+
52
+ ## Test Format
53
+
54
+ Each test file follows this structure:
55
+ ```bash
56
+ #!/bin/bash
57
+ set -e
58
+
59
+ PASSED=0
60
+ FAILED=0
61
+
62
+ # Test 1: Description
63
+ if [condition]; then
64
+ echo "✅ Test 1: Description"
65
+ PASSED=$((PASSED + 1))
66
+ else
67
+ echo "❌ Test 1: Description"
68
+ FAILED=$((FAILED + 1))
69
+ fi
70
+
71
+ # Final summary
72
+ echo "RESULTS: $PASSED passed, $FAILED failed"
73
+ ```
74
+
75
+ ## Test Categories
76
+
77
+ Each test file covers:
78
+ 1. **Authentication** - Verify auth requirements
79
+ 2. **Validation** - Required/optional parameters
80
+ 3. **Edge Cases** - Empty strings, null values, extreme inputs
81
+ 4. **Security** - SQL injection, XSS, buffer overflow
82
+ 5. **Functional** - Core functionality with real API calls
83
+ 6. **Error Handling** - Invalid inputs, missing data
84
+
85
+ ## Requirements
86
+
87
+ - Node.js with MCP server running
88
+ - Backend server at https://backend.eu.ezb.net
89
+ - jq for JSON parsing
90
+ - bash 4.0+
@@ -0,0 +1,170 @@
1
+ #!/bin/bash
2
+ # Comprehensive test suite for mlgym_auth_login
3
+ # Tests functional requirements, edge cases, and security
4
+
5
+ set -e
6
+
7
+ TEST_EMAIL="fulltest-$(date +%s)@example.com"
8
+ TEST_PASS="MyV3ryC0mpl3x!P@ssw0rd#2024"
9
+ PASSED=0
10
+ FAILED=0
11
+
12
+ echo "=== mlgym_auth_login Comprehensive Test Suite ==="
13
+ echo ""
14
+
15
+ # Setup: Create test user
16
+ echo "[SETUP] Creating test user: $TEST_EMAIL"
17
+ CREATE_REQ="{\"jsonrpc\":\"2.0\",\"method\":\"tools/call\",\"params\":{\"name\":\"mlgym_user_create\",\"arguments\":{\"email\":\"$TEST_EMAIL\",\"name\":\"Full Test User\",\"password\":\"$TEST_PASS\",\"accept_terms\":true}},\"id\":1}"
18
+ RESP=$(echo "$CREATE_REQ" | node index.js 2>/dev/null | tail -1)
19
+ USER_ID=$(echo "$RESP" | jq -r '.result.content[0].text' | jq -r '.user_id')
20
+ if [ -n "$USER_ID" ] && [ "$USER_ID" != "null" ]; then
21
+ echo "✅ Test user created (ID: $USER_ID)"
22
+ else
23
+ echo "❌ Failed to create test user"
24
+ exit 1
25
+ fi
26
+ echo ""
27
+
28
+ # Helper to run test
29
+ run_test() {
30
+ local test_name="$1"
31
+ local email="$2"
32
+ local password="$3"
33
+ local expected_status="$4"
34
+
35
+ local req="{\"jsonrpc\":\"2.0\",\"method\":\"tools/call\",\"params\":{\"name\":\"mlgym_auth_login\",\"arguments\":{\"email\":\"$email\",\"password\":\"$password\"}},\"id\":99}"
36
+ local resp=$(echo "$req" | node index.js 2>/dev/null | tail -1)
37
+ local status=$(echo "$resp" | jq -r '.result.content[0].text' | jq -r '.status')
38
+
39
+ if [ "$status" = "$expected_status" ]; then
40
+ echo "✅ $test_name"
41
+ PASSED=$((PASSED + 1))
42
+ else
43
+ echo "❌ $test_name (expected: $expected_status, got: $status)"
44
+ FAILED=$((FAILED + 1))
45
+ fi
46
+ }
47
+
48
+ # Test 1: Valid login
49
+ run_test "Test 1: Valid login" "$TEST_EMAIL" "$TEST_PASS" "success"
50
+
51
+ # Test 2: Wrong password
52
+ run_test "Test 2: Wrong password" "$TEST_EMAIL" "WrongPass123!" "error"
53
+
54
+ # Test 3: Non-existent user
55
+ run_test "Test 3: Non-existent user" "fake999@example.com" "Pass123!" "error"
56
+
57
+ # Test 4: Empty email
58
+ run_test "Test 4: Empty email" "" "$TEST_PASS" "error"
59
+
60
+ # Test 5: Empty password
61
+ run_test "Test 5: Empty password" "$TEST_EMAIL" "" "error"
62
+
63
+ # Test 6: Invalid email format
64
+ run_test "Test 6: Invalid email format" "not-an-email" "Pass123!" "error"
65
+
66
+ # Test 7: SQL injection in email
67
+ run_test "Test 7: SQL injection (email)" "admin@test.com OR 1=1--" "Pass123!" "error"
68
+
69
+ # Test 8: SQL injection in password
70
+ run_test "Test 8: SQL injection (password)" "$TEST_EMAIL" "' OR '1'='1" "error"
71
+
72
+ # Test 9: XSS attempt
73
+ run_test "Test 9: XSS attempt" "<script>alert(1)</script>@test.com" "Pass123!" "error"
74
+
75
+ # Test 10: Missing password parameter
76
+ echo "Test 10: Missing password parameter"
77
+ REQ="{\"jsonrpc\":\"2.0\",\"method\":\"tools/call\",\"params\":{\"name\":\"mlgym_auth_login\",\"arguments\":{\"email\":\"$TEST_EMAIL\"}},\"id\":100}"
78
+ RESP=$(echo "$REQ" | node index.js 2>/dev/null | tail -1)
79
+ STATUS=$(echo "$RESP" | jq -r '.result.content[0].text' | jq -r '.status')
80
+ if [ "$STATUS" = "error" ]; then
81
+ echo "✅ Test 10: Missing password"
82
+ PASSED=$((PASSED + 1))
83
+ else
84
+ echo "❌ Test 10: Should reject missing password"
85
+ FAILED=$((FAILED + 1))
86
+ fi
87
+
88
+ # Test 11: Missing email parameter
89
+ echo "Test 11: Missing email parameter"
90
+ REQ="{\"jsonrpc\":\"2.0\",\"method\":\"tools/call\",\"params\":{\"name\":\"mlgym_auth_login\",\"arguments\":{\"password\":\"$TEST_PASS\"}},\"id\":101}"
91
+ RESP=$(echo "$REQ" | node index.js 2>/dev/null | tail -1)
92
+ STATUS=$(echo "$RESP" | jq -r '.result.content[0].text' | jq -r '.status')
93
+ if [ "$STATUS" = "error" ]; then
94
+ echo "✅ Test 11: Missing email"
95
+ PASSED=$((PASSED + 1))
96
+ else
97
+ echo "❌ Test 11: Should reject missing email"
98
+ FAILED=$((FAILED + 1))
99
+ fi
100
+
101
+ # Test 12: Token storage
102
+ echo "Test 12: Token storage verification"
103
+ if [ -f ~/.mlgym/mcp_config.json ]; then
104
+ TOKEN=$(jq -r '.token' ~/.mlgym/mcp_config.json)
105
+ EMAIL=$(jq -r '.email' ~/.mlgym/mcp_config.json)
106
+ if [ -n "$TOKEN" ] && [ "$TOKEN" != "null" ] && [ "$EMAIL" = "$TEST_EMAIL" ]; then
107
+ echo "✅ Test 12: Token stored correctly"
108
+ PASSED=$((PASSED + 1))
109
+ else
110
+ echo "❌ Test 12: Token not stored properly"
111
+ FAILED=$((FAILED + 1))
112
+ fi
113
+ else
114
+ echo "❌ Test 12: Config file not created"
115
+ FAILED=$((FAILED + 1))
116
+ fi
117
+
118
+ # Test 13: Response format validation
119
+ echo "Test 13: Response format validation"
120
+ REQ="{\"jsonrpc\":\"2.0\",\"method\":\"tools/call\",\"params\":{\"name\":\"mlgym_auth_login\",\"arguments\":{\"email\":\"$TEST_EMAIL\",\"password\":\"$TEST_PASS\"}},\"id\":102}"
121
+ RESP=$(echo "$REQ" | node index.js 2>/dev/null | tail -1)
122
+ CONTENT=$(echo "$RESP" | jq -r '.result.content[0].text')
123
+ HAS_STATUS=$(echo "$CONTENT" | jq -e '.status' > /dev/null 2>&1 && echo "yes" || echo "no")
124
+ HAS_TOKEN=$(echo "$CONTENT" | jq -e '.token' > /dev/null 2>&1 && echo "yes" || echo "no")
125
+ HAS_USER=$(echo "$CONTENT" | jq -e '.user.email' > /dev/null 2>&1 && echo "yes" || echo "no")
126
+ if [ "$HAS_STATUS" = "yes" ] && [ "$HAS_TOKEN" = "yes" ] && [ "$HAS_USER" = "yes" ]; then
127
+ echo "✅ Test 13: Response format valid"
128
+ PASSED=$((PASSED + 1))
129
+ else
130
+ echo "❌ Test 13: Response format invalid (status:$HAS_STATUS, token:$HAS_TOKEN, user:$HAS_USER)"
131
+ FAILED=$((FAILED + 1))
132
+ fi
133
+
134
+ # Test 14: User data accuracy
135
+ echo "Test 14: User data accuracy"
136
+ USER_EMAIL=$(echo "$CONTENT" | jq -r '.user.email')
137
+ if [ "$USER_EMAIL" = "$TEST_EMAIL" ]; then
138
+ echo "✅ Test 14: User data correct"
139
+ PASSED=$((PASSED + 1))
140
+ else
141
+ echo "❌ Test 14: User data mismatch"
142
+ FAILED=$((FAILED + 1))
143
+ fi
144
+
145
+ # Test 15: Buffer overflow attempt
146
+ echo "Test 15: Buffer overflow (very long input)"
147
+ LONG_EMAIL=$(python3 -c "print('a' * 10000 + '@test.com')")
148
+ REQ="{\"jsonrpc\":\"2.0\",\"method\":\"tools/call\",\"params\":{\"name\":\"mlgym_auth_login\",\"arguments\":{\"email\":\"$LONG_EMAIL\",\"password\":\"Pass123!\"}},\"id\":103}"
149
+ RESP=$(echo "$REQ" | node index.js 2>/dev/null | tail -1)
150
+ STATUS=$(echo "$RESP" | jq -r '.result.content[0].text' | jq -r '.status')
151
+ if [ "$STATUS" = "error" ]; then
152
+ echo "✅ Test 15: Buffer overflow prevented"
153
+ PASSED=$((PASSED + 1))
154
+ else
155
+ echo "⚠️ Test 15: Long input accepted (may be vulnerability)"
156
+ FAILED=$((FAILED + 1))
157
+ fi
158
+
159
+ echo ""
160
+ echo "==============================================="
161
+ echo "RESULTS: $PASSED passed, $FAILED failed (Total: $((PASSED + FAILED)))"
162
+ echo "==============================================="
163
+
164
+ if [ $FAILED -eq 0 ]; then
165
+ echo "✅ ALL TESTS PASSED"
166
+ exit 0
167
+ else
168
+ echo "⚠️ SOME TESTS FAILED"
169
+ exit 1
170
+ fi
@@ -0,0 +1,157 @@
1
+ #!/bin/bash
2
+ # Comprehensive test suite for mlgym_auth_logout
3
+ # Tests functional requirements, edge cases, and security
4
+
5
+ set -e
6
+
7
+ PASSED=0
8
+ FAILED=0
9
+
10
+ echo "=== mlgym_auth_logout Comprehensive Test Suite ==="
11
+ echo ""
12
+
13
+ # Test 1: Logout without active session
14
+ echo "Test 1: Logout without active session"
15
+ rm -f ~/.mlgym/mcp_config.json
16
+ REQ='{"jsonrpc":"2.0","method":"tools/call","params":{"name":"mlgym_auth_logout","arguments":{}},"id":1}'
17
+ RESP=$(echo "$REQ" | node index.js 2>/dev/null | tail -1)
18
+ CONTENT=$(echo "$RESP" | jq -r '.result.content[0].text')
19
+ STATUS=$(echo "$CONTENT" | jq -r '.status')
20
+ if [ "$STATUS" = "info" ] || echo "$CONTENT" | grep -q "No active session"; then
21
+ echo "✅ Test 1: Correctly handles no session"
22
+ PASSED=$((PASSED + 1))
23
+ else
24
+ echo "❌ Test 1: Should return info status for no session"
25
+ FAILED=$((FAILED + 1))
26
+ fi
27
+
28
+ # Test 2-5: Logout with active session (create user first)
29
+ echo "Test 2-5: Logout with active session"
30
+ # Create user to get a session
31
+ TEST_EMAIL="logouttest-$(date +%s)@example.com"
32
+ CREATE_REQ="{\"jsonrpc\":\"2.0\",\"method\":\"tools/call\",\"params\":{\"name\":\"mlgym_user_create\",\"arguments\":{\"email\":\"$TEST_EMAIL\",\"name\":\"Logout Test User\",\"password\":\"MyV3ryC0mpl3x!P@ssw0rd#2024\",\"accept_terms\":true}},\"id\":2}"
33
+ CREATE_RESP=$(echo "$CREATE_REQ" | node index.js 2>/dev/null | tail -1)
34
+ USER_ID=$(echo "$CREATE_RESP" | jq -r '.result.content[0].text' | jq -r '.user_id')
35
+
36
+ if [ -n "$USER_ID" ] && [ "$USER_ID" != "null" ]; then
37
+ # Now logout
38
+ LOGOUT_REQ='{"jsonrpc":"2.0","method":"tools/call","params":{"name":"mlgym_auth_logout","arguments":{}},"id":3}'
39
+ LOGOUT_RESP=$(echo "$LOGOUT_REQ" | node index.js 2>/dev/null | tail -1)
40
+ LOGOUT_CONTENT=$(echo "$LOGOUT_RESP" | jq -r '.result.content[0].text')
41
+ LOGOUT_STATUS=$(echo "$LOGOUT_CONTENT" | jq -r '.status')
42
+
43
+ # Test 2: Logout status
44
+ if [ "$LOGOUT_STATUS" = "success" ]; then
45
+ echo "✅ Test 2: Logout successful"
46
+ PASSED=$((PASSED + 1))
47
+ else
48
+ echo "❌ Test 2: Logout should return success status"
49
+ FAILED=$((FAILED + 1))
50
+ fi
51
+
52
+ # Test 3: Config file deletion
53
+ echo "Test 3: Config file deletion after logout"
54
+ if [ ! -f ~/.mlgym/mcp_config.json ]; then
55
+ echo "✅ Test 3: Config file deleted"
56
+ PASSED=$((PASSED + 1))
57
+ else
58
+ echo "❌ Test 3: Config file should be deleted after logout"
59
+ FAILED=$((FAILED + 1))
60
+ fi
61
+
62
+ # Test 4: Response format validation
63
+ echo "Test 4: Response format validation"
64
+ HAS_STATUS=$(echo "$LOGOUT_CONTENT" | jq -e '.status' > /dev/null 2>&1 && echo "yes" || echo "no")
65
+ HAS_MESSAGE=$(echo "$LOGOUT_CONTENT" | jq -e '.message' > /dev/null 2>&1 && echo "yes" || echo "no")
66
+ HAS_PREV_USER=$(echo "$LOGOUT_CONTENT" | jq -e '.previous_user' > /dev/null 2>&1 && echo "yes" || echo "no")
67
+
68
+ if [ "$HAS_STATUS" = "yes" ] && [ "$HAS_MESSAGE" = "yes" ] && [ "$HAS_PREV_USER" = "yes" ]; then
69
+ echo "✅ Test 4: Response format valid"
70
+ PASSED=$((PASSED + 1))
71
+ else
72
+ echo "❌ Test 4: Response format invalid (status:$HAS_STATUS, message:$HAS_MESSAGE, previous_user:$HAS_PREV_USER)"
73
+ FAILED=$((FAILED + 1))
74
+ fi
75
+
76
+ # Test 5: Previous user email verification
77
+ echo "Test 5: Previous user email verification"
78
+ PREV_USER=$(echo "$LOGOUT_CONTENT" | jq -r '.previous_user')
79
+ if [ "$PREV_USER" = "$TEST_EMAIL" ]; then
80
+ echo "✅ Test 5: Previous user email correct"
81
+ PASSED=$((PASSED + 1))
82
+ else
83
+ echo "❌ Test 5: Previous user email mismatch (expected: $TEST_EMAIL, got: $PREV_USER)"
84
+ FAILED=$((FAILED + 1))
85
+ fi
86
+ else
87
+ echo "⚠️ Tests 2-5: Couldn't create user for logout test"
88
+ FAILED=$((FAILED + 4))
89
+ fi
90
+
91
+ # Test 6: Double logout (logout twice)
92
+ echo "Test 6: Double logout (already logged out)"
93
+ DOUBLE_LOGOUT=$(echo "$LOGOUT_REQ" | node index.js 2>/dev/null | tail -1)
94
+ DOUBLE_CONTENT=$(echo "$DOUBLE_LOGOUT" | jq -r '.result.content[0].text')
95
+ DOUBLE_STATUS=$(echo "$DOUBLE_CONTENT" | jq -r '.status')
96
+
97
+ if [ "$DOUBLE_STATUS" = "info" ] || echo "$DOUBLE_CONTENT" | grep -q "No active session"; then
98
+ echo "✅ Test 6: Double logout handled correctly"
99
+ PASSED=$((PASSED + 1))
100
+ else
101
+ echo "❌ Test 6: Should return info status for double logout"
102
+ FAILED=$((FAILED + 1))
103
+ fi
104
+
105
+ # Test 7: Login after logout
106
+ echo "Test 7: Login after logout verification"
107
+ # Create user again (fresh session)
108
+ FRESH_EMAIL="fresh-$(date +%s)@example.com"
109
+ FRESH_PASS="MyV3ryC0mpl3x!P@ssw0rd#2024"
110
+ FRESH_CREATE="{\"jsonrpc\":\"2.0\",\"method\":\"tools/call\",\"params\":{\"name\":\"mlgym_user_create\",\"arguments\":{\"email\":\"$FRESH_EMAIL\",\"name\":\"Fresh User\",\"password\":\"$FRESH_PASS\",\"accept_terms\":true}},\"id\":7}"
111
+ FRESH_RESP=$(echo "$FRESH_CREATE" | node index.js 2>/dev/null | tail -1)
112
+
113
+ # Logout
114
+ FRESH_LOGOUT='{"jsonrpc":"2.0","method":"tools/call","params":{"name":"mlgym_auth_logout","arguments":{}},"id":8}'
115
+ echo "$FRESH_LOGOUT" | node index.js 2>/dev/null > /dev/null
116
+
117
+ # Try to login again
118
+ LOGIN_REQ="{\"jsonrpc\":\"2.0\",\"method\":\"tools/call\",\"params\":{\"name\":\"mlgym_auth_login\",\"arguments\":{\"email\":\"$FRESH_EMAIL\",\"password\":\"$FRESH_PASS\"}},\"id\":9}"
119
+ LOGIN_RESP=$(echo "$LOGIN_REQ" | node index.js 2>/dev/null | tail -1)
120
+ LOGIN_CONTENT=$(echo "$LOGIN_RESP" | jq -r '.result.content[0].text')
121
+ LOGIN_STATUS=$(echo "$LOGIN_CONTENT" | jq -r '.status')
122
+
123
+ if [ "$LOGIN_STATUS" = "success" ]; then
124
+ echo "✅ Test 7: Can login after logout"
125
+ PASSED=$((PASSED + 1))
126
+ else
127
+ echo "❌ Test 7: Should be able to login after logout"
128
+ FAILED=$((FAILED + 1))
129
+ fi
130
+
131
+ # Test 8: Extra parameters ignored
132
+ echo "Test 8: Extra parameters ignored gracefully"
133
+ EXTRA_REQ='{"jsonrpc":"2.0","method":"tools/call","params":{"name":"mlgym_auth_logout","arguments":{"extra_param":"should_be_ignored"}},"id":10}'
134
+ EXTRA_RESP=$(echo "$EXTRA_REQ" | node index.js 2>/dev/null | tail -1)
135
+ EXTRA_CONTENT=$(echo "$EXTRA_RESP" | jq -r '.result.content[0].text')
136
+ EXTRA_STATUS=$(echo "$EXTRA_CONTENT" | jq -r '.status')
137
+
138
+ if [ "$EXTRA_STATUS" = "success" ]; then
139
+ echo "✅ Test 8: Extra parameters ignored"
140
+ PASSED=$((PASSED + 1))
141
+ else
142
+ echo "❌ Test 8: Should handle extra parameters gracefully"
143
+ FAILED=$((FAILED + 1))
144
+ fi
145
+
146
+ echo ""
147
+ echo "==============================================="
148
+ echo "RESULTS: $PASSED passed, $FAILED failed (Total: $((PASSED + FAILED)))"
149
+ echo "==============================================="
150
+
151
+ if [ $FAILED -eq 0 ]; then
152
+ echo "✅ ALL TESTS PASSED"
153
+ exit 0
154
+ else
155
+ echo "⚠️ SOME TESTS FAILED"
156
+ exit 1
157
+ fi
@@ -0,0 +1,204 @@
1
+ #!/bin/bash
2
+ # Comprehensive test suite for deployment functions:
3
+ # - mlgym_deployments_trigger
4
+ # - mlgym_deployments_get_status
5
+ # - mlgym_deployments_list
6
+
7
+ set -e
8
+
9
+ PASSED=0
10
+ FAILED=0
11
+
12
+ echo "=== MCP Deployment Functions Test Suite ==="
13
+ echo ""
14
+
15
+ # Setup: Create user and project with deployment
16
+ echo "[SETUP] Creating test user and project with deployment"
17
+ TEST_EMAIL="deploy-$(date +%s)@example.com"
18
+ TEST_PASS="MyV3ryC0mpl3x!P@ssw0rd#2024"
19
+ CREATE_REQ="{\"jsonrpc\":\"2.0\",\"method\":\"tools/call\",\"params\":{\"name\":\"mlgym_user_create\",\"arguments\":{\"email\":\"$TEST_EMAIL\",\"name\":\"Deploy Test\",\"password\":\"$TEST_PASS\",\"accept_terms\":true}},\"id\":1}"
20
+ CREATE_RESP=$(echo "$CREATE_REQ" | node index.js 2>/dev/null | tail -1)
21
+ USER_ID=$(echo "$CREATE_RESP" | jq -r '.result.content[0].text' | jq -r '.user_id')
22
+
23
+ if [ -n "$USER_ID" ] && [ "$USER_ID" != "null" ]; then
24
+ echo "✅ Test user created (ID: $USER_ID)"
25
+ else
26
+ echo "❌ Failed to create test user"
27
+ exit 1
28
+ fi
29
+
30
+ # Create project WITH deployment enabled
31
+ PROJECT_NAME="deploy-test-$(date +%s | tail -c 6)"
32
+ PROJ_REQ="{\"jsonrpc\":\"2.0\",\"method\":\"tools/call\",\"params\":{\"name\":\"mlgym_project_init\",\"arguments\":{\"name\":\"$PROJECT_NAME\",\"description\":\"Test project with deployment\",\"enable_deployment\":true}},\"id\":2}"
33
+ PROJ_RESP=$(echo "$PROJ_REQ" | node index.js 2>/dev/null | tail -1)
34
+ PROJ_CONTENT=$(echo "$PROJ_RESP" | jq -r '.result.content[0].text')
35
+ PROJECT_ID=$(echo "$PROJ_CONTENT" | jq -r '.project_id' 2>/dev/null)
36
+
37
+ if [ -n "$PROJECT_ID" ] && [ "$PROJECT_ID" != "null" ]; then
38
+ echo "✅ Test project with deployment created (ID: $PROJECT_ID)"
39
+ else
40
+ echo "❌ Failed to create test project"
41
+ exit 1
42
+ fi
43
+
44
+ ## TRIGGER DEPLOYMENT TESTS ##
45
+ echo ""
46
+ echo "=== mlgym_deployments_trigger Tests ==="
47
+ echo ""
48
+
49
+ # Test 1: Trigger without authentication
50
+ echo "Test 1: Trigger deployment without authentication"
51
+ rm -f ~/.mlgym/mcp_config.json
52
+ REQ="{\"jsonrpc\":\"2.0\",\"method\":\"tools/call\",\"params\":{\"name\":\"mlgym_deployments_trigger\",\"arguments\":{\"project_id\":$PROJECT_ID}},\"id\":3}"
53
+ RESP=$(echo "$REQ" | node index.js 2>/dev/null | tail -1)
54
+ CONTENT=$(echo "$RESP" | jq -r '.result.content[0].text')
55
+ STATUS=$(echo "$CONTENT" | jq -r '.status')
56
+
57
+ if [ "$STATUS" = "error" ] && echo "$CONTENT" | grep -q "Not authenticated"; then
58
+ echo "✅ Test 1: Correctly requires authentication"
59
+ PASSED=$((PASSED + 1))
60
+ else
61
+ echo "❌ Test 1: Should require authentication"
62
+ FAILED=$((FAILED + 1))
63
+ fi
64
+
65
+ # Re-login
66
+ LOGIN_REQ="{\"jsonrpc\":\"2.0\",\"method\":\"tools/call\",\"params\":{\"name\":\"mlgym_auth_login\",\"arguments\":{\"email\":\"$TEST_EMAIL\",\"password\":\"$TEST_PASS\"}},\"id\":4}"
67
+ echo "$LOGIN_REQ" | node index.js 2>/dev/null > /dev/null
68
+
69
+ # Test 2: Missing project_id
70
+ echo "Test 2: Trigger deployment without project_id"
71
+ REQ='{"jsonrpc":"2.0","method":"tools/call","params":{"name":"mlgym_deployments_trigger","arguments":{}},"id":5}'
72
+ RESP=$(echo "$REQ" | node index.js 2>/dev/null | tail -1)
73
+ CONTENT=$(echo "$RESP" | jq -r '.result.content[0].text')
74
+ STATUS=$(echo "$CONTENT" | jq -r '.status')
75
+
76
+ if [ "$STATUS" = "error" ] && echo "$CONTENT" | grep -q "required"; then
77
+ echo "✅ Test 2: Correctly validates missing project_id"
78
+ PASSED=$((PASSED + 1))
79
+ else
80
+ echo "❌ Test 2: Should require project_id"
81
+ FAILED=$((FAILED + 1))
82
+ fi
83
+
84
+ # Test 3: Trigger deployment with valid project
85
+ echo "Test 3: Trigger deployment with valid project"
86
+ TRIGGER_REQ="{\"jsonrpc\":\"2.0\",\"method\":\"tools/call\",\"params\":{\"name\":\"mlgym_deployments_trigger\",\"arguments\":{\"project_id\":$PROJECT_ID}},\"id\":6}"
87
+ TRIGGER_RESP=$(echo "$TRIGGER_REQ" | node index.js 2>/dev/null | tail -1)
88
+ TRIGGER_CONTENT=$(echo "$TRIGGER_RESP" | jq -r '.result.content[0].text')
89
+ TRIGGER_STATUS=$(echo "$TRIGGER_CONTENT" | jq -r '.status')
90
+
91
+ DEPLOYMENT_ID=""
92
+ if [ "$TRIGGER_STATUS" = "success" ]; then
93
+ DEPLOYMENT_ID=$(echo "$TRIGGER_CONTENT" | jq -r '.deployment_id')
94
+ echo "✅ Test 3: Deployment triggered (ID: $DEPLOYMENT_ID)"
95
+ PASSED=$((PASSED + 1))
96
+ else
97
+ echo "❌ Test 3: Deployment trigger failed - $(echo "$TRIGGER_CONTENT" | jq -r '.message')"
98
+ FAILED=$((FAILED + 1))
99
+ fi
100
+
101
+ ## GET STATUS TESTS ##
102
+ echo ""
103
+ echo "=== mlgym_deployments_get_status Tests ==="
104
+ echo ""
105
+
106
+ # Test 4: Get status without parameters
107
+ echo "Test 4: Get status without parameters"
108
+ REQ='{"jsonrpc":"2.0","method":"tools/call","params":{"name":"mlgym_deployments_get_status","arguments":{}},"id":7}'
109
+ RESP=$(echo "$REQ" | node index.js 2>/dev/null | tail -1)
110
+ CONTENT=$(echo "$RESP" | jq -r '.result.content[0].text')
111
+ STATUS=$(echo "$CONTENT" | jq -r '.status')
112
+
113
+ if [ "$STATUS" = "error" ]; then
114
+ echo "✅ Test 4: Correctly requires deployment_id or project_id"
115
+ PASSED=$((PASSED + 1))
116
+ else
117
+ echo "❌ Test 4: Should require at least one parameter"
118
+ FAILED=$((FAILED + 1))
119
+ fi
120
+
121
+ # Test 5: Get status with project_id
122
+ if [ -n "$PROJECT_ID" ]; then
123
+ echo "Test 5: Get status with project_id"
124
+ STATUS_REQ="{\"jsonrpc\":\"2.0\",\"method\":\"tools/call\",\"params\":{\"name\":\"mlgym_deployments_get_status\",\"arguments\":{\"project_id\":$PROJECT_ID}},\"id\":8}"
125
+ STATUS_RESP=$(echo "$STATUS_REQ" | node index.js 2>/dev/null | tail -1)
126
+ STATUS_CONTENT=$(echo "$STATUS_RESP" | jq -r '.result.content[0].text')
127
+ STATUS_STATUS=$(echo "$STATUS_CONTENT" | jq -r '.status')
128
+
129
+ if [ "$STATUS_STATUS" = "success" ] || [ "$STATUS_STATUS" = "error" ]; then
130
+ echo "✅ Test 5: Get status returned response"
131
+ PASSED=$((PASSED + 1))
132
+ else
133
+ echo "❌ Test 5: Get status failed"
134
+ FAILED=$((FAILED + 1))
135
+ fi
136
+ fi
137
+
138
+ ## LIST DEPLOYMENTS TESTS ##
139
+ echo ""
140
+ echo "=== mlgym_deployments_list Tests ==="
141
+ echo ""
142
+
143
+ # Test 6: List deployments without authentication
144
+ echo "Test 6: List deployments without authentication"
145
+ rm -f ~/.mlgym/mcp_config.json
146
+ REQ='{"jsonrpc":"2.0","method":"tools/call","params":{"name":"mlgym_deployments_list","arguments":{}},"id":9}'
147
+ RESP=$(echo "$REQ" | node index.js 2>/dev/null | tail -1)
148
+ CONTENT=$(echo "$RESP" | jq -r '.result.content[0].text')
149
+ STATUS=$(echo "$CONTENT" | jq -r '.status')
150
+
151
+ if [ "$STATUS" = "error" ] && echo "$CONTENT" | grep -q "Not authenticated"; then
152
+ echo "✅ Test 6: Correctly requires authentication"
153
+ PASSED=$((PASSED + 1))
154
+ else
155
+ echo "❌ Test 6: Should require authentication"
156
+ FAILED=$((FAILED + 1))
157
+ fi
158
+
159
+ # Re-login
160
+ echo "$LOGIN_REQ" | node index.js 2>/dev/null > /dev/null
161
+
162
+ # Test 7: List all deployments
163
+ echo "Test 7: List all deployments"
164
+ LIST_REQ='{"jsonrpc":"2.0","method":"tools/call","params":{"name":"mlgym_deployments_list","arguments":{}},"id":10}'
165
+ LIST_RESP=$(echo "$LIST_REQ" | node index.js 2>/dev/null | tail -1)
166
+ LIST_CONTENT=$(echo "$LIST_RESP" | jq -r '.result.content[0].text')
167
+ LIST_STATUS=$(echo "$LIST_CONTENT" | jq -r '.status')
168
+
169
+ if [ "$LIST_STATUS" = "success" ]; then
170
+ DEPLOY_COUNT=$(echo "$LIST_CONTENT" | jq -r '.total' 2>/dev/null)
171
+ echo "✅ Test 7: List deployments successful (count: $DEPLOY_COUNT)"
172
+ PASSED=$((PASSED + 1))
173
+ else
174
+ echo "❌ Test 7: List deployments failed"
175
+ FAILED=$((FAILED + 1))
176
+ fi
177
+
178
+ # Test 8: List deployments for specific project
179
+ echo "Test 8: List deployments for specific project"
180
+ PROJ_LIST_REQ="{\"jsonrpc\":\"2.0\",\"method\":\"tools/call\",\"params\":{\"name\":\"mlgym_deployments_list\",\"arguments\":{\"project_id\":$PROJECT_ID}},\"id\":11}"
181
+ PROJ_LIST_RESP=$(echo "$PROJ_LIST_REQ" | node index.js 2>/dev/null | tail -1)
182
+ PROJ_LIST_CONTENT=$(echo "$PROJ_LIST_RESP" | jq -r '.result.content[0].text')
183
+ PROJ_LIST_STATUS=$(echo "$PROJ_LIST_CONTENT" | jq -r '.status')
184
+
185
+ if [ "$PROJ_LIST_STATUS" = "success" ] || [ "$PROJ_LIST_STATUS" = "error" ]; then
186
+ echo "✅ Test 8: List project deployments returned response"
187
+ PASSED=$((PASSED + 1))
188
+ else
189
+ echo "❌ Test 8: List project deployments failed"
190
+ FAILED=$((FAILED + 1))
191
+ fi
192
+
193
+ echo ""
194
+ echo "==============================================="
195
+ echo "RESULTS: $PASSED passed, $FAILED failed (Total: $((PASSED + FAILED)))"
196
+ echo "==============================================="
197
+
198
+ if [ $FAILED -eq 0 ]; then
199
+ echo "✅ ALL TESTS PASSED"
200
+ exit 0
201
+ else
202
+ echo "⚠️ SOME TESTS FAILED"
203
+ exit 1
204
+ fi