From 6f1d512e07d44bb2407b98edfda3a75d7a8abd3d Mon Sep 17 00:00:00 2001 From: zereight <42544600+zereight@users.noreply.github.com> Date: Thu, 15 Oct 2026 06:08:34 +0900 Subject: [PATCH] ci: make lint and format checks pass so they can be enforced `npm run lint` exited 2 on every run: ESLint 9 no longer reads .eslintrc.json, so it failed with "couldn't find an eslint.config file". `prettier --check` failed on 190 files. CI hid both behind `|| echo`. - Move the ESLint config to eslint.config.js (flat config) with the same rules. Drop the jest env; tests use node:test and no-undef is off for TypeScript. Add ignoreRestSiblings for `const { x, ...rest }` omits and allow single-extends interfaces. - Declare @eslint/js (already in the lockfile through eslint). - Fix the 76 existing lint errors. No logic changes: unused imports, constants and catch bindings removed, let -> const, one unused parameter renamed to _options, an unused helper removed from index.ts, and a disable comment on the intentional control-character regex. - List the files that Prettier would currently reformat in .prettierignore, so `prettier --check` passes without a mass reformat. New files are checked. Remove entries as files get formatted. `npm run lint` now exits 0 (warnings only) and `npm run format:check` exits 0. --- .eslintrc.json | 24 --- .prettierignore | 140 +++++++++++++++++- eslint.config.js | 35 +++++ index.ts | 31 +--- mcp-server/src/prd-tools.ts | 2 +- package-lock.json | 1 + package.json | 5 +- test/callback-proxy-tests.ts | 3 +- test/client-pool-test.ts | 3 +- test/clients/custom-header-client.ts | 2 +- test/clients/sse-client.ts | 2 +- test/clients/stdio-client.ts | 2 +- test/clients/streamable-http-client.ts | 2 +- test/dynamic-api-url-test.ts | 6 +- test/mcp-oauth-tests.ts | 7 +- test/no-proxy-integration-test.ts | 3 +- test/remote-auth-simple-test.ts | 6 +- test/remote-auth-tests.ts | 1 - test/schema-tests.ts | 6 - test/stateless/session-id-integration.test.ts | 4 +- test/test-geteffectiveprojectid.ts | 14 +- test/test-health-check.ts | 2 +- test/test-issue-description-patch.ts | 2 - test/test-list-group-members.ts | 2 +- test/test-list-merge-requests.ts | 2 +- test/test-list-project-members.ts | 2 +- test/test-mr-diffs-filter.ts | 2 +- test/test-mr-file-diffs.ts | 4 +- test/test-oauth-proxy-rate-limit.ts | 3 +- test/test-permission-mode.ts | 1 - test/test-search-code.ts | 1 - test/test-token-optimizations.ts | 1 - test/test-toolset-filtering.ts | 1 - test/test-update-issue-slim.ts | 1 - test/utils/mock-gitlab-server.ts | 1 - test/utils/safe-redirect-fetch.test.ts | 3 - test/utils/server-launcher.ts | 1 - utils/graphql-query.ts | 3 +- utils/helpers.ts | 2 +- utils/safe-redirect-fetch.ts | 2 +- utils/url.ts | 1 + 41 files changed, 216 insertions(+), 120 deletions(-) delete mode 100644 .eslintrc.json create mode 100644 eslint.config.js diff --git a/.eslintrc.json b/.eslintrc.json deleted file mode 100644 index 14d4edd9f..000000000 --- a/.eslintrc.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "parser": "@typescript-eslint/parser", - "extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"], - "plugins": ["@typescript-eslint"], - "parserOptions": { - "ecmaVersion": 2022, - "sourceType": "module" - }, - "env": { - "node": true, - "es2022": true, - "jest": true - }, - "rules": { - "no-console": "warn", - "prefer-const": "error", - "no-unused-vars": "off", - "@typescript-eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_" }], - "@typescript-eslint/explicit-module-boundary-types": "off", - "@typescript-eslint/no-explicit-any": "warn", - "@typescript-eslint/no-non-null-assertion": "warn" - }, - "ignorePatterns": ["node_modules/", "build/", "coverage/", "*.js"] -} diff --git a/.prettierignore b/.prettierignore index 3212e2239..2a883012d 100644 --- a/.prettierignore +++ b/.prettierignore @@ -11,4 +11,142 @@ docs/tools/*.md docs/reference/tool-coverage.md # MkDocs build output site/ -.venv-docs/ \ No newline at end of file +.venv-docs/ + +# Not yet formatted with Prettier. Remove entries as files are reformatted. +/.github/agents/ +/.github/skills/ +/mcp-server/src/ +/.github/copilot-instructions.md +/.github/prompts/quick-fix.prompt.md +/.github/prompts/quick-plan.prompt.md +/.well-known/mcp/server-card.json +/CHANGELOG.md +/cli-command.ts +/cli/args.ts +/cli/compact-mcp-result.ts +/cli/curated.ts +/cli/help.ts +/cli/output.ts +/cli/router.ts +/cli/tables.ts +/customSchemas.ts +/docker/DOCKERHUB.md +/docker/README.md +/docs/auth/oauth-callback-proxy.md +/docs/clients/vscode.md +/docs/comparison/community-gitlab-mcp-a.md +/docs/comparison/index.md +/docs/configuration/dynamic-api-url.md +/docs/configuration/multi-instance-design.md +/docs/configuration/stateless-mode.md +/docs/features/resolve-issue-thread.md +/docs/getting-started/cli-arguments.md +/docs/getting-started/cli-without-mcp.md +/docs/reference/cli-usage-design.md +/docs/reference/dependency-proxy-design.md +/docs/reference/github-pages-setup.md +/event.json +/oauth-device-flow.ts +/oauth-proxy.ts +/oauth.ts +/PR_SUMMARY.md +/README.ko.md +/README.md +/README.pt-BR.md +/README.zh-CN.md +/schemas.ts +/scripts/check-skill-sync.ts +/server.json +/server/metrics.ts +/skills/gitlab-mcp/reference/issues.md +/skills/gitlab-mcp/SKILL.md +/skills/gl-cli/SKILL.md +/stateless/client-id.ts +/stateless/consumed-proxy-code-cache.ts +/stateless/errors.ts +/stateless/index.ts +/stateless/pending-auth.ts +/stateless/secret.ts +/stateless/session-id.ts +/stateless/types.ts +/test-cli.js +/test-note.ts +/test-resolve-issue-note.ts +/test/callback-proxy-tests.ts +/test/cli/compact-mcp-result.test.ts +/test/cli/credentials.test.ts +/test/cli/output.test.ts +/test/cli/replay-command.test.ts +/test/cli/router.test.ts +/test/client-pool-test.ts +/test/clients/client.ts +/test/clients/custom-header-client.ts +/test/clients/sse-client.ts +/test/clients/stdio-client.ts +/test/clients/streamable-http-client.ts +/test/config-allowed-groups.test.ts +/test/create-commit-actions.test.ts +/test/dynamic-api-url-test.ts +/test/dynamic-routing-tests.ts +/test/gitlab-artifact-entry-schema.test.ts +/test/mcp-oauth-tests.ts +/test/multi-server-test.ts +/test/no-proxy-integration-test.ts +/test/no-proxy-test.ts +/test/oauth-scopes.test.ts +/test/oauth-startup.test.ts +/test/oauth-tests.ts +/test/remote-auth-simple-test.ts +/test/remote-auth-tests.ts +/test/schema-tests.ts +/test/stateless/callback-proxy.test.ts +/test/stateless/client-id.test.ts +/test/stateless/codec.test.ts +/test/stateless/config-ttl.test.ts +/test/stateless/consumed-proxy-code-cache.test.ts +/test/stateless/session-id-integration.test.ts +/test/streamable-http-sse-stream.test.ts +/test/streamable-http-unauthenticated-discovery.test.ts +/test/test-all-transport-server.ts +/test/test-blank-filters.ts +/test/test-ci-variables.ts +/test/test-dependency-proxy.ts +/test/test-deployment-tools.ts +/test/test-download-attachment.ts +/test/test-empty-toolsets.ts +/test/test-get-file-blame.ts +/test/test-geteffectiveprojectid.ts +/test/test-issue-description-patch.ts +/test/test-job-artifacts.ts +/test/test-json-schema.ts +/test/test-list-group-members.ts +/test/test-list-merge-requests.ts +/test/test-list-project-members.ts +/test/test-merge-request-approval-state-tools.ts +/test/test-merge-request-pipelines.ts +/test/test-mr-diffs-filter.ts +/test/test-mr-file-diffs.ts +/test/test-remote-downloads.ts +/test/test-search-code.ts +/test/test-snippets.ts +/test/test-tags.ts +/test/test-todos.ts +/test/test-token-optimizations.ts +/test/test-toolset-filtering.ts +/test/test-upload-markdown.ts +/test/utils/bulk-publish-options.test.ts +/test/utils/forwarded-public-base-url.test.ts +/test/utils/graphql-query.test.ts +/test/utils/mock-gitlab-server.ts +/test/utils/redact-url.test.ts +/test/utils/tool-args.test.ts +/test/utils/version-check.test.ts +/utils/forwarded-public-base-url.ts +/utils/gitlab-instance-version.ts +/utils/helpers.ts +/utils/merge-request-position.ts +/utils/patch-helper.ts +/utils/proxy-client-ip.ts +/utils/schema.ts +/utils/version-check.ts diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 000000000..b3b775fe1 --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,35 @@ +import js from "@eslint/js"; +import tseslint from "@typescript-eslint/eslint-plugin"; +import tsParser from "@typescript-eslint/parser"; + +const tsFiles = ["**/*.ts"]; + +export default [ + { ignores: ["node_modules/", "build/", "coverage/", "**/*.js", "**/*.mjs", "**/*.cjs"] }, + { ...js.configs.recommended, files: tsFiles }, + ...tseslint.configs["flat/recommended"].map(config => ({ ...config, files: tsFiles })), + { + files: tsFiles, + languageOptions: { + parser: tsParser, + ecmaVersion: 2022, + sourceType: "module", + }, + rules: { + "no-console": "warn", + "prefer-const": "error", + "no-unused-vars": "off", + "@typescript-eslint/no-unused-vars": [ + "error", + { argsIgnorePattern: "^_", ignoreRestSiblings: true }, + ], + "@typescript-eslint/no-empty-object-type": [ + "error", + { allowInterfaces: "with-single-extends" }, + ], + "@typescript-eslint/explicit-module-boundary-types": "off", + "@typescript-eslint/no-explicit-any": "warn", + "@typescript-eslint/no-non-null-assertion": "warn", + }, + }, +]; diff --git a/index.ts b/index.ts index ee0c16ac9..bc83643cc 100644 --- a/index.ts +++ b/index.ts @@ -638,7 +638,6 @@ import { UpdateIssueNoteSchema, UpdateIssueSchema, UpdateIssueDescriptionPatchSchema, - type UpdateIssueDescriptionPatchOptions, UpdateLabelSchema, UpdateProjectSchema, UpdateMergeRequestNoteSchema, @@ -712,14 +711,7 @@ import { HealthCheckSchema, } from "./schemas.js"; -import { - randomUUID, - createCipheriv, - createDecipheriv, - randomBytes, - createHash, - timingSafeEqual, -} from "node:crypto"; +import { randomUUID, timingSafeEqual } from "node:crypto"; import { createLogger, flushBufferedLogs, flushStartupLogs } from "./utils/logger.js"; const logger = createLogger(); @@ -1225,23 +1217,6 @@ function createServer(): McpServer { return mcpServer; } -/** - * Validate configuration at startup - */ -function isLoopbackBindHost(host: string): boolean { - const normalized = host - .trim() - .toLowerCase() - .replace(/^\[|\]$/g, ""); - const isIpv4Loopback = /^127(?:\.\d{1,3}){3}$/.test(normalized); - return ( - normalized === "localhost" || - isIpv4Loopback || - normalized === "::1" || - normalized === "0:0:0:0:0:0:0:1" - ); -} - function formatHostWithPort(host: string, port: number): string | null { const normalized = host .trim() @@ -9783,7 +9758,7 @@ async function getCommitDiff( projectId = decodeURIComponent(projectId); const baseUrl = `${getEffectiveApiUrl()}/projects/${encodeGitLabPathSegment(getEffectiveProjectId(projectId))}/repository/commits/${encodeGitLabPathSegment(sha)}/diff`; - let allDiffs: GitLabMergeRequestDiff[] = []; + const allDiffs: GitLabMergeRequestDiff[] = []; let page = 1; while (true) { @@ -9977,7 +9952,7 @@ async function myIssues(options: MyIssuesOptions = {}): Promise { } // Use listIssues with assignee_username filter - let listIssuesOptions: Omit, "project_id"> = { + const listIssuesOptions: Omit, "project_id"> = { state: options.state || "opened", // Default to "opened" if not specified labels: options.labels, milestone: options.milestone, diff --git a/mcp-server/src/prd-tools.ts b/mcp-server/src/prd-tools.ts index a3bbacb29..bc4faa60d 100644 --- a/mcp-server/src/prd-tools.ts +++ b/mcp-server/src/prd-tools.ts @@ -1,7 +1,7 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { z } from "zod"; import * as path from "node:path"; -import { getWorkspaceRoot, ensureDir, safeReadFile, safeWriteFile, safeJsonParse, generateId, errorResponse } from "./utils.js"; +import { getWorkspaceRoot, ensureDir, safeReadFile, safeWriteFile, safeJsonParse, generateId } from "./utils.js"; function getPrdPath(): string { return path.join(getWorkspaceRoot(), ".omc", "prd.json"); diff --git a/package-lock.json b/package-lock.json index 3bfc634dd..6f297df6c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -30,6 +30,7 @@ "zereight-mcp-gitlab": "build/index.js" }, "devDependencies": { + "@eslint/js": "^9.27.0", "@types/express": "^5.0.2", "@types/jmespath": "^0.15.2", "@types/node": "^22.13.10", diff --git a/package.json b/package.json index a3308ea99..d9c875000 100644 --- a/package.json +++ b/package.json @@ -62,11 +62,11 @@ "test:oauth": "tsx test/oauth-tests.ts && node --import tsx/esm --test --experimental-test-isolation=none test/oauth-device-flow-tests.ts", "test:list-merge-requests": "npm run build && tsx test/test-list-merge-requests.ts", "test:approvals": "npm run build && tsx test/test-merge-request-approvals.ts", - "lint": "eslint . --ext .ts", + "lint": "eslint .", "check:runtime-deps": "node scripts/check-runtime-deps.mjs", "check:skill-sync": "tsx scripts/check-skill-sync.ts", "docs:tools": "tsx scripts/generate-tool-docs.ts && tsx scripts/generate-tool-coverage.ts", - "lint:fix": "eslint . --ext .ts --fix", + "lint:fix": "eslint . --fix", "release": "bash scripts/release.sh", "release:mcp-registry": "bash scripts/publish_mcp_registry.sh", "format": "prettier --write \"**/*.{js,ts,json,md}\"", @@ -90,6 +90,7 @@ "zod": "^4.6.5" }, "devDependencies": { + "@eslint/js": "^9.27.0", "@types/express": "^5.0.2", "@types/jmespath": "^0.15.2", "@types/node": "^22.13.10", diff --git a/test/callback-proxy-tests.ts b/test/callback-proxy-tests.ts index 497105977..7425176a7 100644 --- a/test/callback-proxy-tests.ts +++ b/test/callback-proxy-tests.ts @@ -19,7 +19,6 @@ import { } from "./utils/server-launcher.js"; import { MockGitLabServer, findMockServerPort } from "./utils/mock-gitlab-server.js"; -const MOCK_GITLAB_PORT_BASE = 9400; const MCP_SERVER_PORT_BASE = 3400; const MOCK_ACCESS_TOKEN = "ya29.mock-callback-proxy-token-123456"; const MOCK_REFRESH_TOKEN = "mock-refresh-token-abcdef"; @@ -129,7 +128,7 @@ describe("Callback Proxy Mode", () => { let mcpBaseUrl: string; let mockGitLab: MockGitLabServer; let mockGitLabUrl: string; - let servers: ServerInstance[] = []; + const servers: ServerInstance[] = []; const clientRedirectUri = "http://localhost:19999/oauth/callback"; // Track the last code+verifier GitLab received so we can verify the proxy PKCE diff --git a/test/client-pool-test.ts b/test/client-pool-test.ts index 5632515e4..e2767726b 100644 --- a/test/client-pool-test.ts +++ b/test/client-pool-test.ts @@ -21,7 +21,6 @@ const MOCK_TOKEN = 'glpat-mock-token-12345'; const POOL_MAX_SIZE = 2; // Port ranges -const MOCK_GITLAB_PORT_BASE = 9500; const MCP_SERVER_PORT_BASE = 3500; console.log('🏊 Client Pool Test Suite'); @@ -30,7 +29,7 @@ console.log(''); describe('Client Pool Limits', () => { let mcpUrl: string; let mockGitLab: MockGitLabServer; - let servers: ServerInstance[] = []; + const servers: ServerInstance[] = []; let mockGitLabUrl: string; before(async () => { diff --git a/test/clients/custom-header-client.ts b/test/clients/custom-header-client.ts index 0ce81e7e5..71f3bc572 100644 --- a/test/clients/custom-header-client.ts +++ b/test/clients/custom-header-client.ts @@ -72,7 +72,7 @@ export class CustomHeaderClient { if (this.transport) { try { await this.transport.close(); - } catch (error) { + } catch { // Silently ignore disconnect errors in tests // In production, you'd want proper logging } finally { diff --git a/test/clients/sse-client.ts b/test/clients/sse-client.ts index 1b86397d5..415c01ac8 100644 --- a/test/clients/sse-client.ts +++ b/test/clients/sse-client.ts @@ -99,7 +99,7 @@ export class SSETestClient implements MCPClientInterface { try { const tools = await this.listTools(); return Array.isArray(tools.tools) && tools.tools.length > 0; - } catch (error) { + } catch { return false; } } diff --git a/test/clients/stdio-client.ts b/test/clients/stdio-client.ts index 41605f33a..2efeffe90 100644 --- a/test/clients/stdio-client.ts +++ b/test/clients/stdio-client.ts @@ -122,7 +122,7 @@ export class StdioTestClient implements MCPClientInterface { try { const tools = await this.listTools(); return Array.isArray(tools.tools) && tools.tools.length > 0; - } catch (error) { + } catch { return false; } } diff --git a/test/clients/streamable-http-client.ts b/test/clients/streamable-http-client.ts index 390f8df37..6e6ec438c 100644 --- a/test/clients/streamable-http-client.ts +++ b/test/clients/streamable-http-client.ts @@ -99,7 +99,7 @@ export class StreamableHTTPTestClient implements MCPClientInterface { try { const tools = await this.listTools(); return Array.isArray(tools.tools) && tools.tools.length > 0; - } catch (error) { + } catch { return false; } } diff --git a/test/dynamic-api-url-test.ts b/test/dynamic-api-url-test.ts index a946993b8..86ca241b0 100644 --- a/test/dynamic-api-url-test.ts +++ b/test/dynamic-api-url-test.ts @@ -21,8 +21,6 @@ const MOCK_TOKEN_1 = 'glpat-mock-token-instance-1'; const MOCK_TOKEN_2 = 'glpat-mock-token-instance-2'; // Port ranges -const MOCK_GITLAB_PORT_BASE_1 = 9100; -const MOCK_GITLAB_PORT_BASE_2 = 9200; const MCP_SERVER_PORT_BASE = 3100; console.log('🌐 Dynamic GitLab API URL Test Suite'); @@ -34,7 +32,7 @@ describe('Dynamic API URL - Multiple GitLab Instances', () => { let mockGitLab2: MockGitLabServer; let mockGitLabUrl1: string; let mockGitLabUrl2: string; - let servers: ServerInstance[] = []; + const servers: ServerInstance[] = []; before(async () => { // Start first mock GitLab server @@ -246,7 +244,7 @@ describe('Dynamic API URL - Connection Pool', () => { let mockGitLab2: MockGitLabServer; let mockGitLabUrl1: string; let mockGitLabUrl2: string; - let servers: ServerInstance[] = []; + const servers: ServerInstance[] = []; before(async () => { // Start mock GitLab servers diff --git a/test/mcp-oauth-tests.ts b/test/mcp-oauth-tests.ts index 19ce7dce5..f0469da44 100644 --- a/test/mcp-oauth-tests.ts +++ b/test/mcp-oauth-tests.ts @@ -28,7 +28,6 @@ const MOCK_CLIENT_ID = "mock-app-uid-from-dcr"; const MOCK_PAT_TOKEN = "glpat-mockpat-testtoken-abcdef12"; // ≥20 chars, valid charset const MOCK_JOB_TOKEN = "mockjobtoken-testenv-1234567890"; // ≥20 chars, valid charset -const MOCK_GITLAB_PORT_BASE = 9200; const MCP_SERVER_PORT_BASE = 3200; // --------------------------------------------------------------------------- @@ -105,7 +104,7 @@ function addOAuthEndpoints( describe("MCP OAuth — Discovery Endpoints", () => { let mcpBaseUrl: string; let mockGitLab: MockGitLabServer; - let servers: ServerInstance[] = []; + const servers: ServerInstance[] = []; before(async () => { const mockPort = await findMockServerPort(); @@ -266,7 +265,7 @@ describe("MCP OAuth — /mcp Auth Enforcement", () => { let mcpUrl: string; let mcpBaseUrl: string; let mockGitLab: MockGitLabServer; - let servers: ServerInstance[] = []; + const servers: ServerInstance[] = []; before(async () => { const mockPort = await findMockServerPort(); @@ -783,7 +782,7 @@ describe("MCP OAuth — Header Auth Fallback", () => { let mcpUrl: string; let mcpBaseUrl: string; let mockGitLab: MockGitLabServer; - let servers: ServerInstance[] = []; + const servers: ServerInstance[] = []; before(async () => { const mockPort = await findMockServerPort(); diff --git a/test/no-proxy-integration-test.ts b/test/no-proxy-integration-test.ts index a69772a63..7943b5fc7 100644 --- a/test/no-proxy-integration-test.ts +++ b/test/no-proxy-integration-test.ts @@ -20,7 +20,6 @@ import { CustomHeaderClient } from './clients/custom-header-client.js'; const MOCK_TOKEN = 'glpat-mock-token-12345'; // Port ranges -const MOCK_GITLAB_PORT_BASE = 9600; const MCP_SERVER_PORT_BASE = 3600; console.log('🌐 NO_PROXY Integration Test Suite'); @@ -29,7 +28,7 @@ console.log(''); describe('NO_PROXY Integration Tests', () => { let mcpUrl: string; let mockGitLab: MockGitLabServer; - let servers: ServerInstance[] = []; + const servers: ServerInstance[] = []; let mockGitLabUrl: string; let mockGitLabHost: string; diff --git a/test/remote-auth-simple-test.ts b/test/remote-auth-simple-test.ts index 406dba24b..b97981f55 100644 --- a/test/remote-auth-simple-test.ts +++ b/test/remote-auth-simple-test.ts @@ -21,8 +21,6 @@ const MOCK_TOKEN = 'glpat-mock-token-12345'; const MOCK_JOB_TOKEN = 'glcbt-mock-job-token-9876'; // Port ranges to avoid collisions -const MOCK_GITLAB_PORT_BASE = 9000; -const MOCK_GITLAB_PORT_OFFSET = 500; // Offset for timeout test suite const MCP_SERVER_PORT_BASE = 3000; const MCP_SERVER_PORT_OFFSET = 500; // Offset for timeout test suite @@ -76,7 +74,7 @@ describe('Remote Authorization', { concurrency: 1 }, () => { describe('Remote Authorization - Basic Functionality', () => { let mcpUrl: string; let mockGitLab: MockGitLabServer; - let servers: ServerInstance[] = []; + const servers: ServerInstance[] = []; before(async () => { // Start mock GitLab server @@ -235,7 +233,7 @@ describe('Remote Authorization - Basic Functionality', () => { describe('Remote Authorization - Session Timeout', () => { let mcpUrl: string; let mockGitLab: MockGitLabServer; - let servers: ServerInstance[] = []; + const servers: ServerInstance[] = []; before(async () => { // Use different port ranges to avoid collisions with basic tests diff --git a/test/remote-auth-tests.ts b/test/remote-auth-tests.ts index a08a65613..101f2fda4 100644 --- a/test/remote-auth-tests.ts +++ b/test/remote-auth-tests.ts @@ -3,7 +3,6 @@ * Tests for per-session HTTP header-based authorization */ -import * as path from 'path'; import { describe, test, after, before } from 'node:test'; import assert from 'node:assert'; import { diff --git a/test/schema-tests.ts b/test/schema-tests.ts index c2c149725..7a4f1023c 100644 --- a/test/schema-tests.ts +++ b/test/schema-tests.ts @@ -12,14 +12,8 @@ import { CreateIssueEmojiReactionSchema, DeleteMergeRequestEmojiReactionSchema, DeleteIssueEmojiReactionSchema, - CreateMergeRequestNoteEmojiReactionSchema, - DeleteMergeRequestNoteEmojiReactionSchema, - CreateIssueNoteEmojiReactionSchema, - DeleteIssueNoteEmojiReactionSchema, CreateWorkItemEmojiReactionSchema, - DeleteWorkItemEmojiReactionSchema, CreateWorkItemNoteEmojiReactionSchema, - DeleteWorkItemNoteEmojiReactionSchema, CreateIssueSchema, ListIssuesSchema, ListMergeRequestsSchema, diff --git a/test/stateless/session-id-integration.test.ts b/test/stateless/session-id-integration.test.ts index 86b4ced1d..05ae320a1 100644 --- a/test/stateless/session-id-integration.test.ts +++ b/test/stateless/session-id-integration.test.ts @@ -33,13 +33,12 @@ import { const MOCK_TOKEN = "glpat-mockstateless-12345-abcdef"; // Use unusual port ranges to avoid colliding with other suites. -const MOCK_PORT_BASE = 9800; const SERVER_PORT_BASE_A = 3800; const SERVER_PORT_BASE_B = 3850; const SERVER_PORT_BASE_TTL = 3900; describe("Stateless Mcp-Session-Id — cross-pod integration", () => { - let servers: ServerInstance[] = []; + const servers: ServerInstance[] = []; let mockGitLab: MockGitLabServer; let urlA: string; let urlB: string; @@ -601,7 +600,6 @@ describe("Stateless Mcp-Session-Id — inactivity-TTL semantics", () => { describe("Stateless Mcp-Session-Id — OAuth + sid-only follow-up", () => { const MOCK_OAUTH_TOKEN = "ya29.mock-oauth-token-stateless-abcdef"; const MOCK_CLIENT_ID = "mock-app-uid-oauth-stateless"; - const OAUTH_MOCK_PORT_BASE = 9950; const OAUTH_SERVER_PORT_BASE_A = 3950; const OAUTH_SERVER_PORT_BASE_B = 3970; diff --git a/test/test-geteffectiveprojectid.ts b/test/test-geteffectiveprojectid.ts index 2860730fa..ce1690186 100644 --- a/test/test-geteffectiveprojectid.ts +++ b/test/test-geteffectiveprojectid.ts @@ -36,7 +36,7 @@ describe('getEffectiveProjectId', { concurrency: 1 }, () => { describe('getEffectiveProjectId - No GITLAB_ALLOWED_PROJECT_IDS', () => { let mcpUrl: string; let mockGitLab: MockGitLabServer; - let servers: ServerInstance[] = []; + const servers: ServerInstance[] = []; let client: CustomHeaderClient; let group123Requested = false; let group789Requested = false; @@ -341,7 +341,7 @@ describe('getEffectiveProjectId - No GITLAB_ALLOWED_PROJECT_IDS', () => { describe('getEffectiveProjectId - With single GITLAB_ALLOWED_PROJECT_IDS', () => { let mcpUrl: string; let mockGitLab: MockGitLabServer; - let servers: ServerInstance[] = []; + const servers: ServerInstance[] = []; let client: CustomHeaderClient; before(async () => { @@ -448,7 +448,7 @@ describe('getEffectiveProjectId - With single GITLAB_ALLOWED_PROJECT_IDS', () => describe('getEffectiveProjectId - With multiple GITLAB_ALLOWED_PROJECT_IDS', () => { let mcpUrl: string; let mockGitLab: MockGitLabServer; - let servers: ServerInstance[] = []; + const servers: ServerInstance[] = []; let client: CustomHeaderClient; before(async () => { @@ -542,7 +542,7 @@ describe('getEffectiveProjectId - With multiple GITLAB_ALLOWED_PROJECT_IDS', () describe('GITLAB_PROJECT_ID guards repository and group mutators', () => { let mcpUrl: string; let mockGitLab: MockGitLabServer; - let servers: ServerInstance[] = []; + const servers: ServerInstance[] = []; let client: CustomHeaderClient; before(async () => { @@ -644,7 +644,7 @@ describe('GITLAB_PROJECT_ID guards repository and group mutators', () => { describe('GITLAB_ALLOWED_PROJECT_IDS guards repository and group mutators (allowlist-only, no GITLAB_PROJECT_ID)', () => { let mcpUrl: string; let mockGitLab: MockGitLabServer; - let servers: ServerInstance[] = []; + const servers: ServerInstance[] = []; let client: CustomHeaderClient; before(async () => { @@ -785,7 +785,7 @@ describe('GITLAB_ALLOWED_PROJECT_IDS guards repository and group mutators (allow describe('GITLAB_READ_ONLY_MODE enforces read-only for all write tools', () => { let mcpUrl: string; let mockGitLab: MockGitLabServer; - let servers: ServerInstance[] = []; + const servers: ServerInstance[] = []; let client: CustomHeaderClient; before(async () => { @@ -852,7 +852,7 @@ describe('GITLAB_READ_ONLY_MODE enforces read-only for all write tools', () => { describe('GITLAB_PROJECT_ID guards dependency proxy tools', () => { let mcpUrl: string; let mockGitLab: MockGitLabServer; - let servers: ServerInstance[] = []; + const servers: ServerInstance[] = []; let client: CustomHeaderClient; before(async () => { diff --git a/test/test-health-check.ts b/test/test-health-check.ts index fd5124cf7..ad5f28a38 100644 --- a/test/test-health-check.ts +++ b/test/test-health-check.ts @@ -1,4 +1,4 @@ -import { describe, test, before, after } from "node:test"; +import { describe, test } from "node:test"; import assert from "node:assert"; import { spawn } from "child_process"; import fs from "node:fs"; diff --git a/test/test-issue-description-patch.ts b/test/test-issue-description-patch.ts index 1ee16a9b7..dc592e4b1 100644 --- a/test/test-issue-description-patch.ts +++ b/test/test-issue-description-patch.ts @@ -7,7 +7,6 @@ import { describe, test, before, after } from "node:test"; import assert from "node:assert"; import { launchServer, - findAvailablePort, cleanupServers, ServerInstance, TransportMode, @@ -180,7 +179,6 @@ describe("update_issue_description_patch MCP tool", () => { let server: ServerInstance; let client: CustomHeaderClient; - const MOCK_PORT_BASE = 9600; const MCP_PORT_BASE = 3600; let portCounter = 0; diff --git a/test/test-list-group-members.ts b/test/test-list-group-members.ts index 676ab9483..afa9e7f11 100644 --- a/test/test-list-group-members.ts +++ b/test/test-list-group-members.ts @@ -64,7 +64,7 @@ async function callListGroupMembers(args: ListGroupMembersOptions, env: NodeJS.P if (content) { try { resolve(JSON.parse(content)); - } catch (e) { + } catch { reject(new Error(`Failed to parse tool output JSON: ${content}`)); } } else { diff --git a/test/test-list-merge-requests.ts b/test/test-list-merge-requests.ts index 5ae46ca99..e388f9cd6 100644 --- a/test/test-list-merge-requests.ts +++ b/test/test-list-merge-requests.ts @@ -40,7 +40,7 @@ async function callListMergeRequests(args: Record = {}, env: NodeJS if (content) { try { resolve(JSON.parse(content)); - } catch (e) { + } catch { reject(new Error(`Failed to parse tool output JSON: ${content}`)); } } else { diff --git a/test/test-list-project-members.ts b/test/test-list-project-members.ts index 783ad0ac5..b124f76ea 100644 --- a/test/test-list-project-members.ts +++ b/test/test-list-project-members.ts @@ -64,7 +64,7 @@ async function callListProjectMembers(args: ListProjectMembersOptions, env: Node if (content) { try { resolve(JSON.parse(content)); - } catch (e) { + } catch { reject(new Error(`Failed to parse tool output JSON: ${content}`)); } } else { diff --git a/test/test-mr-diffs-filter.ts b/test/test-mr-diffs-filter.ts index 3ee269dde..f7c140541 100644 --- a/test/test-mr-diffs-filter.ts +++ b/test/test-mr-diffs-filter.ts @@ -41,7 +41,7 @@ async function callGetMergeRequestDiffs(args: Record = {}, env: Nod if (content) { try { resolve(JSON.parse(content)); - } catch (e) { + } catch { reject(new Error(`Failed to parse tool output JSON: ${content}`)); } } else { diff --git a/test/test-mr-file-diffs.ts b/test/test-mr-file-diffs.ts index abc386098..ea2a8b42c 100644 --- a/test/test-mr-file-diffs.ts +++ b/test/test-mr-file-diffs.ts @@ -41,7 +41,7 @@ async function callListMergeRequestChangedFiles(args: Record = {}, if (content) { try { resolve(JSON.parse(content)); - } catch (e) { + } catch { reject(new Error(`Failed to parse tool output JSON: ${content}`)); } } else { @@ -94,7 +94,7 @@ async function callGetMergeRequestFileDiff(args: Record = {}, env: if (content) { try { resolve(JSON.parse(content)); - } catch (e) { + } catch { reject(new Error(`Failed to parse tool output JSON: ${content}`)); } } else { diff --git a/test/test-oauth-proxy-rate-limit.ts b/test/test-oauth-proxy-rate-limit.ts index 1e7097c2d..180993787 100644 --- a/test/test-oauth-proxy-rate-limit.ts +++ b/test/test-oauth-proxy-rate-limit.ts @@ -14,7 +14,6 @@ import { } from "./utils/server-launcher.js"; import { MockGitLabServer, findMockServerPort } from "./utils/mock-gitlab-server.js"; -const MOCK_GITLAB_PORT_BASE = 9210; const MCP_SERVER_PORT_BASE = 3210; const MOCK_CLIENT_ID = "mock-app-uid-from-dcr"; @@ -48,7 +47,7 @@ describe("When MCP OAuth runs behind a trusted proxy", () => { describe("with X-Forwarded-For containing client ports", () => { let mcpBaseUrl: string; let mockGitLab: MockGitLabServer; - let servers: ServerInstance[] = []; + const servers: ServerInstance[] = []; before(async () => { const mockPort = await findMockServerPort(); diff --git a/test/test-permission-mode.ts b/test/test-permission-mode.ts index ba34460f1..68c95af5a 100644 --- a/test/test-permission-mode.ts +++ b/test/test-permission-mode.ts @@ -22,7 +22,6 @@ import { CustomHeaderClient } from "./clients/custom-header-client.js"; const MOCK_TOKEN = "glpat-permission-mode-test"; -const MOCK_PORT_BASE = 9700; const MCP_PORT_BASE = 3700; let mockGitLab: MockGitLabServer; diff --git a/test/test-search-code.ts b/test/test-search-code.ts index 3f4f4b124..cdcf83394 100644 --- a/test/test-search-code.ts +++ b/test/test-search-code.ts @@ -24,7 +24,6 @@ import { CustomHeaderClient } from "./clients/custom-header-client.js"; const MOCK_TOKEN = "glpat-search-test-token"; // Port bases that don't conflict with other test suites -const MOCK_PORT_BASE = 9300; const MCP_PORT_BASE = 3300; let portCounter = 0; diff --git a/test/test-token-optimizations.ts b/test/test-token-optimizations.ts index a22978103..05a38348b 100644 --- a/test/test-token-optimizations.ts +++ b/test/test-token-optimizations.ts @@ -25,7 +25,6 @@ import { CustomHeaderClient } from "./clients/custom-header-client.js"; const MOCK_TOKEN = "glpat-token-opt-test"; // Port bases (offset to avoid collision with other suites) -const MOCK_PORT_BASE = 9400; const MCP_PORT_BASE = 3400; let portCounter = 0; diff --git a/test/test-toolset-filtering.ts b/test/test-toolset-filtering.ts index d7520ca42..df705650d 100644 --- a/test/test-toolset-filtering.ts +++ b/test/test-toolset-filtering.ts @@ -28,7 +28,6 @@ import { const MOCK_TOKEN = "glpat-toolset-test-token"; // Port bases (offset from other test suites to avoid collisions) -const MOCK_PORT_BASE = 9200; const MCP_PORT_BASE = 3200; // Known tool counts per toolset (from TOOLSET_DEFINITIONS) diff --git a/test/test-update-issue-slim.ts b/test/test-update-issue-slim.ts index efd1d9922..d4687d748 100644 --- a/test/test-update-issue-slim.ts +++ b/test/test-update-issue-slim.ts @@ -24,7 +24,6 @@ describe("update_issue slim response", () => { let server: ServerInstance; let client: CustomHeaderClient; - const MOCK_PORT_BASE = 9650; const MCP_PORT_BASE = 3650; before(async () => { diff --git a/test/utils/mock-gitlab-server.ts b/test/utils/mock-gitlab-server.ts index 8ab90d993..d0c45e0d3 100644 --- a/test/utils/mock-gitlab-server.ts +++ b/test/utils/mock-gitlab-server.ts @@ -590,7 +590,6 @@ export class MockGitLabServer { // GET /api/v4/projects/:projectId/merge_requests/:mr_iid/diffs - Get paginated diffs this.app.get('/api/v4/projects/:projectId/merge_requests/:mr_iid/diffs', (req: AuthenticatedRequest, res: Response) => { - const mrIid = parseInt(req.params.mr_iid); // Paginate results for testing pagination logic const allDiffs = [ diff --git a/test/utils/safe-redirect-fetch.test.ts b/test/utils/safe-redirect-fetch.test.ts index fc68a6c0d..25b4a9baf 100644 --- a/test/utils/safe-redirect-fetch.test.ts +++ b/test/utils/safe-redirect-fetch.test.ts @@ -1,6 +1,5 @@ import assert from "node:assert/strict"; import http from "node:http"; -import type { IncomingHttpHeaders } from "node:http"; import { after, before, describe, test } from "node:test"; import { Agent, fetch as undiciFetch, MockAgent } from "undici"; import { @@ -20,7 +19,6 @@ let serverPort: number; let secondServer: http.Server; let secondOrigin: string; let secondPort: number; -let lastSecondHeaders: IncomingHttpHeaders = {}; /** Hosts the trusted-host predicate has been called with. */ const trustedHostArguments: string[] = []; @@ -135,7 +133,6 @@ describe("safe redirect fetch", () => { }); secondServer = http.createServer((req, res) => { - lastSecondHeaders = req.headers; res.writeHead(200).end("second"); }); diff --git a/test/utils/server-launcher.ts b/test/utils/server-launcher.ts index c639836a8..7268c89ed 100644 --- a/test/utils/server-launcher.ts +++ b/test/utils/server-launcher.ts @@ -37,7 +37,6 @@ export async function launchServer(config: ServerConfig): Promise( .map(pattern => { try { return new RegExp(pattern); - } catch (e) { + } catch { console.warn(`Invalid regex pattern ignored: ${pattern}`); return null; } diff --git a/utils/safe-redirect-fetch.ts b/utils/safe-redirect-fetch.ts index 95f5afb40..cbbaa4dfa 100644 --- a/utils/safe-redirect-fetch.ts +++ b/utils/safe-redirect-fetch.ts @@ -273,7 +273,7 @@ function withoutCredentialHeaders( async function assertRedirectTargetAllowed( target: URL, trusted: boolean, - options: FetchWithValidatedRedirectsOptions + _options: FetchWithValidatedRedirectsOptions ): Promise { if (trusted) { return; diff --git a/utils/url.ts b/utils/url.ts index fe1d62a55..6a01d10a2 100644 --- a/utils/url.ts +++ b/utils/url.ts @@ -44,6 +44,7 @@ const DOT_SEGMENT_PATTERN = /(^|[\\/])\.\.?([\\/]|$)/; const LEADING_SEPARATOR_PATTERN = /^[\\/]/; /** NUL, newline and the other control characters: never valid in a path segment. */ +// eslint-disable-next-line no-control-regex const CONTROL_CHARACTER_PATTERN = /[\u0000-\u001f\u007f]/; /**