diff --git a/index.ts b/index.ts index c9fbb4e6..6c81dde9 100644 --- a/index.ts +++ b/index.ts @@ -566,6 +566,7 @@ const authBySession: Record = {}; const BASE_HEADERS: Record = { Accept: "application/json", "Content-Type": "application/json", + "User-Agent": `gitlab-mcp-server/${SERVER_VERSION} (node-fetch)`, }; /** diff --git a/test-results-oauth.json b/test-results-oauth.json new file mode 100644 index 00000000..8073deb3 --- /dev/null +++ b/test-results-oauth.json @@ -0,0 +1,92 @@ +[ + { + "name": "OAuth class instantiation", + "status": "passed", + "duration": 0 + }, + { + "name": "Token storage path configuration", + "status": "passed", + "duration": 0 + }, + { + "name": "Scope configuration with api only", + "status": "passed", + "duration": 0 + }, + { + "name": "Multiple scopes configuration (redundant)", + "status": "passed", + "duration": 1 + }, + { + "name": "hasValidToken returns false without token", + "status": "passed", + "duration": 0 + }, + { + "name": "hasValidToken returns true with valid token", + "status": "passed", + "duration": 1 + }, + { + "name": "hasValidToken returns false with expired token", + "status": "passed", + "duration": 0 + }, + { + "name": "clearToken removes token file", + "status": "passed", + "duration": 1 + }, + { + "name": "Token file has correct permissions", + "status": "passed", + "duration": 0 + }, + { + "name": "Port availability check", + "status": "passed", + "duration": 3 + }, + { + "name": "OAuth redirect URI parsing", + "status": "passed", + "duration": 0 + }, + { + "name": "Token expiration calculation", + "status": "passed", + "duration": 0 + }, + { + "name": "Shared server concept", + "status": "passed", + "duration": 2 + }, + { + "name": "Environment variable configuration", + "status": "passed", + "duration": 0 + }, + { + "name": "Token data structure validation", + "status": "passed", + "duration": 0 + }, + { + "name": "Invalid token storage path handling", + "status": "passed", + "duration": 0 + }, + { + "name": "Self-hosted GitLab URL configuration", + "status": "passed", + "duration": 0 + }, + { + "name": "Custom port in redirect URI", + "status": "passed", + "duration": 0 + } +] \ No newline at end of file diff --git a/test/test-user-agent-header.ts b/test/test-user-agent-header.ts new file mode 100644 index 00000000..a88ddcef --- /dev/null +++ b/test/test-user-agent-header.ts @@ -0,0 +1,54 @@ +import { describe, test, before, after } from 'node:test'; +import assert from 'node:assert'; +import { MockGitLabServer, findMockServerPort } from './utils/mock-gitlab-server.js'; + +const MOCK_TOKEN = 'glpat-mock-token-12345'; + +describe('User-Agent Header Tests', () => { + let mockServer: MockGitLabServer; + let mockPort: number; + + before(async () => { + mockPort = await findMockServerPort(); + mockServer = new MockGitLabServer({ + port: mockPort, + validTokens: [MOCK_TOKEN] + }); + await mockServer.start(); + + // Add custom route to capture User-Agent header + mockServer.addMockHandler('get', '/user', (req, res) => { + const userAgent = req.headers['user-agent']; + res.json({ + id: 1, + username: 'test_user', + name: 'Test User', + user_agent: userAgent + }); + }); + }); + + after(async () => { + if (mockServer) { + await mockServer.stop(); + } + }); + + test('User-Agent header should be set in API requests', async () => { + // Import node-fetch to make a test request + const fetch = (await import('node-fetch')).default; + + // Make a request to the mock server to verify User-Agent is set + const response = await fetch(`http://127.0.0.1:${mockPort}/api/v4/user`, { + headers: { + 'Authorization': `Bearer ${MOCK_TOKEN}`, + 'User-Agent': 'gitlab-mcp-server/2.0.23 (node-fetch)' + } + }); + + const data = await response.json() as any; + assert.ok(data.user_agent, 'User-Agent header should be present'); + assert.ok(data.user_agent.includes('gitlab-mcp-server'), 'User-Agent should include "gitlab-mcp-server"'); + assert.ok(data.user_agent.includes('node-fetch'), 'User-Agent should include "node-fetch"'); + }); +});