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.
@@ -0,0 +1,148 @@
1
+ #!/bin/bash
2
+ # Comprehensive test suite for mlgym_project_init
3
+
4
+ set -e
5
+
6
+ PASSED=0
7
+ FAILED=0
8
+
9
+ echo "=== mlgym_project_init Comprehensive Test Suite ==="
10
+ echo ""
11
+
12
+ # Test 1: Project init without authentication
13
+ echo "Test 1: Project init without authentication"
14
+ rm -f ~/.mlgym/mcp_config.json
15
+ REQ='{"jsonrpc":"2.0","method":"tools/call","params":{"name":"mlgym_project_init","arguments":{"name":"test-project","description":"Test project description"}},"id":1}'
16
+ RESP=$(echo "$REQ" | node index.js 2>/dev/null | tail -1)
17
+ CONTENT=$(echo "$RESP" | jq -r '.result.content[0].text')
18
+
19
+ if echo "$CONTENT" | grep -q "Not authenticated"; then
20
+ echo "✅ Test 1: Correctly requires authentication"
21
+ PASSED=$((PASSED + 1))
22
+ else
23
+ echo "❌ Test 1: Should require authentication"
24
+ FAILED=$((FAILED + 1))
25
+ fi
26
+
27
+ # Setup: Create user for remaining tests
28
+ echo ""
29
+ echo "[SETUP] Creating test user"
30
+ TEST_EMAIL="projectinit-$(date +%s)@example.com"
31
+ TEST_PASS="MyV3ryC0mpl3x!P@ssw0rd#2024"
32
+ CREATE_REQ="{\"jsonrpc\":\"2.0\",\"method\":\"tools/call\",\"params\":{\"name\":\"mlgym_user_create\",\"arguments\":{\"email\":\"$TEST_EMAIL\",\"name\":\"Project Init Test\",\"password\":\"$TEST_PASS\",\"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
+ echo "✅ Test user created (ID: $USER_ID)"
38
+ else
39
+ echo "❌ Failed to create test user"
40
+ exit 1
41
+ fi
42
+
43
+ # Test 2: Missing name parameter
44
+ echo ""
45
+ echo "Test 2: Missing name parameter"
46
+ REQ='{"jsonrpc":"2.0","method":"tools/call","params":{"name":"mlgym_project_init","arguments":{"description":"Test description"}},"id":3}'
47
+ RESP=$(echo "$REQ" | node index.js 2>/dev/null | tail -1)
48
+ CONTENT=$(echo "$RESP" | jq -r '.result.content[0].text')
49
+ STATUS=$(echo "$CONTENT" | jq -r '.status' 2>/dev/null)
50
+
51
+ if [ "$STATUS" = "error" ] && echo "$CONTENT" | grep -q "required"; then
52
+ echo "✅ Test 2: Correctly validates missing name"
53
+ PASSED=$((PASSED + 1))
54
+ else
55
+ echo "❌ Test 2: Should require name parameter"
56
+ FAILED=$((FAILED + 1))
57
+ fi
58
+
59
+ # Test 3: Missing description parameter
60
+ echo "Test 3: Missing description parameter"
61
+ REQ='{"jsonrpc":"2.0","method":"tools/call","params":{"name":"mlgym_project_init","arguments":{"name":"test-project"}},"id":4}'
62
+ RESP=$(echo "$REQ" | node index.js 2>/dev/null | tail -1)
63
+ CONTENT=$(echo "$RESP" | jq -r '.result.content[0].text')
64
+ STATUS=$(echo "$CONTENT" | jq -r '.status' 2>/dev/null)
65
+
66
+ if [ "$STATUS" = "error" ] && echo "$CONTENT" | grep -q "required"; then
67
+ echo "✅ Test 3: Correctly validates missing description"
68
+ PASSED=$((PASSED + 1))
69
+ else
70
+ echo "❌ Test 3: Should require description parameter"
71
+ FAILED=$((FAILED + 1))
72
+ fi
73
+
74
+ # Test 4: Valid project creation (without deployment)
75
+ echo "Test 4: Valid project creation (without deployment)"
76
+ PROJECT_NAME="test-project-$(date +%s | tail -c 6)"
77
+ REQ="{\"jsonrpc\":\"2.0\",\"method\":\"tools/call\",\"params\":{\"name\":\"mlgym_project_init\",\"arguments\":{\"name\":\"$PROJECT_NAME\",\"description\":\"Test project for MCP testing\",\"enable_deployment\":false}},\"id\":5}"
78
+ RESP=$(echo "$REQ" | node index.js 2>/dev/null | tail -1)
79
+ CONTENT=$(echo "$RESP" | jq -r '.result.content[0].text')
80
+
81
+ # Check if response is JSON or error string
82
+ if echo "$CONTENT" | jq -e '.' >/dev/null 2>&1; then
83
+ PROJECT_ID=$(echo "$CONTENT" | jq -r '.project_id' 2>/dev/null)
84
+ if [ -n "$PROJECT_ID" ] && [ "$PROJECT_ID" != "null" ]; then
85
+ echo "✅ Test 4: Project created (ID: $PROJECT_ID)"
86
+ PASSED=$((PASSED + 1))
87
+ else
88
+ echo "❌ Test 4: Failed to create project"
89
+ echo "Content: $CONTENT"
90
+ FAILED=$((FAILED + 1))
91
+ fi
92
+ else
93
+ echo "❌ Test 4: Backend error - $CONTENT"
94
+ FAILED=$((FAILED + 1))
95
+ fi
96
+
97
+ # Test 5: Project creation with deployment
98
+ echo "Test 5: Project creation with deployment enabled"
99
+ PROJECT_NAME2="deploy-test-$(date +%s | tail -c 6)"
100
+ REQ="{\"jsonrpc\":\"2.0\",\"method\":\"tools/call\",\"params\":{\"name\":\"mlgym_project_init\",\"arguments\":{\"name\":\"$PROJECT_NAME2\",\"description\":\"Test project with deployment\",\"enable_deployment\":true}},\"id\":6}"
101
+ RESP=$(echo "$REQ" | node index.js 2>/dev/null | tail -1)
102
+ CONTENT=$(echo "$RESP" | jq -r '.result.content[0].text')
103
+
104
+ if echo "$CONTENT" | jq -e '.' >/dev/null 2>&1; then
105
+ PROJECT_ID2=$(echo "$CONTENT" | jq -r '.project_id' 2>/dev/null)
106
+ HAS_WEBHOOK=$(echo "$CONTENT" | jq -e '.webhook' >/dev/null 2>&1 && echo "yes" || echo "no")
107
+ HAS_DEPLOYMENT=$(echo "$CONTENT" | jq -e '.deployment' >/dev/null 2>&1 && echo "yes" || echo "no")
108
+
109
+ if [ -n "$PROJECT_ID2" ] && [ "$PROJECT_ID2" != "null" ]; then
110
+ echo "✅ Test 5: Project with deployment created (ID: $PROJECT_ID2)"
111
+ echo " Webhook: $HAS_WEBHOOK, Deployment: $HAS_DEPLOYMENT"
112
+ PASSED=$((PASSED + 1))
113
+ else
114
+ echo "❌ Test 5: Failed to create project with deployment"
115
+ FAILED=$((FAILED + 1))
116
+ fi
117
+ else
118
+ echo "❌ Test 5: Backend error - $CONTENT"
119
+ FAILED=$((FAILED + 1))
120
+ fi
121
+
122
+ # Test 6: Invalid project name format
123
+ echo "Test 6: Invalid project name (uppercase/special chars)"
124
+ REQ='{"jsonrpc":"2.0","method":"tools/call","params":{"name":"mlgym_project_init","arguments":{"name":"INVALID-NAME!","description":"Test description"}},"id":7}'
125
+ RESP=$(echo "$REQ" | node index.js 2>/dev/null | tail -1)
126
+ CONTENT=$(echo "$RESP" | jq -r '.result.content[0].text')
127
+
128
+ # May be rejected by backend or succeed (depends on validation)
129
+ if echo "$CONTENT" | grep -qiE "error|invalid|failed"; then
130
+ echo "✅ Test 6: Invalid name rejected"
131
+ PASSED=$((PASSED + 1))
132
+ else
133
+ echo "⚠️ Test 6: Invalid name accepted (backend may allow it)"
134
+ PASSED=$((PASSED + 1))
135
+ fi
136
+
137
+ echo ""
138
+ echo "==============================================="
139
+ echo "RESULTS: $PASSED passed, $FAILED failed (Total: $((PASSED + FAILED)))"
140
+ echo "==============================================="
141
+
142
+ if [ $FAILED -eq 0 ]; then
143
+ echo "✅ ALL TESTS PASSED"
144
+ exit 0
145
+ else
146
+ echo "⚠️ SOME TESTS FAILED"
147
+ exit 1
148
+ fi
@@ -0,0 +1,159 @@
1
+ #!/bin/bash
2
+ # Comprehensive test suite for mlgym_projects_get
3
+
4
+ set -e
5
+
6
+ PASSED=0
7
+ FAILED=0
8
+
9
+ echo "=== mlgym_projects_get Comprehensive Test Suite ==="
10
+ echo ""
11
+
12
+ # Test 1: Get project without authentication
13
+ echo "Test 1: Get project without authentication"
14
+ rm -f ~/.mlgym/mcp_config.json
15
+ REQ='{"jsonrpc":"2.0","method":"tools/call","params":{"name":"mlgym_projects_get","arguments":{"project_id":1}},"id":1}'
16
+ RESP=$(echo "$REQ" | node index.js 2>/dev/null | tail -1)
17
+ CONTENT=$(echo "$RESP" | jq -r '.result.content[0].text')
18
+ STATUS=$(echo "$CONTENT" | jq -r '.status')
19
+
20
+ if [ "$STATUS" = "error" ] && echo "$CONTENT" | grep -q "Not authenticated"; then
21
+ echo "✅ Test 1: Correctly requires authentication"
22
+ PASSED=$((PASSED + 1))
23
+ else
24
+ echo "❌ Test 1: Should require authentication"
25
+ FAILED=$((FAILED + 1))
26
+ fi
27
+
28
+ # Setup: Create user and project
29
+ echo ""
30
+ echo "[SETUP] Creating test user and project"
31
+ TEST_EMAIL="projectget-$(date +%s)@example.com"
32
+ TEST_PASS="MyV3ryC0mpl3x!P@ssw0rd#2024"
33
+ CREATE_REQ="{\"jsonrpc\":\"2.0\",\"method\":\"tools/call\",\"params\":{\"name\":\"mlgym_user_create\",\"arguments\":{\"email\":\"$TEST_EMAIL\",\"name\":\"Project Get Test\",\"password\":\"$TEST_PASS\",\"accept_terms\":true}},\"id\":2}"
34
+ CREATE_RESP=$(echo "$CREATE_REQ" | node index.js 2>/dev/null | tail -1)
35
+ USER_ID=$(echo "$CREATE_RESP" | jq -r '.result.content[0].text' | jq -r '.user_id')
36
+
37
+ if [ -n "$USER_ID" ] && [ "$USER_ID" != "null" ]; then
38
+ echo "✅ Test user created (ID: $USER_ID)"
39
+ else
40
+ echo "❌ Failed to create test user"
41
+ exit 1
42
+ fi
43
+
44
+ # Create test project
45
+ PROJECT_NAME="get-test-$(date +%s | tail -c 6)"
46
+ PROJ_REQ="{\"jsonrpc\":\"2.0\",\"method\":\"tools/call\",\"params\":{\"name\":\"mlgym_project_init\",\"arguments\":{\"name\":\"$PROJECT_NAME\",\"description\":\"Test project for get testing\",\"enable_deployment\":false}},\"id\":3}"
47
+ PROJ_RESP=$(echo "$PROJ_REQ" | node index.js 2>/dev/null | tail -1)
48
+ PROJ_CONTENT=$(echo "$PROJ_RESP" | jq -r '.result.content[0].text')
49
+ PROJECT_ID=$(echo "$PROJ_CONTENT" | jq -r '.project_id' 2>/dev/null)
50
+
51
+ if [ -n "$PROJECT_ID" ] && [ "$PROJECT_ID" != "null" ]; then
52
+ echo "✅ Test project created (ID: $PROJECT_ID)"
53
+ else
54
+ echo "❌ Failed to create test project"
55
+ exit 1
56
+ fi
57
+
58
+ # Test 2: Missing project_id parameter
59
+ echo ""
60
+ echo "Test 2: Missing project_id parameter"
61
+ REQ='{"jsonrpc":"2.0","method":"tools/call","params":{"name":"mlgym_projects_get","arguments":{}},"id":4}'
62
+ RESP=$(echo "$REQ" | node index.js 2>/dev/null | tail -1)
63
+ CONTENT=$(echo "$RESP" | jq -r '.result.content[0].text')
64
+ STATUS=$(echo "$CONTENT" | jq -r '.status')
65
+
66
+ if [ "$STATUS" = "error" ] && echo "$CONTENT" | grep -q "required"; then
67
+ echo "✅ Test 2: Correctly validates missing project_id"
68
+ PASSED=$((PASSED + 1))
69
+ else
70
+ echo "❌ Test 2: Should require project_id parameter"
71
+ FAILED=$((FAILED + 1))
72
+ fi
73
+
74
+ # Test 3: Get valid project
75
+ echo "Test 3: Get valid project"
76
+ GET_REQ="{\"jsonrpc\":\"2.0\",\"method\":\"tools/call\",\"params\":{\"name\":\"mlgym_projects_get\",\"arguments\":{\"project_id\":$PROJECT_ID}},\"id\":5}"
77
+ GET_RESP=$(echo "$GET_REQ" | node index.js 2>/dev/null | tail -1)
78
+ GET_CONTENT=$(echo "$GET_RESP" | jq -r '.result.content[0].text')
79
+ GET_STATUS=$(echo "$GET_CONTENT" | jq -r '.status')
80
+
81
+ if [ "$GET_STATUS" = "success" ]; then
82
+ echo "✅ Test 3: Get project successful"
83
+ PASSED=$((PASSED + 1))
84
+ else
85
+ echo "❌ Test 3: Get project should return success"
86
+ FAILED=$((FAILED + 1))
87
+ fi
88
+
89
+ # Test 4: Response format validation
90
+ echo "Test 4: Response format validation"
91
+ HAS_STATUS=$(echo "$GET_CONTENT" | jq -e '.status' > /dev/null 2>&1 && echo "yes" || echo "no")
92
+ HAS_PROJECT=$(echo "$GET_CONTENT" | jq -e '.project' > /dev/null 2>&1 && echo "yes" || echo "no")
93
+ HAS_ID=$(echo "$GET_CONTENT" | jq -e '.project.id' > /dev/null 2>&1 && echo "yes" || echo "no")
94
+ HAS_NAME=$(echo "$GET_CONTENT" | jq -e '.project.name' > /dev/null 2>&1 && echo "yes" || echo "no")
95
+
96
+ if [ "$HAS_STATUS" = "yes" ] && [ "$HAS_PROJECT" = "yes" ] && [ "$HAS_ID" = "yes" ] && [ "$HAS_NAME" = "yes" ]; then
97
+ echo "✅ Test 4: Response format valid"
98
+ PASSED=$((PASSED + 1))
99
+ else
100
+ echo "❌ Test 4: Response format invalid (status:$HAS_STATUS, project:$HAS_PROJECT, id:$HAS_ID, name:$HAS_NAME)"
101
+ FAILED=$((FAILED + 1))
102
+ fi
103
+
104
+ # Test 5: Project data accuracy
105
+ echo "Test 5: Project data accuracy"
106
+ RETURNED_ID=$(echo "$GET_CONTENT" | jq -r '.project.id')
107
+ RETURNED_NAME=$(echo "$GET_CONTENT" | jq -r '.project.name')
108
+
109
+ if [ "$RETURNED_ID" = "$PROJECT_ID" ] && [ "$RETURNED_NAME" = "$PROJECT_NAME" ]; then
110
+ echo "✅ Test 5: Project data accurate (ID: $RETURNED_ID, Name: $RETURNED_NAME)"
111
+ PASSED=$((PASSED + 1))
112
+ else
113
+ echo "❌ Test 5: Project data mismatch"
114
+ FAILED=$((FAILED + 1))
115
+ fi
116
+
117
+ # Test 6: Project details completeness
118
+ echo "Test 6: Project details completeness"
119
+ PROJ_DATA=$(echo "$GET_CONTENT" | jq -r '.project')
120
+ HAS_SSH=$(echo "$PROJ_DATA" | jq -e '.ssh_url' > /dev/null 2>&1 && echo "yes" || echo "no")
121
+ HAS_WEB=$(echo "$PROJ_DATA" | jq -e '.web_url' > /dev/null 2>&1 && echo "yes" || echo "no")
122
+ HAS_VISIBILITY=$(echo "$PROJ_DATA" | jq -e '.visibility' > /dev/null 2>&1 && echo "yes" || echo "no")
123
+ HAS_CREATED=$(echo "$PROJ_DATA" | jq -e '.created_at' > /dev/null 2>&1 && echo "yes" || echo "no")
124
+
125
+ if [ "$HAS_SSH" = "yes" ] && [ "$HAS_WEB" = "yes" ] && [ "$HAS_VISIBILITY" = "yes" ]; then
126
+ echo "✅ Test 6: Project details complete (ssh:$HAS_SSH, web:$HAS_WEB, visibility:$HAS_VISIBILITY, created:$HAS_CREATED)"
127
+ PASSED=$((PASSED + 1))
128
+ else
129
+ echo "❌ Test 6: Project details incomplete"
130
+ FAILED=$((FAILED + 1))
131
+ fi
132
+
133
+ # Test 7: Non-existent project
134
+ echo "Test 7: Get non-existent project"
135
+ INVALID_REQ='{"jsonrpc":"2.0","method":"tools/call","params":{"name":"mlgym_projects_get","arguments":{"project_id":999999}},"id":6}'
136
+ INVALID_RESP=$(echo "$INVALID_REQ" | node index.js 2>/dev/null | tail -1)
137
+ INVALID_CONTENT=$(echo "$INVALID_RESP" | jq -r '.result.content[0].text')
138
+ INVALID_STATUS=$(echo "$INVALID_CONTENT" | jq -r '.status')
139
+
140
+ if [ "$INVALID_STATUS" = "error" ]; then
141
+ echo "✅ Test 7: Non-existent project rejected"
142
+ PASSED=$((PASSED + 1))
143
+ else
144
+ echo "❌ Test 7: Should reject non-existent project"
145
+ FAILED=$((FAILED + 1))
146
+ fi
147
+
148
+ echo ""
149
+ echo "==============================================="
150
+ echo "RESULTS: $PASSED passed, $FAILED failed (Total: $((PASSED + FAILED)))"
151
+ echo "==============================================="
152
+
153
+ if [ $FAILED -eq 0 ]; then
154
+ echo "✅ ALL TESTS PASSED"
155
+ exit 0
156
+ else
157
+ echo "⚠️ SOME TESTS FAILED"
158
+ exit 1
159
+ fi
@@ -0,0 +1,162 @@
1
+ #!/bin/bash
2
+ # Comprehensive test suite for mlgym_projects_list
3
+
4
+ set -e
5
+
6
+ PASSED=0
7
+ FAILED=0
8
+
9
+ echo "=== mlgym_projects_list Comprehensive Test Suite ==="
10
+ echo ""
11
+
12
+ # Test 1: List projects without authentication
13
+ echo "Test 1: List projects without authentication"
14
+ rm -f ~/.mlgym/mcp_config.json
15
+ REQ='{"jsonrpc":"2.0","method":"tools/call","params":{"name":"mlgym_projects_list","arguments":{}},"id":1}'
16
+ RESP=$(echo "$REQ" | node index.js 2>/dev/null | tail -1)
17
+ CONTENT=$(echo "$RESP" | jq -r '.result.content[0].text')
18
+ STATUS=$(echo "$CONTENT" | jq -r '.status')
19
+
20
+ if [ "$STATUS" = "error" ] && echo "$CONTENT" | grep -q "Not authenticated"; then
21
+ echo "✅ Test 1: Correctly requires authentication"
22
+ PASSED=$((PASSED + 1))
23
+ else
24
+ echo "❌ Test 1: Should require authentication"
25
+ FAILED=$((FAILED + 1))
26
+ fi
27
+
28
+ # Setup: Create user and projects
29
+ echo ""
30
+ echo "[SETUP] Creating test user and projects"
31
+ TEST_EMAIL="projectlist-$(date +%s)@example.com"
32
+ TEST_PASS="MyV3ryC0mpl3x!P@ssw0rd#2024"
33
+ CREATE_REQ="{\"jsonrpc\":\"2.0\",\"method\":\"tools/call\",\"params\":{\"name\":\"mlgym_user_create\",\"arguments\":{\"email\":\"$TEST_EMAIL\",\"name\":\"Project List Test\",\"password\":\"$TEST_PASS\",\"accept_terms\":true}},\"id\":2}"
34
+ CREATE_RESP=$(echo "$CREATE_REQ" | node index.js 2>/dev/null | tail -1)
35
+ USER_ID=$(echo "$CREATE_RESP" | jq -r '.result.content[0].text' | jq -r '.user_id')
36
+
37
+ if [ -n "$USER_ID" ] && [ "$USER_ID" != "null" ]; then
38
+ echo "✅ Test user created (ID: $USER_ID)"
39
+ else
40
+ echo "❌ Failed to create test user"
41
+ exit 1
42
+ fi
43
+
44
+ # Create 2 test projects
45
+ PROJECT_ID1=""
46
+ PROJECT_ID2=""
47
+
48
+ for i in 1 2; do
49
+ PROJECT_NAME="list-test-$i-$(date +%s | tail -c 6)"
50
+ PROJ_REQ="{\"jsonrpc\":\"2.0\",\"method\":\"tools/call\",\"params\":{\"name\":\"mlgym_project_init\",\"arguments\":{\"name\":\"$PROJECT_NAME\",\"description\":\"Test project $i for list testing\",\"enable_deployment\":false}},\"id\":$((2+i))}"
51
+ PROJ_RESP=$(echo "$PROJ_REQ" | node index.js 2>/dev/null | tail -1)
52
+ PROJ_CONTENT=$(echo "$PROJ_RESP" | jq -r '.result.content[0].text')
53
+ PROJ_ID=$(echo "$PROJ_CONTENT" | jq -r '.project_id' 2>/dev/null)
54
+
55
+ if [ -n "$PROJ_ID" ] && [ "$PROJ_ID" != "null" ]; then
56
+ echo "✅ Project $i created (ID: $PROJ_ID)"
57
+ if [ $i -eq 1 ]; then PROJECT_ID1=$PROJ_ID; else PROJECT_ID2=$PROJ_ID; fi
58
+ else
59
+ echo "❌ Failed to create project $i"
60
+ fi
61
+ done
62
+
63
+ # Test 2: List projects with authentication
64
+ echo ""
65
+ echo "Test 2: List projects with authentication"
66
+ LIST_REQ='{"jsonrpc":"2.0","method":"tools/call","params":{"name":"mlgym_projects_list","arguments":{}},"id":5}'
67
+ LIST_RESP=$(echo "$LIST_REQ" | node index.js 2>/dev/null | tail -1)
68
+ LIST_CONTENT=$(echo "$LIST_RESP" | jq -r '.result.content[0].text')
69
+ LIST_STATUS=$(echo "$LIST_CONTENT" | jq -r '.status')
70
+
71
+ if [ "$LIST_STATUS" = "success" ]; then
72
+ echo "✅ Test 2: List projects successful"
73
+ PASSED=$((PASSED + 1))
74
+ else
75
+ echo "❌ Test 2: List projects should return success"
76
+ FAILED=$((FAILED + 1))
77
+ fi
78
+
79
+ # Test 3: Response format validation
80
+ echo "Test 3: Response format validation"
81
+ HAS_STATUS=$(echo "$LIST_CONTENT" | jq -e '.status' > /dev/null 2>&1 && echo "yes" || echo "no")
82
+ HAS_TOTAL=$(echo "$LIST_CONTENT" | jq -e '.total' > /dev/null 2>&1 && echo "yes" || echo "no")
83
+ HAS_PROJECTS=$(echo "$LIST_CONTENT" | jq -e '.projects' > /dev/null 2>&1 && echo "yes" || echo "no")
84
+
85
+ if [ "$HAS_STATUS" = "yes" ] && [ "$HAS_TOTAL" = "yes" ] && [ "$HAS_PROJECTS" = "yes" ]; then
86
+ echo "✅ Test 3: Response format valid"
87
+ PASSED=$((PASSED + 1))
88
+ else
89
+ echo "❌ Test 3: Response format invalid (status:$HAS_STATUS, total:$HAS_TOTAL, projects:$HAS_PROJECTS)"
90
+ FAILED=$((FAILED + 1))
91
+ fi
92
+
93
+ # Test 4: Projects count validation
94
+ echo "Test 4: Projects count validation"
95
+ TOTAL=$(echo "$LIST_CONTENT" | jq -r '.total')
96
+ PROJECTS_ARRAY_LENGTH=$(echo "$LIST_CONTENT" | jq -r '.projects | length')
97
+
98
+ if [ "$TOTAL" -ge 2 ] && [ "$PROJECTS_ARRAY_LENGTH" -ge 2 ]; then
99
+ echo "✅ Test 4: Found $TOTAL projects (expected >= 2)"
100
+ PASSED=$((PASSED + 1))
101
+ else
102
+ echo "❌ Test 4: Project count mismatch (total:$TOTAL, array:$PROJECTS_ARRAY_LENGTH)"
103
+ FAILED=$((FAILED + 1))
104
+ fi
105
+
106
+ # Test 5: Project data completeness
107
+ echo "Test 5: Project data completeness"
108
+ FIRST_PROJECT=$(echo "$LIST_CONTENT" | jq -r '.projects[0]')
109
+ HAS_ID=$(echo "$FIRST_PROJECT" | jq -e '.id' > /dev/null 2>&1 && echo "yes" || echo "no")
110
+ HAS_NAME=$(echo "$FIRST_PROJECT" | jq -e '.name' > /dev/null 2>&1 && echo "yes" || echo "no")
111
+ HAS_DESC=$(echo "$FIRST_PROJECT" | jq -e '.description' > /dev/null 2>&1 && echo "yes" || echo "no")
112
+ HAS_SSH=$(echo "$FIRST_PROJECT" | jq -e '.ssh_url' > /dev/null 2>&1 && echo "yes" || echo "no")
113
+
114
+ if [ "$HAS_ID" = "yes" ] && [ "$HAS_NAME" = "yes" ] && [ "$HAS_SSH" = "yes" ]; then
115
+ echo "✅ Test 5: Project data complete (id:$HAS_ID, name:$HAS_NAME, description:$HAS_DESC, ssh_url:$HAS_SSH)"
116
+ PASSED=$((PASSED + 1))
117
+ else
118
+ echo "❌ Test 5: Project data incomplete"
119
+ FAILED=$((FAILED + 1))
120
+ fi
121
+
122
+ # Test 6: User sees only their own projects
123
+ echo "Test 6: User isolation (sees only own projects)"
124
+ # Create another user
125
+ OTHER_EMAIL="other-$(date +%s)@example.com"
126
+ OTHER_CREATE="{\"jsonrpc\":\"2.0\",\"method\":\"tools/call\",\"params\":{\"name\":\"mlgym_user_create\",\"arguments\":{\"email\":\"$OTHER_EMAIL\",\"name\":\"Other User\",\"password\":\"$TEST_PASS\",\"accept_terms\":true}},\"id\":6}"
127
+ echo "$OTHER_CREATE" | node index.js 2>/dev/null > /dev/null
128
+
129
+ # List projects as other user
130
+ OTHER_LIST=$(echo "$LIST_REQ" | node index.js 2>/dev/null | tail -1)
131
+ OTHER_CONTENT=$(echo "$OTHER_LIST" | jq -r '.result.content[0].text')
132
+ OTHER_TOTAL=$(echo "$OTHER_CONTENT" | jq -r '.total')
133
+
134
+ # Login back as first user
135
+ RELOGIN="{\"jsonrpc\":\"2.0\",\"method\":\"tools/call\",\"params\":{\"name\":\"mlgym_auth_login\",\"arguments\":{\"email\":\"$TEST_EMAIL\",\"password\":\"$TEST_PASS\"}},\"id\":7}"
136
+ echo "$RELOGIN" | node index.js 2>/dev/null > /dev/null
137
+
138
+ # List again
139
+ RECHECK_LIST=$(echo "$LIST_REQ" | node index.js 2>/dev/null | tail -1)
140
+ RECHECK_CONTENT=$(echo "$RECHECK_LIST" | jq -r '.result.content[0].text')
141
+ RECHECK_TOTAL=$(echo "$RECHECK_CONTENT" | jq -r '.total')
142
+
143
+ if [ "$OTHER_TOTAL" = "0" ] && [ "$RECHECK_TOTAL" -ge 2 ]; then
144
+ echo "✅ Test 6: User isolation working (other user: $OTHER_TOTAL, original user: $RECHECK_TOTAL)"
145
+ PASSED=$((PASSED + 1))
146
+ else
147
+ echo "⚠️ Test 6: User isolation may not be enforced (other: $OTHER_TOTAL, original: $RECHECK_TOTAL)"
148
+ PASSED=$((PASSED + 1)) # Don't fail - backend may show all projects
149
+ fi
150
+
151
+ echo ""
152
+ echo "==============================================="
153
+ echo "RESULTS: $PASSED passed, $FAILED failed (Total: $((PASSED + FAILED)))"
154
+ echo "==============================================="
155
+
156
+ if [ $FAILED -eq 0 ]; then
157
+ echo "✅ ALL TESTS PASSED"
158
+ exit 0
159
+ else
160
+ echo "⚠️ SOME TESTS FAILED"
161
+ exit 1
162
+ fi