/**
* @fileoverview Tests for tool definitions and schemas
*/
import { z } from 'zod';
import {
projectsToolSchema,
qualityMetricsToolSchema,
updateMetricThresholdToolSchema,
updateMetricSettingToolSchema,
complianceReportToolSchema,
projectIssuesToolSchema,
runsToolSchema,
runToolSchema,
recentRunIssuesToolSchema,
dependencyVulnerabilitiesToolSchema,
toolSchemas,
} from '../../server/tool-definitions.js';
// Helper function to parse input with ZodRawShape
function parseInput(schema: { inputSchema?: z.ZodRawShape | z.ZodSchema }, input: unknown) {
if (!schema.inputSchema) return undefined;
// Check if it's already a ZodSchema (has safeParse method)
if ('safeParse' in schema.inputSchema) {
return (schema.inputSchema as z.ZodSchema).safeParse(input);
}
// Otherwise, wrap it in z.object()
return z.object(schema.inputSchema as z.ZodRawShape).safeParse(input);
}
describe('Tool Definitions', () => {
describe('projectsToolSchema', () => {
it('should have correct name and description', () => {
expect(projectsToolSchema.name).toBe('projects');
expect(projectsToolSchema.description).toContain('List all available DeepSource projects');
});
it('should have empty input schema', () => {
expect(projectsToolSchema.inputSchema).toBeDefined();
const result = parseInput(projectsToolSchema, {});
expect(result?.success).toBe(true);
expect(result?.data).toEqual({});
});
it('should have output schema', () => {
expect(projectsToolSchema.outputSchema).toBeDefined();
});
});
describe('qualityMetricsToolSchema', () => {
it('should have correct name and description', () => {
expect(qualityMetricsToolSchema.name).toBe('quality_metrics');
expect(qualityMetricsToolSchema.description).toContain('quality metrics');
});
it('should validate valid input', () => {
const validInput = {
projectKey: 'test-project',
shortcodeIn: ['LCV', 'BCV'],
};
const result = parseInput(qualityMetricsToolSchema, validInput);
expect(result?.success).toBe(true);
});
it('should validate input with only projectKey', () => {
const validInput = {
projectKey: 'test-project',
};
const result = parseInput(qualityMetricsToolSchema, validInput);
expect(result?.success).toBe(true);
});
it('should reject input without projectKey', () => {
const invalidInput = {
shortcodeIn: ['LCV'],
};
const result = parseInput(qualityMetricsToolSchema, invalidInput);
expect(result?.success).toBe(false);
});
it('should reject invalid metric shortcodes', () => {
const invalidInput = {
projectKey: 'test-project',
shortcodeIn: ['INVALID'],
};
const result = parseInput(qualityMetricsToolSchema, invalidInput);
expect(result?.success).toBe(false);
});
});
describe('updateMetricThresholdToolSchema', () => {
it('should have correct name and description', () => {
expect(updateMetricThresholdToolSchema.name).toBe('update_metric_threshold');
expect(updateMetricThresholdToolSchema.description).toContain('Update the threshold');
});
it('should validate valid input with threshold', () => {
const validInput = {
projectKey: 'test-project',
repositoryId: 'repo-123',
metricShortcode: 'LCV',
metricKey: 'line-coverage',
thresholdValue: 80,
};
const result = parseInput(updateMetricThresholdToolSchema, validInput);
expect(result?.success).toBe(true);
});
it('should validate input with null threshold', () => {
const validInput = {
projectKey: 'test-project',
repositoryId: 'repo-123',
metricShortcode: 'TCV',
metricKey: 'test-coverage',
thresholdValue: null,
};
const result = parseInput(updateMetricThresholdToolSchema, validInput);
expect(result?.success).toBe(true);
});
it('should reject missing required fields', () => {
const invalidInput = {
projectKey: 'test-project',
metricShortcode: 'LCV',
};
const result = parseInput(updateMetricThresholdToolSchema, invalidInput);
expect(result?.success).toBe(false);
});
});
describe('updateMetricSettingToolSchema', () => {
it('should have correct name and description', () => {
expect(updateMetricSettingToolSchema.name).toBe('update_metric_setting');
expect(updateMetricSettingToolSchema.description).toContain('Update the settings');
});
it('should validate valid input', () => {
const validInput = {
projectKey: 'test-project',
repositoryId: 'repo-123',
metricShortcode: 'DCV',
isReported: true,
isThresholdEnforced: false,
};
const result = parseInput(updateMetricSettingToolSchema, validInput);
expect(result?.success).toBe(true);
});
it('should reject non-boolean values', () => {
const invalidInput = {
projectKey: 'test-project',
repositoryId: 'repo-123',
metricShortcode: 'DCV',
isReported: 'yes',
isThresholdEnforced: 0,
};
const result = parseInput(updateMetricSettingToolSchema, invalidInput);
expect(result?.success).toBe(false);
});
});
describe('complianceReportToolSchema', () => {
it('should have correct name and description', () => {
expect(complianceReportToolSchema.name).toBe('compliance_report');
expect(complianceReportToolSchema.description).toContain('security compliance reports');
});
it('should validate valid report types', () => {
const validTypes = [
'OWASP_TOP_10',
'SANS_TOP_25',
'MISRA_C',
'CODE_COVERAGE',
'CODE_HEALTH_TREND',
'ISSUE_DISTRIBUTION',
'ISSUES_PREVENTED',
'ISSUES_AUTOFIXED',
];
validTypes.forEach((reportType) => {
const input = {
projectKey: 'test-project',
reportType,
};
const result = parseInput(complianceReportToolSchema, input);
expect(result?.success).toBe(true);
});
});
it('should reject invalid report type', () => {
const invalidInput = {
projectKey: 'test-project',
reportType: 'INVALID_TYPE',
};
const result = parseInput(complianceReportToolSchema, invalidInput);
expect(result?.success).toBe(false);
});
});
describe('projectIssuesToolSchema', () => {
it('should have correct name and description', () => {
expect(projectIssuesToolSchema.name).toBe('project_issues');
expect(projectIssuesToolSchema.description).toContain('Get issues from a DeepSource project');
});
it('should validate input with all optional fields', () => {
const validInput = {
projectKey: 'test-project',
analyzerIn: ['python', 'javascript'],
tags: ['security', 'performance'],
path: 'src/main.py',
first: 10,
after: 'cursor123',
};
const result = parseInput(projectIssuesToolSchema, validInput);
expect(result?.success).toBe(true);
});
it('should validate input with only required fields', () => {
const validInput = {
projectKey: 'test-project',
};
const result = parseInput(projectIssuesToolSchema, validInput);
expect(result?.success).toBe(true);
});
it('should accept negative pagination values (schema limitation)', () => {
const invalidInput = {
projectKey: 'test-project',
first: -5,
};
const result = parseInput(projectIssuesToolSchema, invalidInput);
// Note: The schema uses z.number() without validation, so negative values pass
// This is a known limitation in the schema
expect(result?.success).toBe(true);
});
});
describe('runsToolSchema', () => {
it('should have correct name and description', () => {
expect(runsToolSchema.name).toBe('runs');
expect(runsToolSchema.description).toContain('List analysis runs');
});
it('should validate input with analyzer filter', () => {
const validInput = {
projectKey: 'test-project',
analyzerIn: ['test-coverage', 'javascript'],
last: 20,
before: 'cursor456',
};
const result = parseInput(runsToolSchema, validInput);
expect(result?.success).toBe(true);
});
it('should validate both forward and backward pagination', () => {
const forwardInput = {
projectKey: 'test-project',
first: 10,
after: 'cursorA',
};
const backwardInput = {
projectKey: 'test-project',
last: 10,
before: 'cursorB',
};
expect(parseInput(runsToolSchema, forwardInput)?.success).toBe(true);
expect(parseInput(runsToolSchema, backwardInput)?.success).toBe(true);
});
});
describe('runToolSchema', () => {
it('should have correct name and description', () => {
expect(runToolSchema.name).toBe('run');
expect(runToolSchema.description).toBe(
'Get a specific analysis run by its runUid or commitOid'
);
});
it('should validate input with runUid', () => {
const validInput = {
projectKey: 'test-project',
runIdentifier: 'run-uid-123',
};
const result = parseInput(runToolSchema, validInput);
expect(result?.success).toBe(true);
});
it('should validate input with commitOid', () => {
const validInput = {
projectKey: 'test-project',
runIdentifier: 'abc123def456',
isCommitOid: true,
};
const result = parseInput(runToolSchema, validInput);
expect(result?.success).toBe(true);
});
it('should validate input with isCommitOid false', () => {
const validInput = {
projectKey: 'test-project',
runIdentifier: 'run-uid-456',
isCommitOid: false,
};
const result = parseInput(runToolSchema, validInput);
expect(result?.success).toBe(true);
});
});
describe('recentRunIssuesToolSchema', () => {
it('should have correct name and description', () => {
expect(recentRunIssuesToolSchema.name).toBe('recent_run_issues');
expect(recentRunIssuesToolSchema.description).toContain('most recent analysis run');
});
it('should validate input with branch name', () => {
const validInput = {
projectKey: 'test-project',
branchName: 'feature/new-feature',
first: 50,
};
const result = parseInput(recentRunIssuesToolSchema, validInput);
expect(result?.success).toBe(true);
});
it('should require both projectKey and branchName', () => {
const missingBranch = {
projectKey: 'test-project',
};
const missingProject = {
branchName: 'main',
};
expect(parseInput(recentRunIssuesToolSchema, missingBranch)?.success).toBe(false);
expect(parseInput(recentRunIssuesToolSchema, missingProject)?.success).toBe(false);
});
});
describe('dependencyVulnerabilitiesToolSchema', () => {
it('should have correct name and description', () => {
expect(dependencyVulnerabilitiesToolSchema.name).toBe('dependency_vulnerabilities');
expect(dependencyVulnerabilitiesToolSchema.description).toContain(
'dependency vulnerabilities'
);
});
it('should validate input with pagination', () => {
const validInput = {
projectKey: 'test-project',
first: 25,
after: 'vuln-cursor',
};
const result = parseInput(dependencyVulnerabilitiesToolSchema, validInput);
expect(result?.success).toBe(true);
});
it('should validate input with only projectKey', () => {
const validInput = {
projectKey: 'test-project',
};
const result = parseInput(dependencyVulnerabilitiesToolSchema, validInput);
expect(result?.success).toBe(true);
});
});
describe('toolSchemas array', () => {
it('should contain all 10 tool schemas', () => {
expect(toolSchemas).toHaveLength(10);
});
it('should contain all defined schemas', () => {
const toolNames = toolSchemas.map((schema) => schema.name);
expect(toolNames).toContain('projects');
expect(toolNames).toContain('quality_metrics');
expect(toolNames).toContain('update_metric_threshold');
expect(toolNames).toContain('update_metric_setting');
expect(toolNames).toContain('compliance_report');
expect(toolNames).toContain('project_issues');
expect(toolNames).toContain('runs');
expect(toolNames).toContain('run');
expect(toolNames).toContain('recent_run_issues');
expect(toolNames).toContain('dependency_vulnerabilities');
});
it('should have valid schemas for all tools', () => {
toolSchemas.forEach((schema) => {
expect(schema.name).toBeTruthy();
expect(schema.description).toBeTruthy();
expect(typeof schema.name).toBe('string');
expect(typeof schema.description).toBe('string');
});
});
});
describe('Schema edge cases', () => {
it('should handle empty arrays in filters', () => {
const input = {
projectKey: 'test-project',
analyzerIn: [],
tags: [],
};
const result = parseInput(projectIssuesToolSchema, input);
expect(result?.success).toBe(true);
});
it('should handle very long project keys', () => {
// projectsToolSchema has empty input schema
expect(projectsToolSchema.inputSchema).toBeDefined();
const result = parseInput(projectsToolSchema, {});
expect(result?.success).toBe(true);
expect(result?.data).toEqual({});
});
it('should handle special characters in strings', () => {
const input = {
projectKey: 'test-project-@#$%',
path: '/path/with spaces/and-special!@#$%.py',
};
const result = parseInput(projectIssuesToolSchema, input);
expect(result?.success).toBe(true);
});
it('should validate metric shortcodes strictly', () => {
const validShortcodes = ['LCV', 'BCV', 'DCV', 'DDP', 'SCV', 'TCV', 'CMP'];
validShortcodes.forEach((code) => {
const input = {
projectKey: 'test',
shortcodeIn: [code],
};
const result = parseInput(qualityMetricsToolSchema, input);
expect(result?.success).toBe(true);
});
});
});
});