@zereight/mcp-gitlab 1.0.71 → 1.0.74
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/build/customSchemas.js +19 -0
- package/build/index.js +6 -5
- package/build/schemas.js +193 -199
- package/package.json +1 -1
- package/build/tests/integration.test.js +0 -151
- package/build/tests/unit.test.js +0 -122
package/package.json
CHANGED
|
@@ -1,151 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeAll, afterAll } from '@jest/globals';
|
|
2
|
-
import fetch from 'node-fetch';
|
|
3
|
-
// Integration tests that run against real GitLab API (when credentials are provided)
|
|
4
|
-
const GITLAB_API_URL = process.env.GITLAB_API_URL || 'https://gitlab.com';
|
|
5
|
-
const GITLAB_TOKEN = process.env.GITLAB_TOKEN || '';
|
|
6
|
-
const TEST_PROJECT_ID = process.env.TEST_PROJECT_ID || '';
|
|
7
|
-
const skipIntegrationTests = !GITLAB_TOKEN || !TEST_PROJECT_ID;
|
|
8
|
-
describe('GitLab MCP Server Integration Tests', () => {
|
|
9
|
-
if (skipIntegrationTests) {
|
|
10
|
-
it('should skip integration tests when credentials are missing', () => {
|
|
11
|
-
console.log('Skipping integration tests: Missing GITLAB_TOKEN or TEST_PROJECT_ID');
|
|
12
|
-
expect(true).toBe(true);
|
|
13
|
-
});
|
|
14
|
-
return;
|
|
15
|
-
}
|
|
16
|
-
let testIssueId = null;
|
|
17
|
-
let testMRId = null;
|
|
18
|
-
beforeAll(() => {
|
|
19
|
-
console.log('Running integration tests with GitLab API');
|
|
20
|
-
});
|
|
21
|
-
afterAll(async () => {
|
|
22
|
-
// Cleanup: Delete test issue and MR if created
|
|
23
|
-
if (testIssueId) {
|
|
24
|
-
await fetch(`${GITLAB_API_URL}/api/v4/projects/${encodeURIComponent(TEST_PROJECT_ID)}/issues/${testIssueId}`, {
|
|
25
|
-
method: 'DELETE',
|
|
26
|
-
headers: {
|
|
27
|
-
'Authorization': `Bearer ${GITLAB_TOKEN}`
|
|
28
|
-
}
|
|
29
|
-
});
|
|
30
|
-
}
|
|
31
|
-
});
|
|
32
|
-
describe('Project Access', () => {
|
|
33
|
-
it('should fetch project information', async () => {
|
|
34
|
-
const response = await fetch(`${GITLAB_API_URL}/api/v4/projects/${encodeURIComponent(TEST_PROJECT_ID)}`, {
|
|
35
|
-
headers: {
|
|
36
|
-
'Authorization': `Bearer ${GITLAB_TOKEN}`,
|
|
37
|
-
'Accept': 'application/json'
|
|
38
|
-
}
|
|
39
|
-
});
|
|
40
|
-
expect(response.ok).toBe(true);
|
|
41
|
-
const project = await response.json();
|
|
42
|
-
expect(project).toHaveProperty('id');
|
|
43
|
-
expect(project).toHaveProperty('name');
|
|
44
|
-
});
|
|
45
|
-
});
|
|
46
|
-
describe('Issue Operations', () => {
|
|
47
|
-
it('should create an issue', async () => {
|
|
48
|
-
const issueData = {
|
|
49
|
-
title: `Test Issue ${Date.now()}`,
|
|
50
|
-
description: 'This is a test issue created by MCP integration tests'
|
|
51
|
-
};
|
|
52
|
-
const response = await fetch(`${GITLAB_API_URL}/api/v4/projects/${encodeURIComponent(TEST_PROJECT_ID)}/issues`, {
|
|
53
|
-
method: 'POST',
|
|
54
|
-
headers: {
|
|
55
|
-
'Authorization': `Bearer ${GITLAB_TOKEN}`,
|
|
56
|
-
'Accept': 'application/json',
|
|
57
|
-
'Content-Type': 'application/json'
|
|
58
|
-
},
|
|
59
|
-
body: JSON.stringify(issueData)
|
|
60
|
-
});
|
|
61
|
-
expect(response.ok).toBe(true);
|
|
62
|
-
const issue = await response.json();
|
|
63
|
-
expect(issue).toHaveProperty('iid');
|
|
64
|
-
expect(issue.title).toBe(issueData.title);
|
|
65
|
-
testIssueId = issue.iid;
|
|
66
|
-
});
|
|
67
|
-
it('should list issues', async () => {
|
|
68
|
-
const response = await fetch(`${GITLAB_API_URL}/api/v4/projects/${encodeURIComponent(TEST_PROJECT_ID)}/issues?state=opened`, {
|
|
69
|
-
headers: {
|
|
70
|
-
'Authorization': `Bearer ${GITLAB_TOKEN}`,
|
|
71
|
-
'Accept': 'application/json'
|
|
72
|
-
}
|
|
73
|
-
});
|
|
74
|
-
expect(response.ok).toBe(true);
|
|
75
|
-
const issues = await response.json();
|
|
76
|
-
expect(Array.isArray(issues)).toBe(true);
|
|
77
|
-
});
|
|
78
|
-
it('should add a comment to an issue', async () => {
|
|
79
|
-
if (!testIssueId) {
|
|
80
|
-
console.log('Skipping comment test: No test issue created');
|
|
81
|
-
return;
|
|
82
|
-
}
|
|
83
|
-
const commentData = {
|
|
84
|
-
body: 'Test comment from MCP integration tests'
|
|
85
|
-
};
|
|
86
|
-
const response = await fetch(`${GITLAB_API_URL}/api/v4/projects/${encodeURIComponent(TEST_PROJECT_ID)}/issues/${testIssueId}/notes`, {
|
|
87
|
-
method: 'POST',
|
|
88
|
-
headers: {
|
|
89
|
-
'Authorization': `Bearer ${GITLAB_TOKEN}`,
|
|
90
|
-
'Accept': 'application/json',
|
|
91
|
-
'Content-Type': 'application/json'
|
|
92
|
-
},
|
|
93
|
-
body: JSON.stringify(commentData)
|
|
94
|
-
});
|
|
95
|
-
expect(response.ok).toBe(true);
|
|
96
|
-
const comment = await response.json();
|
|
97
|
-
expect(comment.body).toBe(commentData.body);
|
|
98
|
-
});
|
|
99
|
-
});
|
|
100
|
-
describe('Merge Request Operations', () => {
|
|
101
|
-
it('should list merge requests', async () => {
|
|
102
|
-
const response = await fetch(`${GITLAB_API_URL}/api/v4/projects/${encodeURIComponent(TEST_PROJECT_ID)}/merge_requests?state=opened`, {
|
|
103
|
-
headers: {
|
|
104
|
-
'Authorization': `Bearer ${GITLAB_TOKEN}`,
|
|
105
|
-
'Accept': 'application/json'
|
|
106
|
-
}
|
|
107
|
-
});
|
|
108
|
-
expect(response.ok).toBe(true);
|
|
109
|
-
const mrs = await response.json();
|
|
110
|
-
expect(Array.isArray(mrs)).toBe(true);
|
|
111
|
-
});
|
|
112
|
-
});
|
|
113
|
-
describe('Repository Operations', () => {
|
|
114
|
-
it('should fetch repository branches', async () => {
|
|
115
|
-
const response = await fetch(`${GITLAB_API_URL}/api/v4/projects/${encodeURIComponent(TEST_PROJECT_ID)}/repository/branches`, {
|
|
116
|
-
headers: {
|
|
117
|
-
'Authorization': `Bearer ${GITLAB_TOKEN}`,
|
|
118
|
-
'Accept': 'application/json'
|
|
119
|
-
}
|
|
120
|
-
});
|
|
121
|
-
expect(response.ok).toBe(true);
|
|
122
|
-
const branches = await response.json();
|
|
123
|
-
expect(Array.isArray(branches)).toBe(true);
|
|
124
|
-
expect(branches.length).toBeGreaterThan(0);
|
|
125
|
-
});
|
|
126
|
-
it('should fetch repository commits', async () => {
|
|
127
|
-
const response = await fetch(`${GITLAB_API_URL}/api/v4/projects/${encodeURIComponent(TEST_PROJECT_ID)}/repository/commits?per_page=5`, {
|
|
128
|
-
headers: {
|
|
129
|
-
'Authorization': `Bearer ${GITLAB_TOKEN}`,
|
|
130
|
-
'Accept': 'application/json'
|
|
131
|
-
}
|
|
132
|
-
});
|
|
133
|
-
expect(response.ok).toBe(true);
|
|
134
|
-
const commits = await response.json();
|
|
135
|
-
expect(Array.isArray(commits)).toBe(true);
|
|
136
|
-
});
|
|
137
|
-
});
|
|
138
|
-
describe('Pipeline Operations', () => {
|
|
139
|
-
it('should list pipelines', async () => {
|
|
140
|
-
const response = await fetch(`${GITLAB_API_URL}/api/v4/projects/${encodeURIComponent(TEST_PROJECT_ID)}/pipelines?per_page=5`, {
|
|
141
|
-
headers: {
|
|
142
|
-
'Authorization': `Bearer ${GITLAB_TOKEN}`,
|
|
143
|
-
'Accept': 'application/json'
|
|
144
|
-
}
|
|
145
|
-
});
|
|
146
|
-
expect(response.ok).toBe(true);
|
|
147
|
-
const pipelines = await response.json();
|
|
148
|
-
expect(Array.isArray(pipelines)).toBe(true);
|
|
149
|
-
});
|
|
150
|
-
});
|
|
151
|
-
});
|
package/build/tests/unit.test.js
DELETED
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach, afterEach } from '@jest/globals';
|
|
2
|
-
import fetch from 'node-fetch';
|
|
3
|
-
// Mock fetch for unit tests
|
|
4
|
-
jest.mock('node-fetch');
|
|
5
|
-
const mockedFetch = fetch;
|
|
6
|
-
describe('GitLab MCP Server Unit Tests', () => {
|
|
7
|
-
beforeEach(() => {
|
|
8
|
-
jest.clearAllMocks();
|
|
9
|
-
});
|
|
10
|
-
afterEach(() => {
|
|
11
|
-
jest.restoreAllMocks();
|
|
12
|
-
});
|
|
13
|
-
describe('API URL Construction', () => {
|
|
14
|
-
it('should use plural resource names in API endpoints', () => {
|
|
15
|
-
const projectId = 'test/project';
|
|
16
|
-
const issueIid = 123;
|
|
17
|
-
// Test issue endpoint
|
|
18
|
-
const issueUrl = `/api/v4/projects/${encodeURIComponent(projectId)}/issues/${issueIid}`;
|
|
19
|
-
expect(issueUrl).toContain('/issues/');
|
|
20
|
-
expect(issueUrl).not.toContain('/issue/');
|
|
21
|
-
// Test merge request endpoint
|
|
22
|
-
const mrUrl = `/api/v4/projects/${encodeURIComponent(projectId)}/merge_requests/${issueIid}`;
|
|
23
|
-
expect(mrUrl).toContain('/merge_requests/');
|
|
24
|
-
expect(mrUrl).not.toContain('/merge_request/');
|
|
25
|
-
});
|
|
26
|
-
it('should properly encode project IDs with special characters', () => {
|
|
27
|
-
const projectId = 'namespace/project-name';
|
|
28
|
-
const encoded = encodeURIComponent(projectId);
|
|
29
|
-
expect(encoded).toBe('namespace%2Fproject-name');
|
|
30
|
-
});
|
|
31
|
-
});
|
|
32
|
-
describe('API Response Handling', () => {
|
|
33
|
-
it('should handle successful responses', async () => {
|
|
34
|
-
const mockResponse = {
|
|
35
|
-
ok: true,
|
|
36
|
-
status: 200,
|
|
37
|
-
json: async () => ({ id: 1, title: 'Test Issue' })
|
|
38
|
-
};
|
|
39
|
-
mockedFetch.mockResolvedValueOnce(mockResponse);
|
|
40
|
-
const response = await fetch('https://gitlab.com/api/v4/projects/1/issues/1');
|
|
41
|
-
const data = await response.json();
|
|
42
|
-
expect(response.ok).toBe(true);
|
|
43
|
-
expect(data).toEqual({ id: 1, title: 'Test Issue' });
|
|
44
|
-
});
|
|
45
|
-
it('should handle error responses', async () => {
|
|
46
|
-
const mockResponse = {
|
|
47
|
-
ok: false,
|
|
48
|
-
status: 404,
|
|
49
|
-
statusText: 'Not Found',
|
|
50
|
-
text: async () => '{"message":"404 Project Not Found"}'
|
|
51
|
-
};
|
|
52
|
-
mockedFetch.mockResolvedValueOnce(mockResponse);
|
|
53
|
-
const response = await fetch('https://gitlab.com/api/v4/projects/999/issues/1');
|
|
54
|
-
expect(response.ok).toBe(false);
|
|
55
|
-
expect(response.status).toBe(404);
|
|
56
|
-
});
|
|
57
|
-
it('should handle rate limiting', async () => {
|
|
58
|
-
const mockResponse = {
|
|
59
|
-
ok: false,
|
|
60
|
-
status: 429,
|
|
61
|
-
statusText: 'Too Many Requests',
|
|
62
|
-
headers: {
|
|
63
|
-
get: (name) => name === 'RateLimit-Reset' ? '1234567890' : null
|
|
64
|
-
}
|
|
65
|
-
};
|
|
66
|
-
mockedFetch.mockResolvedValueOnce(mockResponse);
|
|
67
|
-
const response = await fetch('https://gitlab.com/api/v4/projects/1/issues');
|
|
68
|
-
expect(response.status).toBe(429);
|
|
69
|
-
});
|
|
70
|
-
});
|
|
71
|
-
describe('Authentication', () => {
|
|
72
|
-
it('should include Bearer token in Authorization header', () => {
|
|
73
|
-
const token = 'test-token-123';
|
|
74
|
-
const headers = {
|
|
75
|
-
'Authorization': `Bearer ${token}`,
|
|
76
|
-
'Accept': 'application/json',
|
|
77
|
-
'Content-Type': 'application/json'
|
|
78
|
-
};
|
|
79
|
-
expect(headers.Authorization).toBe('Bearer test-token-123');
|
|
80
|
-
});
|
|
81
|
-
it('should handle missing token gracefully', () => {
|
|
82
|
-
const token = process.env.GITLAB_TOKEN || '';
|
|
83
|
-
expect(token).toBeDefined();
|
|
84
|
-
});
|
|
85
|
-
});
|
|
86
|
-
describe('Data Validation', () => {
|
|
87
|
-
it('should validate required fields for issue creation', () => {
|
|
88
|
-
const validIssue = {
|
|
89
|
-
title: 'Test Issue',
|
|
90
|
-
description: 'Test Description'
|
|
91
|
-
};
|
|
92
|
-
expect(validIssue.title).toBeTruthy();
|
|
93
|
-
expect(validIssue.title.length).toBeGreaterThan(0);
|
|
94
|
-
});
|
|
95
|
-
it('should validate merge request parameters', () => {
|
|
96
|
-
const validMR = {
|
|
97
|
-
source_branch: 'feature-branch',
|
|
98
|
-
target_branch: 'main',
|
|
99
|
-
title: 'Test MR'
|
|
100
|
-
};
|
|
101
|
-
expect(validMR.source_branch).not.toBe(validMR.target_branch);
|
|
102
|
-
expect(validMR.title).toBeTruthy();
|
|
103
|
-
});
|
|
104
|
-
});
|
|
105
|
-
describe('Error Handling', () => {
|
|
106
|
-
it('should handle network errors', async () => {
|
|
107
|
-
mockedFetch.mockRejectedValueOnce(new Error('Network error'));
|
|
108
|
-
await expect(fetch('https://gitlab.com/api/v4/projects/1/issues'))
|
|
109
|
-
.rejects.toThrow('Network error');
|
|
110
|
-
});
|
|
111
|
-
it('should handle JSON parsing errors', async () => {
|
|
112
|
-
const mockResponse = {
|
|
113
|
-
ok: true,
|
|
114
|
-
status: 200,
|
|
115
|
-
json: async () => { throw new Error('Invalid JSON'); }
|
|
116
|
-
};
|
|
117
|
-
mockedFetch.mockResolvedValueOnce(mockResponse);
|
|
118
|
-
const response = await fetch('https://gitlab.com/api/v4/projects/1/issues');
|
|
119
|
-
await expect(response.json()).rejects.toThrow('Invalid JSON');
|
|
120
|
-
});
|
|
121
|
-
});
|
|
122
|
-
});
|