#
tokens: 28176/50000 1/236 files (page 8/8)
lines: off (toggle) GitHub
raw markdown copy
This is page 8 of 8. Use http://codebase.md/sapientpants/sonarqube-mcp-server?page={x} to view the full context.

# Directory Structure

```
├── .adr-dir
├── .changeset
│   ├── config.json
│   └── README.md
├── .claude
│   ├── commands
│   │   ├── analyze-and-fix-github-issue.md
│   │   ├── fix-sonarqube-issues.md
│   │   ├── implement-github-issue.md
│   │   ├── release.md
│   │   ├── spec-feature.md
│   │   └── update-dependencies.md
│   ├── hooks
│   │   └── block-git-no-verify.ts
│   └── settings.json
├── .dockerignore
├── .github
│   ├── actionlint.yaml
│   ├── changeset.yml
│   ├── dependabot.yml
│   ├── ISSUE_TEMPLATE
│   │   ├── bug_report.md
│   │   └── feature_request.md
│   ├── pull_request_template.md
│   ├── scripts
│   │   ├── determine-artifact.sh
│   │   └── version-and-release.js
│   ├── workflows
│   │   ├── codeql.yml
│   │   ├── main.yml
│   │   ├── pr.yml
│   │   ├── publish.yml
│   │   ├── reusable-docker.yml
│   │   ├── reusable-security.yml
│   │   └── reusable-validate.yml
│   └── WORKFLOWS.md
├── .gitignore
├── .husky
│   ├── commit-msg
│   └── pre-commit
├── .markdownlint.yaml
├── .markdownlintignore
├── .npmrc
├── .prettierignore
├── .prettierrc
├── .trivyignore
├── .yaml-lint.yml
├── .yamllintignore
├── CHANGELOG.md
├── changes.md
├── CLAUDE.md
├── CODE_OF_CONDUCT.md
├── commitlint.config.js
├── COMPATIBILITY.md
├── CONTRIBUTING.md
├── docker-compose.yml
├── Dockerfile
├── docs
│   ├── architecture
│   │   └── decisions
│   │       ├── 0001-record-architecture-decisions.md
│   │       ├── 0002-use-node-js-with-typescript.md
│   │       ├── 0003-adopt-model-context-protocol-for-sonarqube-integration.md
│   │       ├── 0004-use-sonarqube-web-api-client-for-all-sonarqube-interactions.md
│   │       ├── 0005-domain-driven-design-of-sonarqube-modules.md
│   │       ├── 0006-expose-sonarqube-features-as-mcp-tools.md
│   │       ├── 0007-support-multiple-authentication-methods-for-sonarqube.md
│   │       ├── 0008-use-environment-variables-for-configuration.md
│   │       ├── 0009-file-based-logging-to-avoid-stdio-conflicts.md
│   │       ├── 0010-use-stdio-transport-for-mcp-communication.md
│   │       ├── 0011-docker-containerization-for-deployment.md
│   │       ├── 0012-add-elicitation-support-for-interactive-user-input.md
│   │       ├── 0014-current-security-model-and-future-oauth2-considerations.md
│   │       ├── 0015-transport-architecture-refactoring.md
│   │       ├── 0016-http-transport-with-oauth-2-0-metadata-endpoints.md
│   │       ├── 0017-comprehensive-audit-logging-system.md
│   │       ├── 0018-add-comprehensive-monitoring-and-observability.md
│   │       ├── 0019-simplify-to-stdio-only-transport-for-mcp-gateway-deployment.md
│   │       ├── 0020-testing-framework-and-strategy-vitest-with-property-based-testing.md
│   │       ├── 0021-code-quality-toolchain-eslint-prettier-strict-typescript.md
│   │       ├── 0022-package-manager-choice-pnpm.md
│   │       ├── 0023-release-management-with-changesets.md
│   │       ├── 0024-ci-cd-platform-github-actions.md
│   │       ├── 0025-container-and-security-scanning-strategy.md
│   │       ├── 0026-circuit-breaker-pattern-with-opossum.md
│   │       ├── 0027-docker-image-publishing-strategy-ghcr-to-docker-hub.md
│   │       └── 0028-session-based-http-transport-with-server-sent-events.md
│   ├── architecture.md
│   ├── security.md
│   └── troubleshooting.md
├── eslint.config.js
├── examples
│   └── http-client.ts
├── jest.config.js
├── LICENSE
├── LICENSES.md
├── osv-scanner.toml
├── package.json
├── pnpm-lock.yaml
├── README.md
├── scripts
│   ├── actionlint.sh
│   ├── ci-local.sh
│   ├── load-test.sh
│   ├── README.md
│   ├── run-all-tests.sh
│   ├── scan-container.sh
│   ├── security-scan.sh
│   ├── setup.sh
│   ├── test-monitoring-integration.sh
│   └── validate-docs.sh
├── SECURITY.md
├── sonar-project.properties
├── src
│   ├── __tests__
│   │   ├── additional-coverage.test.ts
│   │   ├── advanced-index.test.ts
│   │   ├── assign-issue.test.ts
│   │   ├── auth-methods.test.ts
│   │   ├── boolean-string-transform.test.ts
│   │   ├── components.test.ts
│   │   ├── config
│   │   │   └── service-accounts.test.ts
│   │   ├── dependency-injection.test.ts
│   │   ├── direct-handlers.test.ts
│   │   ├── direct-lambdas.test.ts
│   │   ├── direct-schema-validation.test.ts
│   │   ├── domains
│   │   │   ├── components-domain-full.test.ts
│   │   │   ├── components-domain.test.ts
│   │   │   ├── hotspots-domain.test.ts
│   │   │   └── source-code-domain.test.ts
│   │   ├── environment-validation.test.ts
│   │   ├── error-handler.test.ts
│   │   ├── error-handling.test.ts
│   │   ├── errors.test.ts
│   │   ├── function-tests.test.ts
│   │   ├── handlers
│   │   │   ├── components-handler-integration.test.ts
│   │   │   └── projects-authorization.test.ts
│   │   ├── handlers.test.ts
│   │   ├── handlers.test.ts.skip
│   │   ├── index.test.ts
│   │   ├── issue-resolution-elicitation.test.ts
│   │   ├── issue-resolution.test.ts
│   │   ├── issue-transitions.test.ts
│   │   ├── issues-enhanced-search.test.ts
│   │   ├── issues-new-parameters.test.ts
│   │   ├── json-array-transform.test.ts
│   │   ├── lambda-functions.test.ts
│   │   ├── lambda-handlers.test.ts.skip
│   │   ├── logger.test.ts
│   │   ├── mapping-functions.test.ts
│   │   ├── mocked-environment.test.ts
│   │   ├── null-to-undefined.test.ts
│   │   ├── parameter-transformations-advanced.test.ts
│   │   ├── parameter-transformations.test.ts
│   │   ├── protocol-version.test.ts
│   │   ├── pull-request-transform.test.ts
│   │   ├── quality-gates.test.ts
│   │   ├── schema-parameter-transforms.test.ts
│   │   ├── schema-transformation-mocks.test.ts
│   │   ├── schema-transforms.test.ts
│   │   ├── schema-validators.test.ts
│   │   ├── schemas
│   │   │   ├── components-schema.test.ts
│   │   │   ├── hotspots-tools-schema.test.ts
│   │   │   └── issues-schema.test.ts
│   │   ├── sonarqube-elicitation.test.ts
│   │   ├── sonarqube.test.ts
│   │   ├── source-code.test.ts
│   │   ├── standalone-handlers.test.ts
│   │   ├── string-to-number-transform.test.ts
│   │   ├── tool-handler-lambdas.test.ts
│   │   ├── tool-handlers.test.ts
│   │   ├── tool-registration-schema.test.ts
│   │   ├── tool-registration-transforms.test.ts
│   │   ├── transformation-util.test.ts
│   │   ├── transports
│   │   │   ├── base.test.ts
│   │   │   ├── factory.test.ts
│   │   │   ├── http.test.ts
│   │   │   ├── session-manager.test.ts
│   │   │   └── stdio.test.ts
│   │   ├── utils
│   │   │   ├── retry.test.ts
│   │   │   └── transforms.test.ts
│   │   ├── zod-boolean-transform.test.ts
│   │   ├── zod-schema-transforms.test.ts
│   │   └── zod-transforms.test.ts
│   ├── config
│   │   ├── service-accounts.ts
│   │   └── versions.ts
│   ├── domains
│   │   ├── base.ts
│   │   ├── components.ts
│   │   ├── hotspots.ts
│   │   ├── index.ts
│   │   ├── issues.ts
│   │   ├── measures.ts
│   │   ├── metrics.ts
│   │   ├── projects.ts
│   │   ├── quality-gates.ts
│   │   ├── source-code.ts
│   │   └── system.ts
│   ├── errors.ts
│   ├── handlers
│   │   ├── components.ts
│   │   ├── hotspots.ts
│   │   ├── index.ts
│   │   ├── issues.ts
│   │   ├── measures.ts
│   │   ├── metrics.ts
│   │   ├── projects.ts
│   │   ├── quality-gates.ts
│   │   ├── source-code.ts
│   │   └── system.ts
│   ├── index.ts
│   ├── monitoring
│   │   ├── __tests__
│   │   │   └── circuit-breaker.test.ts
│   │   ├── circuit-breaker.ts
│   │   ├── health.ts
│   │   └── metrics.ts
│   ├── schemas
│   │   ├── common.ts
│   │   ├── components.ts
│   │   ├── hotspots-tools.ts
│   │   ├── hotspots.ts
│   │   ├── index.ts
│   │   ├── issues.ts
│   │   ├── measures.ts
│   │   ├── metrics.ts
│   │   ├── projects.ts
│   │   ├── quality-gates.ts
│   │   ├── source-code.ts
│   │   └── system.ts
│   ├── sonarqube.ts
│   ├── transports
│   │   ├── base.ts
│   │   ├── factory.ts
│   │   ├── http.ts
│   │   ├── index.ts
│   │   ├── session-manager.ts
│   │   └── stdio.ts
│   ├── types
│   │   ├── common.ts
│   │   ├── components.ts
│   │   ├── hotspots.ts
│   │   ├── index.ts
│   │   ├── issues.ts
│   │   ├── measures.ts
│   │   ├── metrics.ts
│   │   ├── projects.ts
│   │   ├── quality-gates.ts
│   │   ├── source-code.ts
│   │   └── system.ts
│   └── utils
│       ├── __tests__
│       │   ├── elicitation.test.ts
│       │   ├── pattern-matcher.test.ts
│       │   └── structured-response.test.ts
│       ├── client-factory.ts
│       ├── elicitation.ts
│       ├── error-handler.ts
│       ├── logger.ts
│       ├── parameter-mappers.ts
│       ├── pattern-matcher.ts
│       ├── retry.ts
│       ├── structured-response.ts
│       └── transforms.ts
├── test-http-transport.sh
├── tmp
│   └── .gitkeep
├── tsconfig.build.json
├── tsconfig.json
├── vitest.config.d.ts
├── vitest.config.js
├── vitest.config.js.map
└── vitest.config.ts
```

# Files

--------------------------------------------------------------------------------
/src/__tests__/index.test.ts:
--------------------------------------------------------------------------------

```typescript
import { describe, it, expect, beforeEach, afterEach, beforeAll, vi } from 'vitest';
import nock from 'nock';
import { z } from 'zod';

// Mock process.exit to prevent the test runner from exiting
vi.spyOn(process, 'exit').mockImplementation(() => {
  throw new Error('process.exit called');
});

// Store original env vars
const originalEnv = { ...process.env };
// Mock environment variables
process.env.SONARQUBE_TOKEN = 'test-token';
process.env.SONARQUBE_URL = 'http://localhost:9000';
// Mock SonarQube client responses
beforeAll(() => {
  nock('http://localhost:9000')
    .persist()
    .get('/api/projects/search')
    .query(true)
    .reply(200, {
      projects: [
        {
          key: 'test-project',
          name: 'Test Project',
          qualifier: 'TRK',
          visibility: 'public',
          lastAnalysisDate: '2024-03-01',
          revision: 'abc123',
          managed: false,
        },
      ],
      paging: {
        pageIndex: 1,
        pageSize: 10,
        total: 1,
      },
    });
  nock('http://localhost:9000')
    .persist()
    .get('/api/metrics/search')
    .query(true)
    .reply(200, {
      metrics: [
        {
          key: 'test-metric',
          name: 'Test Metric',
          description: 'Test metric description',
          domain: 'test',
          type: 'INT',
        },
      ],
      paging: {
        pageIndex: 1,
        pageSize: 10,
        total: 1,
      },
    });
  nock('http://localhost:9000')
    .persist()
    .get('/api/issues/search')
    .query(true)
    .reply(200, {
      issues: [
        {
          key: 'test-issue',
          rule: 'test-rule',
          severity: 'MAJOR',
          component: 'test-component',
          project: 'test-project',
          line: 1,
          status: 'OPEN',
          message: 'Test issue',
        },
      ],
      components: [],
      rules: [],
      users: [],
      facets: [],
      paging: {
        pageIndex: 1,
        pageSize: 10,
        total: 1,
      },
    });
  nock('http://localhost:9000').persist().get('/api/system/health').reply(200, {
    health: 'GREEN',
    causes: [],
  });
  nock('http://localhost:9000').persist().get('/api/system/status').reply(200, {
    id: 'test-id',
    version: '10.3.0.82913',
    status: 'UP',
  });
  nock('http://localhost:9000').persist().get('/api/system/ping').reply(200, 'pong');
  // Mock SonarQube measures API responses
  nock('http://localhost:9000')
    .persist()
    .get('/api/measures/component')
    .query(true)
    .reply(200, {
      component: {
        key: 'test-project',
        name: 'Test Project',
        qualifier: 'TRK',
        measures: [
          {
            metric: 'coverage',
            value: '85.4',
          },
          {
            metric: 'bugs',
            value: '12',
          },
        ],
      },
      metrics: [
        {
          key: 'coverage',
          name: 'Coverage',
          description: 'Test coverage percentage',
          domain: 'Coverage',
          type: 'PERCENT',
        },
        {
          key: 'bugs',
          name: 'Bugs',
          description: 'Number of bugs',
          domain: 'Reliability',
          type: 'INT',
        },
      ],
    });
  nock('http://localhost:9000')
    .persist()
    .get('/api/measures/components')
    .query(true)
    .reply(200, {
      components: [
        {
          key: 'test-project-1',
          name: 'Test Project 1',
          qualifier: 'TRK',
          measures: [
            {
              metric: 'coverage',
              value: '85.4',
            },
          ],
        },
        {
          key: 'test-project-2',
          name: 'Test Project 2',
          qualifier: 'TRK',
          measures: [
            {
              metric: 'coverage',
              value: '72.1',
            },
          ],
        },
      ],
      metrics: [
        {
          key: 'coverage',
          name: 'Coverage',
          description: 'Test coverage percentage',
          domain: 'Coverage',
          type: 'PERCENT',
        },
      ],
      paging: {
        pageIndex: 1,
        pageSize: 100,
        total: 2,
      },
    });
  nock('http://localhost:9000')
    .persist()
    .get('/api/measures/search_history')
    .query(true)
    .reply(200, {
      measures: [
        {
          metric: 'coverage',
          history: [
            {
              date: '2023-01-01T00:00:00+0000',
              value: '85.4',
            },
            {
              date: '2023-02-01T00:00:00+0000',
              value: '87.2',
            },
          ],
        },
      ],
      paging: {
        pageIndex: 1,
        pageSize: 100,
        total: 1,
      },
    });
});
afterAll(() => {
  nock.cleanAll();
});
// Mock the handlers
const mockHandlers = {
  handleSonarQubeProjects: (vi.fn() as any).mockResolvedValue({
    content: [
      {
        type: 'text' as const,
        text: JSON.stringify({
          projects: [
            {
              key: 'test-project',
              name: 'Test Project',
              qualifier: 'TRK',
              visibility: 'public',
              lastAnalysisDate: '2024-03-01',
              revision: 'abc123',
              managed: false,
            },
          ],
          paging: {
            pageIndex: 1,
            pageSize: 10,
            total: 1,
          },
        }),
      },
    ],
  }),
  handleSonarQubeGetMetrics: (vi.fn() as any).mockResolvedValue({
    content: [
      {
        type: 'text' as const,
        text: JSON.stringify({
          metrics: [
            {
              key: 'test-metric',
              name: 'Test Metric',
              description: 'Test metric description',
              domain: 'test',
              type: 'INT',
            },
          ],
          paging: {
            pageIndex: 1,
            pageSize: 10,
            total: 1,
          },
        }),
      },
    ],
  }),
  handleSonarQubeGetIssues: (vi.fn() as any).mockResolvedValue({
    content: [
      {
        type: 'text' as const,
        text: JSON.stringify({
          issues: [
            {
              key: 'test-issue',
              rule: 'test-rule',
              severity: 'MAJOR',
              component: 'test-component',
              project: 'test-project',
              line: 1,
              status: 'OPEN',
              message: 'Test issue',
            },
          ],
          components: [],
          rules: [],
          users: [],
          facets: [],
          paging: {
            pageIndex: 1,
            pageSize: 10,
            total: 1,
          },
        }),
      },
    ],
  }),
  handleSonarQubeGetHealth: (vi.fn() as any).mockResolvedValue({
    content: [
      {
        type: 'text' as const,
        text: JSON.stringify({
          health: 'GREEN',
          causes: [],
        }),
      },
    ],
  }),
  handleSonarQubeGetStatus: (vi.fn() as any).mockResolvedValue({
    content: [
      {
        type: 'text' as const,
        text: JSON.stringify({
          id: 'test-id',
          version: '10.3.0.82913',
          status: 'UP',
        }),
      },
    ],
  }),
  handleSonarQubePing: (vi.fn() as any).mockResolvedValue({
    content: [
      {
        type: 'text' as const,
        text: 'pong',
      },
    ],
  }),
  handleSonarQubeComponentMeasures: (vi.fn() as any).mockResolvedValue({
    content: [
      {
        type: 'text' as const,
        text: JSON.stringify({
          component: {
            key: 'test-project',
            name: 'Test Project',
            qualifier: 'TRK',
            measures: [
              {
                metric: 'coverage',
                value: '85.4',
              },
              {
                metric: 'bugs',
                value: '12',
              },
            ],
          },
          metrics: [
            {
              key: 'coverage',
              name: 'Coverage',
              description: 'Test coverage percentage',
              domain: 'Coverage',
              type: 'PERCENT',
            },
            {
              key: 'bugs',
              name: 'Bugs',
              description: 'Number of bugs',
              domain: 'Reliability',
              type: 'INT',
            },
          ],
        }),
      },
    ],
  }),
  handleSonarQubeComponentsMeasures: (vi.fn() as any).mockResolvedValue({
    content: [
      {
        type: 'text' as const,
        text: JSON.stringify({
          components: [
            {
              key: 'test-project-1',
              name: 'Test Project 1',
              qualifier: 'TRK',
              measures: [
                {
                  metric: 'coverage',
                  value: '85.4',
                },
              ],
            },
            {
              key: 'test-project-2',
              name: 'Test Project 2',
              qualifier: 'TRK',
              measures: [
                {
                  metric: 'coverage',
                  value: '72.1',
                },
              ],
            },
          ],
          metrics: [
            {
              key: 'coverage',
              name: 'Coverage',
              description: 'Test coverage percentage',
              domain: 'Coverage',
              type: 'PERCENT',
            },
          ],
          paging: {
            pageIndex: 1,
            pageSize: 100,
            total: 2,
          },
        }),
      },
    ],
  }),
  handleSonarQubeMeasuresHistory: (vi.fn() as any).mockResolvedValue({
    content: [
      {
        type: 'text' as const,
        text: JSON.stringify({
          measures: [
            {
              metric: 'coverage',
              history: [
                {
                  date: '2023-01-01T00:00:00+0000',
                  value: '85.4',
                },
                {
                  date: '2023-02-01T00:00:00+0000',
                  value: '87.2',
                },
              ],
            },
          ],
          paging: {
            pageIndex: 1,
            pageSize: 100,
            total: 1,
          },
        }),
      },
    ],
  }),
};
// Define the mock handlers but don't mock the entire module
vi.mock('../index.js', async () => {
  // Get the original module
  const originalModule = await vi.importActual('../index.js');
  return {
    // Return everything from the original module
    ...originalModule,
    // But override these specific functions for tests that need mocks
    mcpServer: {
      ...(originalModule.mcpServer as Record<string, unknown>),
      connect: vi.fn(),
    },
  };
});
// Save environment variables
// Using the originalEnv declared at the top of the file
let mcpServer: any;
let nullToUndefined: any;
let handleSonarQubeProjects: any;
let mapToSonarQubeParams: any;
let handleSonarQubeGetIssues: any;
let handleSonarQubeGetMetrics: any;
let handleSonarQubeGetHealth: any;
let handleSonarQubeGetStatus: any;
let handleSonarQubePing: any;
let handleSonarQubeComponentMeasures: any;
let handleSonarQubeComponentsMeasures: any;
let handleSonarQubeMeasuresHistory: any;
let handleSonarQubeHotspots: any;
let handleSonarQubeHotspot: any;
let handleSonarQubeUpdateHotspotStatus: any;
let qualityGateHandler: any;
let qualityGateStatusHandler: any;
let hotspotHandler: any;
let updateHotspotStatusHandler: any;
describe('MCP Server', () => {
  beforeAll(async () => {
    const module = await import('../index.js');
    mcpServer = module.mcpServer;
    nullToUndefined = module.nullToUndefined;
    handleSonarQubeProjects = module.handleSonarQubeProjects;
    mapToSonarQubeParams = module.mapToSonarQubeParams;
    handleSonarQubeGetIssues = module.handleSonarQubeGetIssues;
    handleSonarQubeGetMetrics = module.handleSonarQubeGetMetrics;
    handleSonarQubeGetHealth = module.handleSonarQubeGetHealth;
    handleSonarQubeGetStatus = module.handleSonarQubeGetStatus;
    handleSonarQubePing = module.handleSonarQubePing;
    handleSonarQubeComponentMeasures = module.handleSonarQubeComponentMeasures;
    handleSonarQubeComponentsMeasures = module.handleSonarQubeComponentsMeasures;
    handleSonarQubeMeasuresHistory = module.handleSonarQubeMeasuresHistory;
    handleSonarQubeHotspots = module.handleSonarQubeHotspots;
    handleSonarQubeHotspot = module.handleSonarQubeHotspot;
    handleSonarQubeUpdateHotspotStatus = module.handleSonarQubeUpdateHotspotStatus;
    qualityGateHandler = module.qualityGateHandler;
    qualityGateStatusHandler = module.qualityGateStatusHandler;
    hotspotHandler = module.hotspotHandler;
    updateHotspotStatusHandler = module.updateHotspotStatusHandler;
  });
  beforeEach(() => {
    vi.resetModules();
    process.env = { ...originalEnv };
    // Ensure test environment variables are set
    process.env.SONARQUBE_TOKEN = 'test-token';
    process.env.SONARQUBE_URL = 'http://localhost:9000';
    nock.cleanAll();
  });
  afterEach(() => {
    process.env = originalEnv;
    vi.restoreAllMocks();
    nock.cleanAll();
  });
  it('should have initialized the MCP server', () => {
    expect(mcpServer).toBeDefined();
    expect(mcpServer.server).toBeDefined();
  });
  describe('Tool registration', () => {
    let testServer: any;
    let registeredTools: Map<string, any>;
    beforeEach(() => {
      registeredTools = new Map();
      testServer = {
        tool: vi.fn((name: string, description: string, schema: any, handler: any) => {
          registeredTools.set(name, { description, schema, handler });
        }),
      };
      // Register tools
      testServer.tool(
        'projects',
        'List all SonarQube projects',
        { page: {}, page_size: {} },
        mockHandlers.handleSonarQubeProjects
      );
      testServer.tool(
        'metrics',
        'Get available metrics from SonarQube',
        { page: {}, page_size: {} },
        mockHandlers.handleSonarQubeGetMetrics
      );
      testServer.tool(
        'issues',
        'Get issues for a SonarQube project',
        {
          project_key: {},
          severity: {},
          page: {},
          page_size: {},
          statuses: {},
          resolutions: {},
          resolved: {},
          types: {},
          rules: {},
          tags: {},
        },
        mockHandlers.handleSonarQubeGetIssues
      );
      testServer.tool(
        'system_health',
        'Get the health status of the SonarQube instance',
        {},
        mockHandlers.handleSonarQubeGetHealth
      );
      testServer.tool(
        'system_status',
        'Get the status of the SonarQube instance',
        {},
        mockHandlers.handleSonarQubeGetStatus
      );
      testServer.tool(
        'system_ping',
        'Ping the SonarQube instance to check if it is up',
        {},
        mockHandlers.handleSonarQubePing
      );
      testServer.tool(
        'measures_component',
        'Get measures for a specific component',
        {
          component: {},
          metric_keys: {},
          additional_fields: {},
          branch: {},
          pull_request: {},
          period: {},
        },
        mockHandlers.handleSonarQubeComponentMeasures
      );
      testServer.tool(
        'measures_components',
        'Get measures for multiple components',
        {
          component_keys: {},
          metric_keys: {},
          additional_fields: {},
          branch: {},
          pull_request: {},
          period: {},
          page: {},
          page_size: {},
        },
        mockHandlers.handleSonarQubeComponentsMeasures
      );
      testServer.tool(
        'measures_history',
        'Get measures history for a component',
        {
          component: {},
          metrics: {},
          from: {},
          to: {},
          branch: {},
          pull_request: {},
          page: {},
          page_size: {},
        },
        mockHandlers.handleSonarQubeMeasuresHistory
      );
    });
    it('should register all required tools', () => {
      expect(registeredTools.size).toBe(9);
      expect(registeredTools.has('projects')).toBe(true);
      expect(registeredTools.has('metrics')).toBe(true);
      expect(registeredTools.has('issues')).toBe(true);
      expect(registeredTools.has('system_health')).toBe(true);
      expect(registeredTools.has('system_status')).toBe(true);
      expect(registeredTools.has('system_ping')).toBe(true);
      expect(registeredTools.has('measures_component')).toBe(true);
      expect(registeredTools.has('measures_components')).toBe(true);
      expect(registeredTools.has('measures_history')).toBe(true);
    });
    it('should register tools with correct descriptions', () => {
      expect(registeredTools.get('projects').description).toBe('List all SonarQube projects');
      expect(registeredTools.get('metrics').description).toBe(
        'Get available metrics from SonarQube'
      );
      expect(registeredTools.get('issues').description).toBe('Get issues for a SonarQube project');
      expect(registeredTools.get('system_health').description).toBe(
        'Get the health status of the SonarQube instance'
      );
      expect(registeredTools.get('system_status').description).toBe(
        'Get the status of the SonarQube instance'
      );
      expect(registeredTools.get('system_ping').description).toBe(
        'Ping the SonarQube instance to check if it is up'
      );
      expect(registeredTools.get('measures_component').description).toBe(
        'Get measures for a specific component'
      );
      expect(registeredTools.get('measures_components').description).toBe(
        'Get measures for multiple components'
      );
      expect(registeredTools.get('measures_history').description).toBe(
        'Get measures history for a component'
      );
    });
    it('should register tools with correct handlers', () => {
      expect(registeredTools.get('projects').handler).toBe(mockHandlers.handleSonarQubeProjects);
      expect(registeredTools.get('metrics').handler).toBe(mockHandlers.handleSonarQubeGetMetrics);
      expect(registeredTools.get('issues').handler).toBe(mockHandlers.handleSonarQubeGetIssues);
      expect(registeredTools.get('system_health').handler).toBe(
        mockHandlers.handleSonarQubeGetHealth
      );
      expect(registeredTools.get('system_status').handler).toBe(
        mockHandlers.handleSonarQubeGetStatus
      );
      expect(registeredTools.get('system_ping').handler).toBe(mockHandlers.handleSonarQubePing);
      expect(registeredTools.get('measures_component').handler).toBe(
        mockHandlers.handleSonarQubeComponentMeasures
      );
      expect(registeredTools.get('measures_components').handler).toBe(
        mockHandlers.handleSonarQubeComponentsMeasures
      );
      expect(registeredTools.get('measures_history').handler).toBe(
        mockHandlers.handleSonarQubeMeasuresHistory
      );
    });
  });
  describe('nullToUndefined', () => {
    it('should return undefined for null', () => {
      expect(nullToUndefined(null)).toBeUndefined();
    });
    it('should return the value for non-null', () => {
      expect(nullToUndefined('value')).toBe('value');
    });
  });
  describe('handleSonarQubeProjects', () => {
    it('should fetch and return a list of projects', async () => {
      nock('http://localhost:9000')
        .get('/api/projects/search')
        .query(true)
        .reply(200, {
          components: [
            {
              key: 'project1',
              name: 'Project 1',
              qualifier: 'TRK',
              visibility: 'public',
              lastAnalysisDate: '2024-03-01',
              revision: 'abc123',
              managed: false,
            },
          ],
          paging: { pageIndex: 1, pageSize: 1, total: 1 },
        });
      const response = await handleSonarQubeProjects({ page: 1, page_size: 1 });
      expect(response.content[0].text).toContain('project1');
    });
  });
  describe('mapToSonarQubeParams', () => {
    it('should map MCP tool parameters to SonarQube client parameters', () => {
      const params = mapToSonarQubeParams({ project_key: 'key', severity: 'MAJOR' });
      expect(params.projectKey).toBe('key');
      expect(params.severity).toBe('MAJOR');
    });
  });
  describe('handleSonarQubeGetIssues', () => {
    it('should fetch and return a list of issues', async () => {
      nock('http://localhost:9000')
        .get('/api/issues/search')
        .query(true)
        .reply(200, {
          issues: [
            {
              key: 'issue1',
              rule: 'rule1',
              severity: 'MAJOR',
              component: 'comp1',
              project: 'proj1',
              line: 1,
              status: 'OPEN',
              message: 'Test issue',
            },
          ],
          components: [],
          rules: [],
          paging: { pageIndex: 1, pageSize: 1, total: 1 },
        });
      const response = await handleSonarQubeGetIssues({ projectKey: 'key' });
      expect(response.content[0].text).toContain('issue');
    });
  });
  describe('handleSonarQubeGetMetrics', () => {
    it('should fetch and return a list of metrics', async () => {
      nock('http://localhost:9000')
        .get('/api/metrics/search')
        .query(true)
        .reply(200, {
          metrics: [
            {
              key: 'metric1',
              name: 'Metric 1',
              description: 'Test metric',
              domain: 'domain1',
              type: 'INT',
            },
          ],
          paging: { pageIndex: 1, pageSize: 1, total: 1 },
        });
      const response = await handleSonarQubeGetMetrics({ page: 1, pageSize: 1 });
      expect(response.content[0].text).toContain('metric');
    });
  });
  describe('handleSonarQubeGetHealth', () => {
    it('should fetch and return health status', async () => {
      nock('http://localhost:9000').get('/api/v2/system/health').reply(200, {
        status: 'GREEN',
        checkedAt: '2023-12-01T10:00:00Z',
      });
      const response = await handleSonarQubeGetHealth();
      expect(response.content[0].text).toContain('GREEN');
    });
  });
  describe('handleSonarQubeGetStatus', () => {
    it('should fetch and return system status', async () => {
      nock('http://localhost:9000').get('/api/system/status').reply(200, {
        id: 'test-id',
        version: '10.3.0.82913',
        status: 'UP',
      });
      const response = await handleSonarQubeGetStatus();
      expect(response.content[0].text).toContain('UP');
    });
  });
  describe('handleSonarQubePing', () => {
    it('should ping the system and return the result', async () => {
      nock('http://localhost:9000').get('/api/system/ping').reply(200, 'pong');
      const response = await handleSonarQubePing();
      expect(response.content[0].text).toBe('pong');
    });
  });
  describe('Conditional server start', () => {
    it('should not start the server if NODE_ENV is test', () => {
      process.env.NODE_ENV = 'test';
      const mcpConnectSpy = vi.spyOn(mcpServer, 'connect');
      // Since the server doesn't start in test mode, we verify that connect is not called
      expect(mcpConnectSpy).not.toHaveBeenCalled();
      mcpConnectSpy.mockRestore();
    });
    it('should use transport factory in production mode', () => {
      // Test that our transport factory is used (covered by integration)
      // The actual server startup is tested manually or in integration tests
      // since we can't easily test the module-level code execution
      expect(true).toBe(true);
    });
  });
  describe('Schema transformations', () => {
    it('should handle page and page_size transformations correctly', () => {
      // Use the actual schema from the tool registration
      const pageSchema = z
        .string()
        .optional()
        .transform((val: any) => (val ? parseInt(val, 10) || null : null));
      // Test valid number strings
      expect(pageSchema.parse('10')).toBe(10);
      expect(pageSchema.parse('20')).toBe(20);
      // Test invalid number strings
      expect(pageSchema.parse('invalid')).toBe(null);
      expect(pageSchema.parse('not-a-number')).toBe(null);
      // Test empty/undefined values
      expect(pageSchema.parse(undefined)).toBe(null);
      expect(pageSchema.parse('')).toBe(null);
    });
    it('should handle boolean transformations correctly', () => {
      const booleanSchema = z
        .union([z.boolean(), z.string().transform((val: any) => val === 'true')])
        .nullable()
        .optional();
      // Test string values
      expect(booleanSchema.parse('true')).toBe(true);
      expect(booleanSchema.parse('false')).toBe(false);
      // Test boolean values
      expect(booleanSchema.parse(true)).toBe(true);
      expect(booleanSchema.parse(false)).toBe(false);
      // Test null/undefined values
      expect(booleanSchema.parse(null)).toBe(null);
      expect(booleanSchema.parse(undefined)).toBe(undefined);
    });
    it('should handle array transformations correctly', () => {
      const stringArraySchema = z.array(z.string()).nullable().optional();
      const statusSchema = z
        .array(
          z.enum([
            'OPEN',
            'CONFIRMED',
            'REOPENED',
            'RESOLVED',
            'CLOSED',
            'TO_REVIEW',
            'IN_REVIEW',
            'REVIEWED',
          ])
        )
        .nullable()
        .optional();
      const resolutionSchema = z
        .array(z.enum(['FALSE-POSITIVE', 'WONTFIX', 'FIXED', 'REMOVED']))
        .nullable()
        .optional();
      const typeSchema = z
        .array(z.enum(['CODE_SMELL', 'BUG', 'VULNERABILITY', 'SECURITY_HOTSPOT']))
        .nullable()
        .optional();
      // Test valid arrays
      expect(statusSchema.parse(['OPEN', 'CONFIRMED'])).toEqual(['OPEN', 'CONFIRMED']);
      expect(resolutionSchema.parse(['FALSE-POSITIVE', 'WONTFIX'])).toEqual([
        'FALSE-POSITIVE',
        'WONTFIX',
      ]);
      expect(typeSchema.parse(['CODE_SMELL', 'BUG'])).toEqual(['CODE_SMELL', 'BUG']);
      expect(stringArraySchema.parse(['value1', 'value2'])).toEqual(['value1', 'value2']);
      // Test null/undefined values
      expect(statusSchema.parse(null)).toBe(null);
      expect(resolutionSchema.parse(null)).toBe(null);
      expect(typeSchema.parse(null)).toBe(null);
      expect(stringArraySchema.parse(null)).toBe(null);
      expect(statusSchema.parse(undefined)).toBe(undefined);
      expect(resolutionSchema.parse(undefined)).toBe(undefined);
      expect(typeSchema.parse(undefined)).toBe(undefined);
      expect(stringArraySchema.parse(undefined)).toBe(undefined);
      // Test invalid values
      expect(() => statusSchema.parse(['INVALID'])).toThrow();
      expect(() => resolutionSchema.parse(['INVALID'])).toThrow();
      expect(() => typeSchema.parse(['INVALID'])).toThrow();
    });
    it('should handle severity schema correctly', () => {
      const severitySchema = z
        .enum(['INFO', 'MINOR', 'MAJOR', 'CRITICAL', 'BLOCKER'])
        .nullable()
        .optional();
      // Test valid values
      expect(severitySchema.parse('INFO')).toBe('INFO');
      expect(severitySchema.parse('MINOR')).toBe('MINOR');
      expect(severitySchema.parse('MAJOR')).toBe('MAJOR');
      expect(severitySchema.parse('CRITICAL')).toBe('CRITICAL');
      expect(severitySchema.parse('BLOCKER')).toBe('BLOCKER');
      // Test null/undefined values
      expect(severitySchema.parse(null)).toBe(null);
      expect(severitySchema.parse(undefined)).toBe(undefined);
      // Test invalid values
      expect(() => severitySchema.parse('INVALID')).toThrow();
    });
    it('should handle date parameters correctly', () => {
      const dateSchema = z.string().nullable().optional();
      // Test valid dates
      expect(dateSchema.parse('2024-01-01')).toBe('2024-01-01');
      expect(dateSchema.parse('2024-12-31')).toBe('2024-12-31');
      // Test null/undefined values
      expect(dateSchema.parse(null)).toBe(null);
      expect(dateSchema.parse(undefined)).toBe(undefined);
    });
    it('should handle hotspot search boolean transformations correctly', () => {
      // Test string to boolean transformation schemas used in hotspot search
      const hotspotBooleanSchema = z
        .union([z.boolean(), z.string().transform((val: any) => val === 'true')])
        .nullable()
        .optional();
      // Test boolean values
      expect(hotspotBooleanSchema.parse(true)).toBe(true);
      expect(hotspotBooleanSchema.parse(false)).toBe(false);
      // Test string values
      expect(hotspotBooleanSchema.parse('true')).toBe(true);
      expect(hotspotBooleanSchema.parse('false')).toBe(false);
      expect(hotspotBooleanSchema.parse('any')).toBe(false);
      // Test null/undefined values
      expect(hotspotBooleanSchema.parse(null)).toBe(null);
      expect(hotspotBooleanSchema.parse(undefined)).toBe(undefined);
    });
    it('should handle complex parameter combinations', () => {
      // Mock SonarQube API response
      nock('http://localhost:9000')
        .get('/api/issues/search')
        .query(true)
        .reply(200, {
          issues: [],
          components: [],
          rules: [],
          paging: { pageIndex: 1, pageSize: 100, total: 0 },
        });
      const params = {
        project_key: 'test-project',
        severity: 'MAJOR',
        statuses: ['OPEN', 'CONFIRMED'],
        resolutions: ['FALSE-POSITIVE', 'WONTFIX'],
        types: ['CODE_SMELL', 'BUG'],
        rules: ['rule1', 'rule2'],
        tags: ['tag1', 'tag2'],
        created_after: '2024-01-01',
        created_before: '2024-12-31',
        created_at: '2024-06-15',
        created_in_last: '7d',
        assignees: ['user1', 'user2'],
        authors: ['author1', 'author2'],
        cwe: ['cwe1', 'cwe2'],
        languages: ['java', 'typescript'],
        owasp_top10: ['a1', 'a2'],
        sans_top25: ['sans1', 'sans2'],
        sonarsource_security: ['sec1', 'sec2'],
        on_component_only: true,
        facets: ['facet1', 'facet2'],
        since_leak_period: true,
        in_new_code_period: true,
      };
      // Verify parameters are properly mapped
      const mappedParams = mapToSonarQubeParams(params);
      expect(mappedParams).toBeDefined();
      expect(mappedParams.projectKey).toBe('test-project');
    });
  });
  describe('Tool handlers', () => {
    beforeEach(() => {
      vi.resetAllMocks();
    });
    describe('handleSonarQubeComponentMeasures', () => {
      it('should fetch and return component measures', async () => {
        nock('http://localhost:9000')
          .get('/api/measures/component')
          .query(true)
          .reply(200, {
            component: {
              key: 'test-component',
              name: 'Test Component',
              qualifier: 'TRK',
              measures: [
                {
                  metric: 'coverage',
                  value: '85.4',
                },
              ],
            },
            metrics: [
              {
                key: 'coverage',
                name: 'Coverage',
                description: 'Test coverage',
                domain: 'Coverage',
                type: 'PERCENT',
              },
            ],
          });
        const response = await handleSonarQubeComponentMeasures({
          component: 'test-component',
          metricKeys: ['coverage'],
        });
        expect(response.content[0].text).toContain('test-component');
        expect(response.content[0].text).toContain('coverage');
        expect(response.content[0].text).toContain('85.4');
      });
      it('should fetch component measures with all optional parameters', async () => {
        nock('http://localhost:9000')
          .get('/api/measures/component')
          .query((queryObject) => {
            return (
              queryObject.component === 'test-component' &&
              queryObject.metricKeys === 'coverage,bugs' &&
              queryObject.additionalFields === 'periods,metrics' &&
              queryObject.branch === 'main' &&
              queryObject.pullRequest === 'pr-123'
            );
          })
          .matchHeader('authorization', 'Bearer test-token')
          .reply(200, {
            component: {
              key: 'test-component',
              name: 'Test Component',
              qualifier: 'TRK',
              measures: [
                {
                  metric: 'coverage',
                  value: '85.4',
                  period: { index: 1, value: '+5.4' },
                },
                {
                  metric: 'bugs',
                  value: '10',
                  period: { index: 1, value: '-2' },
                },
              ],
              periods: [{ index: 1, mode: 'previous_version', date: '2023-01-01T00:00:00+0000' }],
            },
            metrics: [
              {
                key: 'coverage',
                name: 'Coverage',
                description: 'Test coverage',
                domain: 'Coverage',
                type: 'PERCENT',
              },
              {
                key: 'bugs',
                name: 'Bugs',
                description: 'Number of bugs',
                domain: 'Reliability',
                type: 'INT',
              },
            ],
          });
        const response = await handleSonarQubeComponentMeasures({
          component: 'test-component',
          metricKeys: ['coverage', 'bugs'],
          additionalFields: ['periods', 'metrics'],
          branch: 'main',
          pullRequest: 'pr-123',
          period: '1',
        });
        const result = JSON.parse(response.content[0].text);
        expect(result.component.key).toBe('test-component');
        expect(result.component.measures).toHaveLength(2);
        expect(result.component.periods).toBeDefined();
        expect(result.metrics).toHaveLength(2);
        expect(result.component.measures[0].period).toBeDefined();
        expect(result.component.measures[0].period.index).toBe(1);
      });
    });
    describe('handleSonarQubeComponentsMeasures', () => {
      it('should fetch and return measures for multiple components', async () => {
        // Mock individual component measure calls
        nock('http://localhost:9000')
          .get('/api/measures/component')
          .query({
            component: 'test-component-1',
            metricKeys: 'bugs',
          })
          .matchHeader('authorization', 'Bearer test-token')
          .reply(200, {
            component: {
              key: 'test-component-1',
              name: 'Test Component 1',
              qualifier: 'TRK',
              measures: [
                {
                  metric: 'bugs',
                  value: '10',
                },
              ],
            },
            metrics: [
              {
                key: 'bugs',
                name: 'Bugs',
                description: 'Number of bugs',
                domain: 'Reliability',
                type: 'INT',
              },
            ],
          });
        nock('http://localhost:9000')
          .get('/api/measures/component')
          .query({
            component: 'test-component-2',
            metricKeys: 'bugs',
          })
          .matchHeader('authorization', 'Bearer test-token')
          .reply(200, {
            component: {
              key: 'test-component-2',
              name: 'Test Component 2',
              qualifier: 'TRK',
              measures: [
                {
                  metric: 'bugs',
                  value: '5',
                },
              ],
            },
            metrics: [
              {
                key: 'bugs',
                name: 'Bugs',
                description: 'Number of bugs',
                domain: 'Reliability',
                type: 'INT',
              },
            ],
          });
        // Mock the additional call to get metrics from first component
        nock('http://localhost:9000')
          .get('/api/measures/component')
          .query({
            component: 'test-component-1',
            metricKeys: 'bugs',
          })
          .matchHeader('authorization', 'Bearer test-token')
          .reply(200, {
            component: {
              key: 'test-component-1',
              name: 'Test Component 1',
              qualifier: 'TRK',
              measures: [
                {
                  metric: 'bugs',
                  value: '10',
                },
              ],
            },
            metrics: [
              {
                key: 'bugs',
                name: 'Bugs',
                description: 'Number of bugs',
                domain: 'Reliability',
                type: 'INT',
              },
            ],
          });
        const response = await handleSonarQubeComponentsMeasures({
          componentKeys: ['test-component-1', 'test-component-2'],
          metricKeys: ['bugs'],
          page: 1,
          pageSize: 100,
        });
        expect(response.content[0].text).toContain('test-component-1');
        expect(response.content[0].text).toContain('test-component-2');
        expect(response.content[0].text).toContain('bugs');
      });
      it('should fetch components measures with all optional parameters', async () => {
        // Mock individual component measure calls with optional parameters
        nock('http://localhost:9000')
          .get('/api/measures/component')
          .query({
            component: 'test-component-1',
            metricKeys: 'coverage,bugs',
            additionalFields: 'periods,metrics',
            branch: 'develop',
            pullRequest: 'pr-456',
          })
          .matchHeader('authorization', 'Bearer test-token')
          .reply(200, {
            component: {
              key: 'test-component-1',
              name: 'Test Component 1',
              qualifier: 'TRK',
              measures: [
                {
                  metric: 'coverage',
                  value: '85.4',
                  period: { index: 2, value: '+5.4' },
                },
                {
                  metric: 'bugs',
                  value: '10',
                  period: { index: 2, value: '-2' },
                },
              ],
              periods: [{ index: 2, mode: 'previous_version', date: '2023-01-01T00:00:00+0000' }],
            },
            metrics: [
              {
                key: 'coverage',
                name: 'Coverage',
                description: 'Test coverage',
                domain: 'Coverage',
                type: 'PERCENT',
              },
              {
                key: 'bugs',
                name: 'Bugs',
                description: 'Number of bugs',
                domain: 'Reliability',
                type: 'INT',
              },
            ],
            paging: {
              pageIndex: 3,
              pageSize: 25,
              total: 50,
            },
          });
        // Mock the additional call to get metrics from first component
        nock('http://localhost:9000')
          .get('/api/measures/component')
          .query({
            component: 'test-component-1',
            metricKeys: 'coverage,bugs',
            additionalFields: 'periods,metrics',
            branch: 'develop',
            pullRequest: 'pr-456',
          })
          .matchHeader('authorization', 'Bearer test-token')
          .reply(200, {
            component: {
              key: 'test-component-1',
              name: 'Test Component 1',
              qualifier: 'TRK',
              measures: [
                {
                  metric: 'coverage',
                  value: '85.4',
                  period: { index: 2, value: '+5.4' },
                },
                {
                  metric: 'bugs',
                  value: '10',
                  period: { index: 2, value: '-2' },
                },
              ],
              periods: [{ index: 2, mode: 'previous_version', date: '2023-01-01T00:00:00+0000' }],
            },
            metrics: [
              {
                key: 'coverage',
                name: 'Coverage',
                description: 'Test coverage',
                domain: 'Coverage',
                type: 'PERCENT',
              },
              {
                key: 'bugs',
                name: 'Bugs',
                description: 'Number of bugs',
                domain: 'Reliability',
                type: 'INT',
              },
            ],
            period: {
              index: 2,
              mode: 'previous_version',
              date: '2023-01-01T00:00:00+0000',
            },
          });
        // Mock second component
        nock('http://localhost:9000')
          .get('/api/measures/component')
          .query({
            component: 'test-component-2',
            metricKeys: 'coverage,bugs',
            additionalFields: 'periods,metrics',
            branch: 'develop',
            pullRequest: 'pr-456',
          })
          .matchHeader('authorization', 'Bearer test-token')
          .reply(200, {
            component: {
              key: 'test-component-2',
              name: 'Test Component 2',
              qualifier: 'TRK',
              measures: [
                {
                  metric: 'coverage',
                  value: '78.2',
                  period: { index: 2, value: '+3.1' },
                },
                {
                  metric: 'bugs',
                  value: '5',
                  period: { index: 2, value: '-1' },
                },
              ],
              periods: [{ index: 2, mode: 'previous_version', date: '2023-01-01T00:00:00+0000' }],
            },
            metrics: [
              {
                key: 'coverage',
                name: 'Coverage',
                description: 'Test coverage',
                domain: 'Coverage',
                type: 'PERCENT',
              },
              {
                key: 'bugs',
                name: 'Bugs',
                description: 'Number of bugs',
                domain: 'Reliability',
                type: 'INT',
              },
            ],
            period: {
              index: 2,
              mode: 'previous_version',
              date: '2023-01-01T00:00:00+0000',
            },
          });
        const response = await handleSonarQubeComponentsMeasures({
          componentKeys: ['test-component-1', 'test-component-2'],
          metricKeys: ['coverage', 'bugs'],
          additionalFields: ['periods', 'metrics'],
          branch: 'develop',
          pullRequest: 'pr-456',
          period: '2',
          page: 1,
          pageSize: 25,
        });
        const result = JSON.parse(response.content[0].text);
        expect(result.components).toHaveLength(2);
        expect(result.metrics).toHaveLength(2);
        expect(result.paging.pageIndex).toBe(1);
        expect(result.paging.pageSize).toBe(25);
        expect(result.paging.total).toBe(2);
        expect(result.components[0].key).toBe('test-component-1');
        expect(result.components[0].measures).toHaveLength(2);
        expect(result.components[0].periods).toBeDefined();
        expect(result.components[0].measures[0].period).toBeDefined();
        expect(result.components[0].measures[0].period.index).toBe(2);
      });
    });
    describe('handleSonarQubeMeasuresHistory', () => {
      it('should fetch and return measures history', async () => {
        nock('http://localhost:9000')
          .get('/api/measures/search_history')
          .query(true)
          .reply(200, {
            measures: [
              {
                metric: 'coverage',
                history: [
                  {
                    date: '2023-01-01T00:00:00+0000',
                    value: '80.0',
                  },
                  {
                    date: '2023-02-01T00:00:00+0000',
                    value: '85.0',
                  },
                ],
              },
            ],
            paging: {
              pageIndex: 1,
              pageSize: 100,
              total: 1,
            },
          });
        const response = await handleSonarQubeMeasuresHistory({
          component: 'test-component',
          metrics: ['coverage'],
          from: '2023-01-01',
          to: '2023-02-01',
        });
        expect(response.content[0].text).toContain('coverage');
        expect(response.content[0].text).toContain('history');
        expect(response.content[0].text).toContain('2023-01-01');
        expect(response.content[0].text).toContain('2023-02-01');
      });
      it('should fetch measures history with all optional parameters', async () => {
        nock('http://localhost:9000')
          .get('/api/measures/search_history')
          .query((queryObject) => {
            return (
              queryObject.component === 'test-component' &&
              queryObject.metrics === 'coverage,bugs,code_smells' &&
              queryObject.from === '2023-01-01' &&
              queryObject.to === '2023-12-31' &&
              queryObject.branch === 'release' &&
              queryObject.pullRequest === 'pr-789' &&
              queryObject.ps === '30' &&
              queryObject.p === '2'
            );
          })
          .reply(200, {
            measures: [
              {
                metric: 'coverage',
                history: [
                  {
                    date: '2023-01-01T00:00:00+0000',
                    value: '80.0',
                  },
                  {
                    date: '2023-03-01T00:00:00+0000',
                    value: '83.5',
                  },
                  {
                    date: '2023-06-01T00:00:00+0000',
                    value: '85.0',
                  },
                  {
                    date: '2023-09-01T00:00:00+0000',
                    value: '87.2',
                  },
                  {
                    date: '2023-12-01T00:00:00+0000',
                    value: '90.1',
                  },
                ],
              },
              {
                metric: 'bugs',
                history: [
                  {
                    date: '2023-01-01T00:00:00+0000',
                    value: '15',
                  },
                  {
                    date: '2023-03-01T00:00:00+0000',
                    value: '12',
                  },
                  {
                    date: '2023-06-01T00:00:00+0000',
                    value: '10',
                  },
                  {
                    date: '2023-09-01T00:00:00+0000',
                    value: '7',
                  },
                  {
                    date: '2023-12-01T00:00:00+0000',
                    value: '5',
                  },
                ],
              },
              {
                metric: 'code_smells',
                history: [
                  {
                    date: '2023-01-01T00:00:00+0000',
                    value: '50',
                  },
                  {
                    date: '2023-03-01T00:00:00+0000',
                    value: '45',
                  },
                  {
                    date: '2023-06-01T00:00:00+0000',
                    value: '40',
                  },
                  {
                    date: '2023-09-01T00:00:00+0000',
                    value: '35',
                  },
                  {
                    date: '2023-12-01T00:00:00+0000',
                    value: '30',
                  },
                ],
              },
            ],
            paging: {
              pageIndex: 2,
              pageSize: 30,
              total: 60,
            },
          });
        const response = await handleSonarQubeMeasuresHistory({
          component: 'test-component',
          metrics: ['coverage', 'bugs', 'code_smells'],
          from: '2023-01-01',
          to: '2023-12-31',
          branch: 'release',
          pullRequest: 'pr-789',
          page: 2,
          pageSize: 30,
        });
        const result = JSON.parse(response.content[0].text);
        expect(result.measures).toHaveLength(3);
        expect(result.paging.pageIndex).toBe(2);
        expect(result.paging.pageSize).toBe(30);
        expect(result.paging.total).toBe(60);
        // Check coverage metric
        expect(result.measures[0].metric).toBe('coverage');
        expect(result.measures[0].history).toHaveLength(5);
        expect(result.measures[0].history[0].date).toBe('2023-01-01T00:00:00+0000');
        expect(result.measures[0].history[0].value).toBe('80.0');
        expect(result.measures[0].history[4].date).toBe('2023-12-01T00:00:00+0000');
        expect(result.measures[0].history[4].value).toBe('90.1');
        // Check bugs metric
        expect(result.measures[1].metric).toBe('bugs');
        expect(result.measures[1].history).toHaveLength(5);
        expect(result.measures[1].history[0].value).toBe('15');
        expect(result.measures[1].history[4].value).toBe('5');
        // Check code_smells metric
        expect(result.measures[2].metric).toBe('code_smells');
        expect(result.measures[2].history).toHaveLength(5);
        expect(result.measures[2].history[0].value).toBe('50');
        expect(result.measures[2].history[4].value).toBe('30');
      });
    });
    describe('measures_component tool lambda', () => {
      it('should call handleSonarQubeComponentMeasures with correct parameters', async () => {
        // Create a simulated lambda function that mimics the tool handler
        const componentMeasuresLambda = async (params: Record<string, unknown>) => {
          return await handleSonarQubeComponentMeasures({
            component: params.component as string,
            metricKeys: Array.isArray(params.metric_keys)
              ? (params.metric_keys as string[])
              : [params.metric_keys as string],
            additionalFields: params.additional_fields as string[] | undefined,
            branch: params.branch as string | undefined,
            pullRequest: params.pull_request as string | undefined,
            period: params.period as string | undefined,
          });
        };
        // Mock the handleSonarQubeComponentMeasures function
        const mockHandler = (vi.fn() as any).mockResolvedValue({
          content: [{ type: 'text', text: '{"component":{}}' }],
        });
        const originalHandler = handleSonarQubeComponentMeasures;
        handleSonarQubeComponentMeasures = mockHandler;
        // Test with string metrics parameter
        await componentMeasuresLambda({
          component: 'my-project',
          metric_keys: 'coverage',
          branch: 'main',
        });
        // Test with array metrics parameter
        await componentMeasuresLambda({
          component: 'my-project',
          metric_keys: ['coverage', 'bugs'],
          additional_fields: ['periods'],
          pull_request: 'pr-123',
          period: '1',
        });
        // Check that the handler was called with the correct parameters
        expect(mockHandler).toHaveBeenCalledTimes(2);
        // Check first call with string parameter
        expect(mockHandler.mock.calls[0][0]).toEqual({
          component: 'my-project',
          metricKeys: ['coverage'],
          branch: 'main',
          additionalFields: undefined,
          pullRequest: undefined,
          period: undefined,
        });
        // Check second call with array parameter
        expect(mockHandler.mock.calls[1][0]).toEqual({
          component: 'my-project',
          metricKeys: ['coverage', 'bugs'],
          additionalFields: ['periods'],
          branch: undefined,
          pullRequest: 'pr-123',
          period: '1',
        });
        // Restore the original handler
        handleSonarQubeComponentMeasures = originalHandler;
      });
    });
    describe('measures_components tool lambda', () => {
      it('should call handleSonarQubeComponentsMeasures with correct parameters', async () => {
        // Create a simulated lambda function that mimics the tool handler
        const componentsMeasuresLambda = async (params: Record<string, unknown>) => {
          return await handleSonarQubeComponentsMeasures({
            componentKeys: Array.isArray(params.component_keys)
              ? (params.component_keys as string[])
              : [params.component_keys as string],
            metricKeys: Array.isArray(params.metric_keys)
              ? (params.metric_keys as string[])
              : [params.metric_keys as string],
            additionalFields: params.additional_fields as string[] | undefined,
            branch: params.branch as string | undefined,
            pullRequest: params.pull_request as string | undefined,
            period: params.period as string | undefined,
            page: nullToUndefined(params.page) as number | undefined,
            pageSize: nullToUndefined(params.page_size) as number | undefined,
          });
        };
        // Mock the handler function
        const mockHandler = (vi.fn() as any).mockResolvedValue({
          content: [{ type: 'text', text: '{"components":[]}' }],
        });
        const originalHandler = handleSonarQubeComponentsMeasures;
        handleSonarQubeComponentsMeasures = mockHandler;
        // Test with string parameters
        await componentsMeasuresLambda({
          component_keys: 'project1',
          metric_keys: 'coverage',
          page: '1',
          page_size: '10',
        });
        // Test with array parameters
        await componentsMeasuresLambda({
          component_keys: ['project1', 'project2'],
          metric_keys: ['coverage', 'bugs'],
          additional_fields: ['periods'],
          branch: 'main',
          period: '1',
        });
        // Test with pull request parameter
        await componentsMeasuresLambda({
          component_keys: 'project1',
          metric_keys: ['coverage', 'bugs'],
          pull_request: 'pr-123',
        });
        // Check that the handler was called with the correct parameters
        expect(mockHandler).toHaveBeenCalledTimes(3);
        // Check first call with string parameters
        expect(mockHandler.mock.calls[0][0]).toEqual({
          componentKeys: ['project1'],
          metricKeys: ['coverage'],
          additionalFields: undefined,
          branch: undefined,
          pullRequest: undefined,
          period: undefined,
          page: '1',
          pageSize: '10',
        });
        // Check second call with array parameters
        expect(mockHandler.mock.calls[1][0]).toEqual({
          componentKeys: ['project1', 'project2'],
          metricKeys: ['coverage', 'bugs'],
          additionalFields: ['periods'],
          branch: 'main',
          pullRequest: undefined,
          period: '1',
          page: undefined,
          pageSize: undefined,
        });
        // Check third call with pull request parameter
        expect(mockHandler.mock.calls[2][0]).toEqual({
          componentKeys: ['project1'],
          metricKeys: ['coverage', 'bugs'],
          additionalFields: undefined,
          branch: undefined,
          pullRequest: 'pr-123',
          period: undefined,
          page: undefined,
          pageSize: undefined,
        });
        // Restore the original handler
        handleSonarQubeComponentsMeasures = originalHandler;
      });
    });
    describe('measures_history tool lambda', () => {
      it('should call handleSonarQubeMeasuresHistory with correct parameters', async () => {
        // Create a simulated lambda function that mimics the tool handler
        const measuresHistoryLambda = async (params: Record<string, unknown>) => {
          return await handleSonarQubeMeasuresHistory({
            component: params.component as string,
            metrics: Array.isArray(params.metrics)
              ? (params.metrics as string[])
              : [params.metrics as string],
            from: params.from as string | undefined,
            to: params.to as string | undefined,
            branch: params.branch as string | undefined,
            pullRequest: params.pull_request as string | undefined,
            page: nullToUndefined(params.page) as number | undefined,
            pageSize: nullToUndefined(params.page_size) as number | undefined,
          });
        };
        // Mock the handler function
        const mockHandler = (vi.fn() as any).mockResolvedValue({
          content: [{ type: 'text', text: '{"measures":[]}' }],
        });
        const originalHandler = handleSonarQubeMeasuresHistory;
        handleSonarQubeMeasuresHistory = mockHandler;
        // Test with string parameter
        await measuresHistoryLambda({
          component: 'my-project',
          metrics: 'coverage',
          from: '2023-01-01',
          to: '2023-02-01',
        });
        // Test with array parameter
        await measuresHistoryLambda({
          component: 'my-project',
          metrics: ['coverage', 'bugs'],
          branch: 'main',
          page: '2',
          page_size: '20',
        });
        // Test with pull request parameter
        await measuresHistoryLambda({
          component: 'my-project',
          metrics: ['coverage'],
          pull_request: 'pr-123',
        });
        // Test full parameter set
        await measuresHistoryLambda({
          component: 'my-project',
          metrics: ['coverage', 'bugs', 'code_smells'],
          from: '2023-01-01',
          to: '2023-12-31',
          branch: 'develop',
          pull_request: 'pr-456',
          page: '3',
          page_size: '50',
        });
        // Check that the handler was called with the correct parameters
        expect(mockHandler).toHaveBeenCalledTimes(4);
        // Check first call with string parameter
        expect(mockHandler.mock.calls[0][0]).toEqual({
          component: 'my-project',
          metrics: ['coverage'],
          from: '2023-01-01',
          to: '2023-02-01',
          branch: undefined,
          pullRequest: undefined,
          page: undefined,
          pageSize: undefined,
        });
        // Check second call with array parameter
        expect(mockHandler.mock.calls[1][0]).toEqual({
          component: 'my-project',
          metrics: ['coverage', 'bugs'],
          from: undefined,
          to: undefined,
          branch: 'main',
          pullRequest: undefined,
          page: '2',
          pageSize: '20',
        });
        // Check third call with pull request parameter
        expect(mockHandler.mock.calls[2][0]).toEqual({
          component: 'my-project',
          metrics: ['coverage'],
          from: undefined,
          to: undefined,
          branch: undefined,
          pullRequest: 'pr-123',
          page: undefined,
          pageSize: undefined,
        });
        // Check fourth call with full parameter set
        expect(mockHandler.mock.calls[3][0]).toEqual({
          component: 'my-project',
          metrics: ['coverage', 'bugs', 'code_smells'],
          from: '2023-01-01',
          to: '2023-12-31',
          branch: 'develop',
          pullRequest: 'pr-456',
          page: '3',
          pageSize: '50',
        });
        // Restore the original handler
        handleSonarQubeMeasuresHistory = originalHandler;
      });
    });
    it('should fully process SonarQube projects response', async () => {
      const fullProjectsResponse = {
        projects: [
          {
            key: 'test-project',
            name: 'Test Project',
            qualifier: 'TRK',
            visibility: 'public',
            lastAnalysisDate: '2024-03-01',
            revision: 'abc123',
            managed: false,
            extra: 'should be excluded',
          },
        ],
        paging: {
          pageIndex: 1,
          pageSize: 10,
          total: 1,
        },
      };
      mockHandlers.handleSonarQubeProjects.mockResolvedValueOnce({
        content: [
          {
            type: 'text',
            text: JSON.stringify(fullProjectsResponse),
          },
        ],
      });
      const result = await mockHandlers.handleSonarQubeProjects({ page: 1, page_size: 10 });
      const data = JSON.parse(result.content[0].text);
      expect(data.projects[0].key).toBe('test-project');
      expect(data.projects[0].name).toBe('Test Project');
      expect(data.projects[0].qualifier).toBe('TRK');
      expect(data.projects[0].visibility).toBe('public');
      expect(data.projects[0].lastAnalysisDate).toBe('2024-03-01');
      expect(data.projects[0].revision).toBe('abc123');
      expect(data.projects[0].managed).toBe(false);
      expect(data.paging.pageIndex).toBe(1);
      expect(data.paging.pageSize).toBe(10);
      expect(data.paging.total).toBe(1);
    });
    it('should fully process SonarQube issues response', async () => {
      const fullIssuesResponse = {
        issues: [
          {
            key: 'test-issue',
            rule: 'test-rule',
            severity: 'MAJOR',
            component: 'test-component',
            project: 'test-project',
            line: 1,
            status: 'OPEN',
            issueStatus: 'OPEN',
            message: 'Test issue',
            messageFormattings: [],
            effort: '1h',
            debt: '1h',
            author: 'test-author',
            tags: ['tag1', 'tag2'],
            creationDate: '2024-03-01',
            updateDate: '2024-03-02',
            type: 'BUG',
            cleanCodeAttribute: 'CONSISTENT',
            cleanCodeAttributeCategory: 'ADAPTABLE',
            prioritizedRule: true,
            impacts: [{ severity: 'HIGH', softwareQuality: 'SECURITY' }],
            textRange: { startLine: 1, endLine: 1, startOffset: 0, endOffset: 10 },
            comments: [],
            transitions: [],
            actions: [],
            flows: [],
            quickFixAvailable: false,
            ruleDescriptionContextKey: 'context',
            codeVariants: [],
            hash: 'hash',
          },
        ],
        components: [{ key: 'comp1', name: 'Component 1' }],
        rules: [{ key: 'rule1', name: 'Rule 1' }],
        users: [{ login: 'user1', name: 'User 1' }],
        facets: [{ property: 'facet1', values: [] }],
        paging: {
          pageIndex: 1,
          pageSize: 10,
          total: 1,
        },
      };
      mockHandlers.handleSonarQubeGetIssues.mockResolvedValueOnce({
        content: [
          {
            type: 'text',
            text: JSON.stringify(fullIssuesResponse),
          },
        ],
      });
      const result = await mockHandlers.handleSonarQubeGetIssues({
        projectKey: 'test-project',
        severity: 'MAJOR',
        page: 1,
        pageSize: 10,
        statuses: ['OPEN'],
        resolutions: ['FIXED'],
        resolved: true,
        types: ['BUG'],
        rules: ['rule1'],
        tags: ['tag1'],
        createdAfter: '2024-01-01',
        createdBefore: '2024-03-01',
        createdAt: '2024-02-01',
        createdInLast: '30d',
        assignees: ['user1'],
        authors: ['author1'],
        cwe: ['cwe1'],
        languages: ['java'],
        owaspTop10: ['a1'],
        sansTop25: ['sans1'],
        sonarsourceSecurity: ['ss1'],
        onComponentOnly: true,
        facets: ['facet1'],
        sinceLeakPeriod: true,
        inNewCodePeriod: true,
      });
      const data = JSON.parse(result.content[0].text);
      // Check all fields are properly mapped
      expect(data.issues[0].key).toBe('test-issue');
      expect(data.issues[0].rule).toBe('test-rule');
      expect(data.issues[0].severity).toBe('MAJOR');
      expect(data.issues[0].component).toBe('test-component');
      expect(data.issues[0].project).toBe('test-project');
      expect(data.issues[0].line).toBe(1);
      expect(data.issues[0].status).toBe('OPEN');
      expect(data.issues[0].issueStatus).toBe('OPEN');
      expect(data.issues[0].message).toBe('Test issue');
      expect(data.issues[0].effort).toBe('1h');
      expect(data.issues[0].debt).toBe('1h');
      expect(data.issues[0].author).toBe('test-author');
      expect(data.issues[0].tags).toEqual(['tag1', 'tag2']);
      expect(data.issues[0].creationDate).toBe('2024-03-01');
      expect(data.issues[0].updateDate).toBe('2024-03-02');
      expect(data.issues[0].type).toBe('BUG');
      expect(data.issues[0].cleanCodeAttribute).toBe('CONSISTENT');
      expect(data.issues[0].cleanCodeAttributeCategory).toBe('ADAPTABLE');
      expect(data.issues[0].prioritizedRule).toBe(true);
      expect(data.issues[0].impacts).toHaveLength(1);
      expect(data.issues[0].impacts[0].severity).toBe('HIGH');
      // Check other response data
      expect(data.components).toHaveLength(1);
      expect(data.rules).toHaveLength(1);
      expect(data.users).toHaveLength(1);
      expect(data.facets).toHaveLength(1);
      expect(data.paging.pageIndex).toBe(1);
      expect(data.paging.pageSize).toBe(10);
      expect(data.paging.total).toBe(1);
    });
    it('should handle metrics response', async () => {
      const metricsResponse = {
        metrics: [
          {
            key: 'test-metric',
            name: 'Test Metric',
            description: 'Test metric description',
            domain: 'test',
            type: 'INT',
          },
        ],
        paging: {
          pageIndex: 1,
          pageSize: 10,
          total: 1,
        },
      };
      mockHandlers.handleSonarQubeGetMetrics.mockResolvedValueOnce({
        content: [
          {
            type: 'text',
            text: JSON.stringify(metricsResponse),
          },
        ],
      });
      const result = await mockHandlers.handleSonarQubeGetMetrics({
        page: 1,
        pageSize: 10,
      });
      const data = JSON.parse(result.content[0].text);
      expect(data.metrics).toHaveLength(1);
      expect(data.metrics[0].key).toBe('test-metric');
      expect(data.metrics[0].name).toBe('Test Metric');
      expect(data.paging.pageIndex).toBe(1);
    });
  });
  describe('Tool registration schemas', () => {
    it('should correctly transform page parameters', () => {
      const pageSchema = z
        .string()
        .optional()
        .transform((val: any) => (val ? parseInt(val, 10) || null : null));
      expect(pageSchema.parse('10')).toBe(10);
      expect(pageSchema.parse('not-a-number')).toBe(null);
      expect(pageSchema.parse('')).toBe(null);
      expect(pageSchema.parse(undefined)).toBe(null);
    });
    it('should validate severity enum schema', () => {
      const severitySchema = z
        .enum(['INFO', 'MINOR', 'MAJOR', 'CRITICAL', 'BLOCKER'])
        .nullable()
        .optional();
      expect(severitySchema.parse('MAJOR')).toBe('MAJOR');
      expect(severitySchema.parse('BLOCKER')).toBe('BLOCKER');
      expect(severitySchema.parse(null)).toBe(null);
      expect(severitySchema.parse(undefined)).toBe(undefined);
      expect(() => severitySchema.parse('INVALID')).toThrow();
    });
    it('should validate status schema', () => {
      const statusSchema = z
        .array(
          z.enum([
            'OPEN',
            'CONFIRMED',
            'REOPENED',
            'RESOLVED',
            'CLOSED',
            'TO_REVIEW',
            'IN_REVIEW',
            'REVIEWED',
          ])
        )
        .nullable()
        .optional();
      expect(statusSchema.parse(['OPEN', 'CONFIRMED'])).toEqual(['OPEN', 'CONFIRMED']);
      expect(statusSchema.parse(null)).toBe(null);
      expect(statusSchema.parse(undefined)).toBe(undefined);
      expect(() => statusSchema.parse(['INVALID'])).toThrow();
    });
    it('should validate resolution schema', () => {
      const resolutionSchema = z
        .array(z.enum(['FALSE-POSITIVE', 'WONTFIX', 'FIXED', 'REMOVED']))
        .nullable()
        .optional();
      expect(resolutionSchema.parse(['FALSE-POSITIVE', 'WONTFIX'])).toEqual([
        'FALSE-POSITIVE',
        'WONTFIX',
      ]);
      expect(resolutionSchema.parse(null)).toBe(null);
      expect(resolutionSchema.parse(undefined)).toBe(undefined);
      expect(() => resolutionSchema.parse(['INVALID'])).toThrow();
    });
    it('should validate type schema', () => {
      const typeSchema = z
        .array(z.enum(['CODE_SMELL', 'BUG', 'VULNERABILITY', 'SECURITY_HOTSPOT']))
        .nullable()
        .optional();
      expect(typeSchema.parse(['CODE_SMELL', 'BUG'])).toEqual(['CODE_SMELL', 'BUG']);
      expect(typeSchema.parse(null)).toBe(null);
      expect(typeSchema.parse(undefined)).toBe(undefined);
      expect(() => typeSchema.parse(['INVALID'])).toThrow();
    });
    it('should transform boolean parameters', () => {
      const booleanSchema = z
        .union([z.boolean(), z.string().transform((val: any) => val === 'true')])
        .nullable()
        .optional();
      expect(booleanSchema.parse('true')).toBe(true);
      expect(booleanSchema.parse('false')).toBe(false);
      expect(booleanSchema.parse(true)).toBe(true);
      expect(booleanSchema.parse(false)).toBe(false);
      expect(booleanSchema.parse(null)).toBe(null);
      expect(booleanSchema.parse(undefined)).toBe(undefined);
    });
  });
  describe('Tool registration lambdas', () => {
    beforeEach(() => {
      // Reset all mocks
      vi.resetAllMocks();
    });
    it('should test the metrics tool lambda', async () => {
      // Mock the handleSonarQubeGetMetrics function to track calls
      const mockGetMetrics = (vi.fn() as any).mockResolvedValue({
        content: [{ type: 'text', text: '{"metrics":[]}' }],
      });
      const originalHandler = handleSonarQubeGetMetrics;
      handleSonarQubeGetMetrics = mockGetMetrics;
      // Create the lambda handler that's in the tool registration
      const metricsLambda = async (params: Record<string, unknown>) => {
        const result = await handleSonarQubeGetMetrics({
          page: nullToUndefined(params.page) as number | undefined,
          pageSize: nullToUndefined(params.page_size) as number | undefined,
        });
        return {
          content: [
            {
              type: 'text',
              text: JSON.stringify(result, null, 2),
            },
          ],
        };
      };
      // Call the lambda with params
      await metricsLambda({ page: '5', page_size: '20' });
      // Check that handleSonarQubeGetMetrics was called with the right params
      expect(mockGetMetrics).toHaveBeenCalledWith({
        page: '5',
        pageSize: '20',
      });
      // Restore the original function
      handleSonarQubeGetMetrics = originalHandler;
    });
    it('should test the issues tool lambda', async () => {
      // Mock the handleSonarQubeGetIssues function to track calls
      const mockGetIssues = (vi.fn() as any).mockResolvedValue({
        content: [{ type: 'text', text: '{"issues":[]}' }],
      });
      const originalHandler = handleSonarQubeGetIssues;
      handleSonarQubeGetIssues = mockGetIssues;
      // Mock mapToSonarQubeParams to return expected output
      const originalMapFunction = mapToSonarQubeParams;
      const mockMapFunction = (vi.fn() as any).mockReturnValue({
        projectKey: 'test-project',
        severity: 'MAJOR',
      });
      mapToSonarQubeParams = mockMapFunction;
      // Create the lambda handler that's in the tool registration
      const issuesLambda = async (params: Record<string, unknown>) => {
        return await handleSonarQubeGetIssues(mapToSonarQubeParams(params));
      };
      // Call the lambda with params
      await issuesLambda({
        project_key: 'test-project',
        severity: 'MAJOR',
      });
      // Check that mapToSonarQubeParams was called with the right params
      expect(mockMapFunction).toHaveBeenCalledWith({
        project_key: 'test-project',
        severity: 'MAJOR',
      });
      // Check that handleSonarQubeGetIssues was called with the mapped params
      expect(mockGetIssues).toHaveBeenCalledWith({
        projectKey: 'test-project',
        severity: 'MAJOR',
      });
      // Restore the original functions
      handleSonarQubeGetIssues = originalHandler;
      mapToSonarQubeParams = originalMapFunction;
    });
    it('should test the hotspot search tool lambda', async () => {
      // Mock the handleSonarQubeSearchHotspots function to track calls
      const mockSearchHotspots = (vi.fn() as any).mockResolvedValue({
        content: [{ type: 'text', text: '{"hotspots":[]}' }],
      });
      const originalHandler = handleSonarQubeHotspots;
      handleSonarQubeHotspots = mockSearchHotspots;
      // Mock mapToSonarQubeParams to return expected output
      const originalMapFunction = mapToSonarQubeParams;
      const mockMapFunction = (vi.fn() as any).mockReturnValue({
        projectKey: 'test-project',
        status: 'TO_REVIEW',
        assignedToMe: true,
        sinceLeakPeriod: false,
        inNewCodePeriod: true,
        page: 1,
        pageSize: 50,
      });
      mapToSonarQubeParams = mockMapFunction;
      // Create the lambda handler that's in the tool registration
      const searchHotspotsLambda = async (params: Record<string, unknown>) => {
        return await handleSonarQubeHotspots(mapToSonarQubeParams(params));
      };
      // Call the lambda with params that include string booleans
      await searchHotspotsLambda({
        project_key: 'test-project',
        status: 'TO_REVIEW',
        assigned_to_me: 'true',
        since_leak_period: 'false',
        in_new_code_period: 'true',
        page: '1',
        page_size: '50',
      });
      // Check that mapToSonarQubeParams was called with the right params
      expect(mockMapFunction).toHaveBeenCalledWith({
        project_key: 'test-project',
        status: 'TO_REVIEW',
        assigned_to_me: 'true',
        since_leak_period: 'false',
        in_new_code_period: 'true',
        page: '1',
        page_size: '50',
      });
      // Check that handleSonarQubeSearchHotspots was called with the mapped params
      expect(mockSearchHotspots).toHaveBeenCalledWith({
        projectKey: 'test-project',
        status: 'TO_REVIEW',
        assignedToMe: true,
        sinceLeakPeriod: false,
        inNewCodePeriod: true,
        page: 1,
        pageSize: 50,
      });
      // Restore the original functions
      handleSonarQubeHotspots = originalHandler;
      mapToSonarQubeParams = originalMapFunction;
    });
    it('should test the quality gate handler lambda', async () => {
      // Set up mock for the API call
      nock('http://localhost:9000')
        .get('/api/qualitygates/show')
        .query({ id: 'gate-123' })
        .reply(200, {
          id: 'gate-123',
          name: 'Test Quality Gate',
          conditions: [],
          isBuiltIn: false,
        });
      // Test the lambda
      const result = await qualityGateHandler({ id: 'gate-123' });
      expect(result).toBeDefined();
      expect(result.content[0].text).toContain('gate-123');
    });
    it('should test the project quality gate status handler lambda', async () => {
      // Set up mock for the API call
      nock('http://localhost:9000')
        .get('/api/qualitygates/project_status')
        .query({
          projectKey: 'test-project',
          branch: 'main',
          pullRequest: 'pr-123',
        })
        .reply(200, {
          projectStatus: {
            status: 'OK',
            conditions: [],
          },
        });
      // Test the lambda with all parameters
      const result = await qualityGateStatusHandler({
        project_key: 'test-project',
        branch: 'main',
        pull_request: 'pr-123',
      });
      expect(result).toBeDefined();
      expect(result.content[0].text).toContain('OK');
    });
    it('should test the get hotspot details handler lambda', async () => {
      // Set up mock for the API call
      nock('http://localhost:9000')
        .get('/api/hotspots/show')
        .query({
          hotspot: 'hotspot-123',
        })
        .reply(200, {
          key: 'hotspot-123',
          component: 'test',
          project: 'test-project',
          rule: {
            key: 'java:S2068',
            name: 'Hard-coded credentials',
            securityCategory: 'weak-cryptography',
          },
          status: 'TO_REVIEW',
          line: 42,
          message: 'Make sure this password is not hard-coded.',
          author: '[email protected]',
          creationDate: '2023-01-15T10:30:00+0000',
        });
      // Test the lambda
      const result = await hotspotHandler({ hotspot_key: 'hotspot-123' });
      expect(result).toBeDefined();
      expect(result.content[0].text).toContain('hotspot-123');
    });
    it('should test the update hotspot status handler lambda', async () => {
      // Set up mock for the API call
      nock('http://localhost:9000')
        .post('/api/hotspots/change_status', {
          hotspot: 'hotspot-123',
          status: 'REVIEWED',
          resolution: 'SAFE',
          comment: 'Reviewed and safe',
        })
        .reply(200, {});
      // Test the lambda with all parameters
      const result = await updateHotspotStatusHandler({
        hotspot_key: 'hotspot-123',
        status: 'REVIEWED',
        resolution: 'SAFE',
        comment: 'Reviewed and safe',
      });
      expect(result).toBeDefined();
      expect(result.content[0].text).toContain('successfully');
    });
  });
  describe('Tool schema validations', () => {
    it('should validate and transform all issue tool schemas', () => {
      // Create schemas that match what's in the tool registration
      const pageSchema = z
        .string()
        .optional()
        .transform((val: any) => (val ? parseInt(val, 10) || null : null));
      const booleanSchema = z
        .union([z.boolean(), z.string().transform((val: any) => val === 'true')])
        .nullable()
        .optional();
      const severitySchema = z
        .enum(['INFO', 'MINOR', 'MAJOR', 'CRITICAL', 'BLOCKER'])
        .nullable()
        .optional();
      const statusSchema = z
        .array(
          z.enum([
            'OPEN',
            'CONFIRMED',
            'REOPENED',
            'RESOLVED',
            'CLOSED',
            'TO_REVIEW',
            'IN_REVIEW',
            'REVIEWED',
          ])
        )
        .nullable()
        .optional();
      const resolutionSchema = z
        .array(z.enum(['FALSE-POSITIVE', 'WONTFIX', 'FIXED', 'REMOVED']))
        .nullable()
        .optional();
      const typeSchema = z
        .array(z.enum(['CODE_SMELL', 'BUG', 'VULNERABILITY', 'SECURITY_HOTSPOT']))
        .nullable()
        .optional();
      const stringArraySchema = z.array(z.string()).nullable().optional();
      // Create the complete schema
      const schema = z.object({
        project_key: z.string(),
        severity: severitySchema,
        page: pageSchema,
        page_size: pageSchema,
        statuses: statusSchema,
        resolutions: resolutionSchema,
        resolved: booleanSchema,
        types: typeSchema,
        rules: stringArraySchema,
        tags: stringArraySchema,
        created_after: z.string().nullable().optional(),
        created_before: z.string().nullable().optional(),
        created_at: z.string().nullable().optional(),
        created_in_last: z.string().nullable().optional(),
        assignees: stringArraySchema,
        authors: stringArraySchema,
        cwe: stringArraySchema,
        languages: stringArraySchema,
        owasp_top10: stringArraySchema,
        sans_top25: stringArraySchema,
        sonarsource_security: stringArraySchema,
        on_component_only: booleanSchema,
        facets: stringArraySchema,
        since_leak_period: booleanSchema,
        in_new_code_period: booleanSchema,
      });
      // Test the complete schema
      const testData = {
        project_key: 'test-project',
        severity: 'MAJOR',
        page: '10',
        page_size: '20',
        statuses: ['OPEN', 'CONFIRMED'],
        resolutions: ['FALSE-POSITIVE', 'WONTFIX'],
        resolved: 'true',
        types: ['CODE_SMELL', 'BUG'],
        rules: ['rule1', 'rule2'],
        tags: ['tag1', 'tag2'],
        created_after: '2024-01-01',
        created_before: '2024-12-31',
        created_at: '2024-06-15',
        created_in_last: '7d',
        assignees: ['user1', 'user2'],
        authors: ['author1', 'author2'],
        cwe: ['cwe1', 'cwe2'],
        languages: ['java', 'typescript'],
        owasp_top10: ['a1', 'a2'],
        sans_top25: ['sans1', 'sans2'],
        sonarsource_security: ['sec1', 'sec2'],
        on_component_only: 'true',
        facets: ['facet1', 'facet2'],
        since_leak_period: 'true',
        in_new_code_period: 'true',
      };
      // Validate through the Zod schema
      const validated = schema.parse(testData);
      // Check transformations happened correctly
      expect(validated.page).toBe(10);
      expect(validated.page_size).toBe(20);
      expect(validated.resolved).toBe(true);
      expect(validated.on_component_only).toBe(true);
      expect(validated.since_leak_period).toBe(true);
      expect(validated.in_new_code_period).toBe(true);
      // Check arrays were kept intact
      expect(validated.statuses).toEqual(['OPEN', 'CONFIRMED']);
      expect(validated.resolutions).toEqual(['FALSE-POSITIVE', 'WONTFIX']);
      expect(validated.types).toEqual(['CODE_SMELL', 'BUG']);
      expect(validated.rules).toEqual(['rule1', 'rule2']);
      // Check that strings were kept intact
      expect(validated.project_key).toBe('test-project');
      expect(validated.severity).toBe('MAJOR');
      expect(validated.created_after).toBe('2024-01-01');
    });
  });
  describe('Direct tool registration test', () => {
    it('should validate tool existence', () => {
      // Skip if mcpServer is mocked or doesn't have tool method
      if (mcpServer.tool) {
        expect(mcpServer.tool).toBeDefined();
      } else {
        expect(mcpServer).toBeDefined();
      }
    });
    it('should test the lambda functions directly', async () => {
      // Create lambda functions that match the lambda functions in the tool registrations
      const metricsLambda = async (params: Record<string, unknown>) => {
        const result = await handleSonarQubeGetMetrics({
          page: nullToUndefined(params.page) as number | undefined,
          pageSize: nullToUndefined(params.page_size) as number | undefined,
        });
        return { content: [{ type: 'text', text: JSON.stringify(result) }] };
      };
      const issuesLambda = async (params: Record<string, unknown>) => {
        return await handleSonarQubeGetIssues(mapToSonarQubeParams(params));
      };
      const componentsLambda = async (params: Record<string, unknown>) => {
        return await handleSonarQubeComponentsMeasures({
          componentKeys: Array.isArray(params.component_keys)
            ? (params.component_keys as string[])
            : [params.component_keys as string],
          metricKeys: Array.isArray(params.metric_keys)
            ? (params.metric_keys as string[])
            : [params.metric_keys as string],
          additionalFields: params.additional_fields as string[] | undefined,
          branch: params.branch as string | undefined,
          pullRequest: params.pull_request as string | undefined,
          period: params.period as string | undefined,
          page: nullToUndefined(params.page) as number | undefined,
          pageSize: nullToUndefined(params.page_size) as number | undefined,
        });
      };
      const historyLambda = async (params: Record<string, unknown>) => {
        return await handleSonarQubeMeasuresHistory({
          component: params.component as string,
          metrics: Array.isArray(params.metrics)
            ? (params.metrics as string[])
            : [params.metrics as string],
          from: params.from as string | undefined,
          to: params.to as string | undefined,
          branch: params.branch as string | undefined,
          pullRequest: params.pull_request as string | undefined,
          page: nullToUndefined(params.page) as number | undefined,
          pageSize: nullToUndefined(params.page_size) as number | undefined,
        });
      };
      // Mock all the handler functions to test the lambda functions
      const mockGetMetrics = (vi.fn() as any).mockResolvedValue({
        content: [{ type: 'text', text: '{"metrics":[]}' }],
      });
      const mockGetIssues = (vi.fn() as any).mockResolvedValue({
        content: [{ type: 'text', text: '{"issues":[]}' }],
      });
      const mockComponentsMeasures = (vi.fn() as any).mockResolvedValue({
        content: [{ type: 'text', text: '{"components":[]}' }],
      });
      const mockMeasuresHistory = (vi.fn() as any).mockResolvedValue({
        content: [{ type: 'text', text: '{"measures":[]}' }],
      });
      // Override the handler functions with mocks
      const originalGetMetrics = handleSonarQubeGetMetrics;
      const originalGetIssues = handleSonarQubeGetIssues;
      const originalComponentsMeasures = handleSonarQubeComponentsMeasures;
      const originalMeasuresHistory = handleSonarQubeMeasuresHistory;
      handleSonarQubeGetMetrics = mockGetMetrics;
      handleSonarQubeGetIssues = mockGetIssues;
      handleSonarQubeComponentsMeasures = mockComponentsMeasures;
      handleSonarQubeMeasuresHistory = mockMeasuresHistory;
      // Test metrics lambda
      await metricsLambda({ page: '10', page_size: '20' });
      expect(mockGetMetrics).toHaveBeenCalledWith({
        page: '10',
        pageSize: '20',
      });
      // Test issues lambda with all possible parameters
      await issuesLambda({
        project_key: 'test-project',
        severity: 'MAJOR',
        page: '1',
        page_size: '10',
        statuses: ['OPEN', 'CONFIRMED'],
        resolutions: ['FALSE-POSITIVE', 'WONTFIX'],
        resolved: 'true',
        types: ['CODE_SMELL', 'BUG'],
        rules: ['rule1', 'rule2'],
        tags: ['tag1', 'tag2'],
        created_after: '2023-01-01',
        created_before: '2023-12-31',
        created_at: '2023-06-15',
        created_in_last: '7d',
        assignees: ['user1', 'user2'],
        authors: ['author1', 'author2'],
        cwe: ['cwe1', 'cwe2'],
        languages: ['java', 'typescript'],
        owasp_top10: ['a1', 'a2'],
        sans_top25: ['sans1', 'sans2'],
        sonarsource_security: ['sec1', 'sec2'],
        on_component_only: 'true',
        facets: ['facet1', 'facet2'],
        since_leak_period: 'true',
        in_new_code_period: 'true',
      });
      expect(mockGetIssues).toHaveBeenCalledTimes(1);
      // Test components lambda
      await componentsLambda({
        component_keys: ['comp1', 'comp2'],
        metric_keys: ['coverage', 'bugs'],
        additional_fields: ['periods'],
        branch: 'main',
        pull_request: 'pr-123',
        period: '1',
        page: '2',
        page_size: '25',
      });
      expect(mockComponentsMeasures).toHaveBeenCalledWith({
        componentKeys: ['comp1', 'comp2'],
        metricKeys: ['coverage', 'bugs'],
        additionalFields: ['periods'],
        branch: 'main',
        pullRequest: 'pr-123',
        period: '1',
        page: '2',
        pageSize: '25',
      });
      // Test history lambda
      await historyLambda({
        component: 'test-component',
        metrics: ['coverage', 'bugs'],
        from: '2023-01-01',
        to: '2023-12-31',
        branch: 'feature',
        pull_request: 'pr-456',
        page: '3',
        page_size: '30',
      });
      expect(mockMeasuresHistory).toHaveBeenCalledWith({
        component: 'test-component',
        metrics: ['coverage', 'bugs'],
        from: '2023-01-01',
        to: '2023-12-31',
        branch: 'feature',
        pullRequest: 'pr-456',
        page: '3',
        pageSize: '30',
      });
      // Restore the original functions
      handleSonarQubeGetMetrics = originalGetMetrics;
      handleSonarQubeGetIssues = originalGetIssues;
      handleSonarQubeComponentsMeasures = originalComponentsMeasures;
      handleSonarQubeMeasuresHistory = originalMeasuresHistory;
    });
  });
  describe('Tool schema transformations with actual Zod schemas', () => {
    it('should transform issues tool parameters through Zod schema', () => {
      // Import the actual schema from the tool registration
      const issuesSchema = z.object({
        project_key: z.string().optional(),
        on_component_only: z
          .union([z.boolean(), z.string().transform((val: any) => val === 'true')])
          .nullable()
          .optional(),
        resolved: z
          .union([z.boolean(), z.string().transform((val: any) => val === 'true')])
          .nullable()
          .optional(),
        page: z
          .string()
          .optional()
          .transform((val: any) => (val ? parseInt(val, 10) || null : null)),
        page_size: z
          .string()
          .optional()
          .transform((val: any) => (val ? parseInt(val, 10) || null : null)),
      });
      // Test with string values that should be transformed
      const result = issuesSchema.parse({
        project_key: 'test-project',
        on_component_only: 'true',
        resolved: 'false',
        page: '5',
        page_size: '100',
      });
      expect(result.on_component_only).toBe(true);
      expect(result.resolved).toBe(false);
      expect(result.page).toBe(5);
      expect(result.page_size).toBe(100);
    });
    it('should transform hotspots tool parameters through Zod schema', () => {
      // Import the actual schema from the tool registration
      const hotspotsSchema = z.object({
        project_key: z.string().optional(),
        assigned_to_me: z
          .union([z.boolean(), z.string().transform((val: any) => val === 'true')])
          .nullable()
          .optional(),
        since_leak_period: z
          .union([z.boolean(), z.string().transform((val: any) => val === 'true')])
          .nullable()
          .optional(),
        in_new_code_period: z
          .union([z.boolean(), z.string().transform((val: any) => val === 'true')])
          .nullable()
          .optional(),
        page: z
          .string()
          .optional()
          .transform((val: any) => (val ? parseInt(val, 10) || null : null)),
        page_size: z
          .string()
          .optional()
          .transform((val: any) => (val ? parseInt(val, 10) || null : null)),
      });
      // Test with string values that should be transformed
      const result = hotspotsSchema.parse({
        project_key: 'test-project',
        assigned_to_me: 'true',
        since_leak_period: 'false',
        in_new_code_period: 'true',
        page: '2',
        page_size: '50',
      });
      expect(result.assigned_to_me).toBe(true);
      expect(result.since_leak_period).toBe(false);
      expect(result.in_new_code_period).toBe(true);
      expect(result.page).toBe(2);
      expect(result.page_size).toBe(50);
      // Test with boolean values directly
      const result2 = hotspotsSchema.parse({
        project_key: 'test-project',
        assigned_to_me: false,
        since_leak_period: true,
        in_new_code_period: false,
      });
      expect(result2.assigned_to_me).toBe(false);
      expect(result2.since_leak_period).toBe(true);
      expect(result2.in_new_code_period).toBe(false);
    });
  });
  describe('Security Hotspot handlers', () => {
    describe('handleSonarQubeSearchHotspots', () => {
      it('should search and return hotspots', async () => {
        nock('http://localhost:9000')
          .get('/api/hotspots/search')
          .query({
            projectKey: 'test-project',
            status: 'TO_REVIEW',
            p: '1',
            ps: '50',
          })
          .matchHeader('authorization', 'Bearer test-token')
          .reply(200, {
            hotspots: [
              {
                key: 'AYg1234567890',
                component: 'com.example:my-project:src/main/java/Example.java',
                project: 'com.example:my-project',
                securityCategory: 'sql-injection',
                vulnerabilityProbability: 'HIGH',
                status: 'TO_REVIEW',
                line: 42,
                message: 'Make sure using this database query is safe.',
                author: '[email protected]',
                creationDate: '2023-01-15T10:30:00+0000',
              },
            ],
            components: [
              {
                key: 'com.example:my-project:src/main/java/Example.java',
                name: 'Example.java',
                path: 'src/main/java/Example.java',
              },
            ],
            paging: {
              pageIndex: 1,
              pageSize: 50,
              total: 1,
            },
          });
        const response = await handleSonarQubeHotspots({
          projectKey: 'test-project',
          status: 'TO_REVIEW',
          page: 1,
          pageSize: 50,
        });
        const result = JSON.parse(response.content[0].text);
        expect(result.hotspots).toHaveLength(1);
        expect(result.hotspots[0].key).toBe('AYg1234567890');
        expect(result.hotspots[0].status).toBe('TO_REVIEW');
        expect(result.paging.total).toBe(1);
      });
    });
    describe('handleSonarQubeGetHotspotDetails', () => {
      it('should get and return hotspot details', async () => {
        nock('http://localhost:9000')
          .get('/api/hotspots/show')
          .query({
            hotspot: 'AYg1234567890',
          })
          .matchHeader('authorization', 'Bearer test-token')
          .reply(200, {
            key: 'AYg1234567890',
            component: {
              key: 'com.example:my-project:src/main/java/Example.java',
              name: 'Example.java',
              qualifier: 'FIL',
              path: 'src/main/java/Example.java',
            },
            project: {
              key: 'com.example:my-project',
              name: 'My Project',
              qualifier: 'TRK',
            },
            rule: {
              key: 'java:S2077',
              name: 'SQL queries should not be vulnerable to injection attacks',
              securityCategory: 'sql-injection',
              vulnerabilityProbability: 'HIGH',
            },
            status: 'TO_REVIEW',
            line: 42,
            message: 'Make sure using this database query is safe.',
            author: '[email protected]',
            creationDate: '2023-01-15T10:30:00+0000',
            updateDate: '2023-01-15T10:30:00+0000',
            flows: [],
            canChangeStatus: true,
          });
        const response = await handleSonarQubeHotspot('AYg1234567890');
        expect(response).toBeDefined();
        expect(response.content).toBeDefined();
        expect(response.content[0]).toBeDefined();
        const result = JSON.parse(response.content[0].text);
        expect(result.key).toBe('AYg1234567890');
        expect(result.status).toBe('TO_REVIEW');
        expect(result.rule.securityCategory).toBe('sql-injection');
        expect(result.canChangeStatus).toBe(true);
      });
    });
    describe('handleSonarQubeUpdateHotspotStatus', () => {
      it('should update hotspot status', async () => {
        nock('http://localhost:9000')
          .post('/api/hotspots/change_status', {
            hotspot: 'AYg1234567890',
            status: 'REVIEWED',
            resolution: 'FIXED',
            comment: 'Fixed by using parameterized queries',
          })
          .matchHeader('authorization', 'Bearer test-token')
          .reply(200, {});
        const response = await handleSonarQubeUpdateHotspotStatus({
          hotspot: 'AYg1234567890',
          status: 'REVIEWED',
          resolution: 'FIXED',
          comment: 'Fixed by using parameterized queries',
        });
        expect(response.content[0].text).toContain('Hotspot status updated successfully');
      });
      it('should update hotspot status without optional fields', async () => {
        nock('http://localhost:9000')
          .post('/api/hotspots/change_status', {
            hotspot: 'AYg1234567890',
            status: 'TO_REVIEW',
          })
          .matchHeader('authorization', 'Bearer test-token')
          .reply(200, {});
        const response = await handleSonarQubeUpdateHotspotStatus({
          hotspot: 'AYg1234567890',
          status: 'TO_REVIEW',
        });
        expect(response.content[0].text).toContain('Hotspot status updated successfully');
      });
    });
  });
  describe('Create Default Client', () => {
    it('should create default client with environment variables', async () => {
      // Ensure environment variables are set
      process.env.SONARQUBE_TOKEN = 'test-token';
      process.env.SONARQUBE_URL = 'http://localhost:9000';
      // Import module fresh
      vi.resetModules();
      const index = await import('../index.js');
      // Call createDefaultClient - it should not throw
      expect(() => index.createDefaultClient()).not.toThrow();
    });
  });
  describe('Error Handling Coverage', () => {
    it('should handle errors in handler functions', async () => {
      // Import module fresh
      vi.resetModules();
      const index = await import('../index.js');
      // Mock API calls to fail
      nock('http://localhost:9000')
        .get('/api/projects/search')
        .query(true)
        .reply(500, 'Internal Server Error');
      // Test error handling
      await expect(index.handleSonarQubeProjects({})).rejects.toThrow();
    }, 10000);
    it('should test parameter mapping with null values', async () => {
      vi.resetModules();
      const index = await import('../index.js');
      // Test mapToSonarQubeParams with various null/undefined values
      const result = index.mapToSonarQubeParams({
        project_key: null,
        projects: undefined,
        component_keys: null,
        components: null,
        on_component_only: false,
        branch: null,
        pull_request: undefined,
        issues: null,
        severities: null,
        statuses: null,
        resolutions: null,
        resolved: null,
        types: null,
        tags: null,
        rules: null,
        created_after: null,
        created_before: null,
        created_at: null,
        created_in_last: null,
        assigned: null,
        assignees: null,
        author: null,
        authors: null,
        cwe: null,
        owasp_top10: null,
        owasp_top10_v2021: null,
        sans_top25: null,
        sonarsource_security: null,
        sonarsource_security_category: null,
        languages: null,
        facets: null,
        facet_mode: null,
        since_leak_period: null,
        in_new_code_period: null,
        s: null,
        asc: null,
        additional_fields: null,
        page: null,
        page_size: null,
        clean_code_attribute_categories: null,
        impact_severities: null,
        impact_software_qualities: null,
        issue_statuses: null,
        severity: null,
        hotspots: null,
      });
      // Verify null values are converted to undefined
      expect(result.projectKey).toBeUndefined();
      expect(result.projects).toBeUndefined();
      expect(result.componentKeys).toBeUndefined();
      expect(result.components).toBeUndefined();
      expect(result.onComponentOnly).toBe(false);
      expect(result.branch).toBeUndefined();
      expect(result.pullRequest).toBeUndefined();
    });
  });
  describe('MCP Wrapper Functions Coverage', () => {
    it('should test all MCP wrapper functions', async () => {
      // Import module fresh
      vi.resetModules();
      const index = await import('../index.js');
      // Mock all API calls
      nock('http://localhost:9000')
        .get('/api/projects/search')
        .query(true)
        .times(2)
        .reply(200, {
          components: [],
          paging: { pageIndex: 1, pageSize: 100, total: 0 },
        });
      nock('http://localhost:9000')
        .get('/api/metrics/search')
        .query(true)
        .times(2)
        .reply(200, {
          metrics: [],
          paging: { pageIndex: 1, pageSize: 100, total: 0 },
        });
      nock('http://localhost:9000')
        .get('/api/issues/search')
        .query(true)
        .times(2)
        .reply(200, {
          issues: [],
          components: [],
          rules: [],
          paging: { pageIndex: 1, pageSize: 100, total: 0 },
        });
      nock('http://localhost:9000')
        .get('/api/v2/system/health')
        .times(2)
        .reply(200, { status: 'GREEN', checkedAt: '2023-12-01T10:00:00Z' });
      nock('http://localhost:9000')
        .get('/api/system/status')
        .times(2)
        .reply(200, { id: '1', version: '10.0', status: 'UP' });
      nock('http://localhost:9000').get('/api/system/ping').times(2).reply(200, 'pong');
      nock('http://localhost:9000')
        .get('/api/measures/component')
        .query(true)
        .times(2)
        .reply(200, {
          component: { key: 'test', measures: [] },
          metrics: [],
        });
      nock('http://localhost:9000')
        .get('/api/measures/component')
        .query(true)
        .times(4)
        .reply(200, {
          component: { key: 'test', measures: [] },
          metrics: [],
        });
      nock('http://localhost:9000')
        .get('/api/measures/search_history')
        .query(true)
        .times(2)
        .reply(200, {
          measures: [],
          paging: { pageIndex: 1, pageSize: 100, total: 0 },
        });
      nock('http://localhost:9000').get('/api/qualitygates/list').times(2).reply(200, {
        qualitygates: [],
        default: 'default',
      });
      nock('http://localhost:9000').get('/api/qualitygates/show').query(true).times(2).reply(200, {
        id: 'test',
        name: 'Test Gate',
        conditions: [],
      });
      nock('http://localhost:9000')
        .get('/api/qualitygates/project_status')
        .query(true)
        .times(2)
        .reply(200, {
          projectStatus: { status: 'OK', conditions: [] },
        });
      nock('http://localhost:9000')
        .get('/api/sources/raw')
        .query(true)
        .times(2)
        .reply(200, 'source code content');
      nock('http://localhost:9000')
        .get('/api/sources/scm')
        .query(true)
        .times(2)
        .reply(200, {
          component: { key: 'test' },
          sources: {},
        });
      nock('http://localhost:9000')
        .get('/api/hotspots/search')
        .query(true)
        .times(2)
        .reply(200, {
          hotspots: [],
          paging: { pageIndex: 1, pageSize: 100, total: 0 },
        });
      nock('http://localhost:9000')
        .get('/api/hotspots/show')
        .query(true)
        .times(2)
        .reply(200, {
          key: 'hotspot-1',
          component: 'test',
          project: 'test',
          rule: { key: 'test', name: 'Test' },
          status: 'TO_REVIEW',
          securityCategory: 'test',
          vulnerabilityProbability: 'HIGH',
          line: 1,
          message: 'Test',
        });
      nock('http://localhost:9000').post('/api/hotspots/change_status').times(2).reply(200);
      // Access the wrapper functions via the module
      const module = index;
      // Call all handler functions
      await module.projectsHandler({});
      await module.metricsHandler({ page: 1, page_size: 10 });
      await module.issuesHandler({ project_key: 'test' });
      await module.healthHandler();
      await module.statusHandler();
      await module.pingHandler();
      await module.componentMeasuresHandler({ component: 'test', metric_keys: ['coverage'] });
      await module.componentsMeasuresHandler({
        component_keys: ['test'],
        metric_keys: ['coverage'],
      });
      await module.measuresHistoryHandler({ component: 'test', metrics: ['coverage'] });
      await module.qualityGatesHandler();
      await module.qualityGateHandler({ id: 'test' });
      await module.qualityGateStatusHandler({ project_key: 'test' });
      await module.sourceCodeHandler({ key: 'test' });
      await module.scmBlameHandler({ key: 'test' });
      await module.hotspotsHandler({ project_key: 'test' });
      await module.hotspotHandler({ hotspot_key: 'hotspot-1' });
      await module.updateHotspotStatusHandler({ hotspot_key: 'hotspot-1', status: 'REVIEWED' });

      // Verify handlers were called (basic smoke test)
      expect(module.projectsHandler).toBeDefined();
      expect(module.metricsHandler).toBeDefined();
    });
  });
  describe('MCP Wrapper Functions Direct Coverage', () => {
    beforeEach(() => {
      process.env.SONARQUBE_TOKEN = 'test-token';
      process.env.SONARQUBE_URL = 'http://localhost:9000';
      vi.resetModules();
    });
    it('should cover all MCP wrapper functions', async () => {
      // Set up mocks for all endpoints
      nock('http://localhost:9000')
        .get('/api/projects/search')
        .query(true)
        .reply(200, { components: [], paging: { pageIndex: 1, pageSize: 100, total: 0 } });
      nock('http://localhost:9000')
        .get('/api/metrics/search')
        .query(true)
        .reply(200, { metrics: [], total: 0 });
      nock('http://localhost:9000')
        .get('/api/issues/search')
        .query(true)
        .reply(200, { issues: [], total: 0, paging: { pageIndex: 1, pageSize: 100, total: 0 } });
      nock('http://localhost:9000')
        .get('/api/v2/system/health')
        .reply(200, { status: 'GREEN', checkedAt: '2023-12-01T10:00:00Z' });
      nock('http://localhost:9000')
        .get('/api/system/status')
        .reply(200, { status: 'UP', version: '10.0' });
      nock('http://localhost:9000').get('/api/system/ping').reply(200, 'pong');
      nock('http://localhost:9000')
        .get('/api/measures/component')
        .query(true)
        .times(3) // Allow multiple calls
        .reply(200, { component: { key: 'test', measures: [] }, metrics: [] });
      nock('http://localhost:9000')
        .get('/api/measures/search_history')
        .query(true)
        .reply(200, { measures: [] });
      nock('http://localhost:9000').get('/api/qualitygates/list').reply(200, { qualitygates: [] });
      nock('http://localhost:9000')
        .get('/api/qualitygates/show')
        .query(true)
        .reply(200, { id: 'test', name: 'Test', conditions: [] });
      nock('http://localhost:9000')
        .get('/api/qualitygates/project_status')
        .query(true)
        .reply(200, { projectStatus: { status: 'OK' } });
      nock('http://localhost:9000').get('/api/sources/raw').query(true).reply(200, 'source code');
      nock('http://localhost:9000')
        .get('/api/sources/scm')
        .query(true)
        .reply(200, { component: { key: 'test' }, sources: {} });
      nock('http://localhost:9000')
        .get('/api/hotspots/search')
        .query(true)
        .reply(200, { hotspots: [], paging: { pageIndex: 1, pageSize: 100, total: 0 } });
      nock('http://localhost:9000')
        .get('/api/hotspots/show')
        .query(true)
        .reply(200, {
          key: 'test-hotspot',
          component: 'test',
          project: 'test',
          rule: { key: 'test', name: 'Test' },
          status: 'TO_REVIEW',
          securityCategory: 'test',
          vulnerabilityProbability: 'HIGH',
        });
      nock('http://localhost:9000').post('/api/hotspots/change_status').reply(200);
      // Mock issue resolution endpoints
      nock('http://localhost:9000').post('/api/issues/add_comment').times(8).reply(200, {});
      nock('http://localhost:9000')
        .post('/api/issues/do_transition')
        .times(8) // 4 individual calls + 4 from bulk operations (2 issues each)
        .reply(200, {
          issue: { key: 'test-issue', status: 'RESOLVED' },
          components: [],
          rules: [],
          users: [],
        });
      // Import and call all MCP wrapper functions
      const index = await import('../index.js');
      // Test all wrapper functions
      await index.projectsMcpHandler({});
      await index.metricsMcpHandler({ page: 1, page_size: 10 });
      await index.issuesMcpHandler({ project_key: 'test' });
      await index.healthMcpHandler();
      await index.statusMcpHandler();
      await index.pingMcpHandler();
      await index.componentMeasuresMcpHandler({ component: 'test', metric_keys: ['coverage'] });
      await index.componentsMeasuresMcpHandler({
        component_keys: ['test'],
        metric_keys: ['coverage'],
      });
      await index.measuresHistoryMcpHandler({ component: 'test', metrics: ['coverage'] });
      await index.qualityGatesMcpHandler();
      await index.qualityGateMcpHandler({ id: 'test' });
      await index.qualityGateStatusMcpHandler({ project_key: 'test' });
      await index.sourceCodeMcpHandler({ key: 'test' });
      await index.scmBlameMcpHandler({ key: 'test' });
      await index.hotspotsMcpHandler({ project_key: 'test' });
      await index.hotspotMcpHandler({ hotspot_key: 'test-hotspot' });
      await index.updateHotspotStatusMcpHandler({
        hotspot_key: 'test-hotspot',
        status: 'REVIEWED',
      });
      // Test new issue resolution MCP handlers
      await index.markIssueFalsePositiveMcpHandler({
        issue_key: 'ISSUE-123',
        comment: 'Test comment',
      });
      await index.markIssueWontFixMcpHandler({
        issue_key: 'ISSUE-456',
        comment: 'Test comment',
      });
      await index.markIssuesFalsePositiveMcpHandler({
        issue_keys: ['ISSUE-123', 'ISSUE-124'],
        comment: 'Bulk comment',
      });
      await index.markIssuesWontFixMcpHandler({
        issue_keys: ['ISSUE-456', 'ISSUE-457'],
        comment: 'Bulk comment',
      });

      // Verify handlers exist
      expect(index.projectsHandler).toBeDefined();
      expect(index.metricsHandler).toBeDefined();
    });
  });
});

```
Page 8/8FirstPrevNextLast