/// import fs from 'node:fs'; import os from 'node:os'; import path from 'node:path'; import { afterEach, describe, expect, it } from 'vitest'; import { allToolDefinitions } from '../../src/tools/definitions/shared/all-tool-definitions.js'; const temporaryDirectories: string[] = []; function writeFiles(root: string, files: ReadonlyMap): void { for (const [relativePath, source] of files) { const filePath = path.join(root, relativePath); fs.mkdirSync(path.dirname(filePath), { recursive: true }); fs.writeFileSync(filePath, source); } } function createDefinitionsFixture(): string { const root = fs.mkdtempSync(path.join(os.tmpdir(), 'parameter-audit-schema-')); temporaryDirectories.push(root); const files = new Map([ ['shared/actions.ts', "export const SHARED_ACTIONS = ['shared_action'] as const;\n"], [ 'first/properties.ts', [ 'export const commonProperties = {', " action: { type: 'string', enum: ['first_action'] },", " firstOnly: { type: 'string' }", '};', '' ].join('\n') ], [ 'first/schema.ts', [ "import { commonProperties as importedProperties } from './properties.js';", 'export const inputSchema = {', " type: 'object',", ' properties: { ...importedProperties },', " required: ['action', 'firstOnly']", '};', '' ].join('\n') ], [ 'first/first-tool.ts', [ "import { inputSchema as aliasedSchema } from './schema.js';", 'export const firstToolDefinition = {', " name: 'first_tool',", ' inputSchema: aliasedSchema', '};', '' ].join('\n') ], [ 'second/properties.ts', [ "import { SHARED_ACTIONS as aliasedActions } from '../shared/actions.js';", 'export const commonProperties = {', " action: { type: 'string', enum: [...aliasedActions, 'second_action'] },", " secondOnly: { type: 'number' }", '};', '' ].join('\n') ], [ 'second/schema.ts', [ "import { commonProperties as importedProperties } from './properties.js';", 'export const inputSchema = {', " type: 'object',", ' properties: { ...importedProperties },', " required: ['action']", '};', '' ].join('\n') ], [ 'second/second-tool.ts', [ "import { inputSchema as aliasedSchema } from './schema.js';", 'export const secondToolDefinition = {', " name: 'second_tool',", ' inputSchema: aliasedSchema', '};', '' ].join('\n') ] ]); writeFiles(root, files); return root; } function createActionOverrideFixture(): string { const root = fs.mkdtempSync(path.join(os.tmpdir(), 'parameter-audit-action-override-')); temporaryDirectories.push(root); const files = new Map([ [ 'shared/base-properties.ts', [ 'export const baseProperties = {', " action: { type: 'string', enum: ['spread_action'] },", " sharedOnly: { type: 'boolean' }", '};', '' ].join('\n') ], [ 'shared/enumless-properties.ts', [ 'export const enumlessProperties = {', " action: { type: 'string', description: 'Replacement without enum' }", '};', '' ].join('\n') ], [ 'without-enum/without-enum-tool.ts', [ "import { baseProperties } from '../shared/base-properties.js';", 'export const withoutEnumToolDefinition = {', " name: 'without_enum',", ' inputSchema: {', ' properties: {', ' ...baseProperties,', " action: { type: 'string', description: 'Replacement without enum' }", ' },', " required: ['action']", ' }', '};', '' ].join('\n') ], [ 'nested-spread/nested-spread-tool.ts', [ "import { baseProperties } from '../shared/base-properties.js';", "import { enumlessProperties } from '../shared/enumless-properties.js';", 'export const nestedSpreadToolDefinition = {', " name: 'nested_spread',", ' inputSchema: {', ' properties: {', ' ...baseProperties,', ' ...enumlessProperties', ' },', " required: ['action']", ' }', '};', '' ].join('\n') ], [ 'with-enum/with-enum-tool.ts', [ "import { baseProperties } from '../shared/base-properties.js';", 'export const withEnumToolDefinition = {', " name: 'with_enum',", ' inputSchema: {', ' properties: {', ' ...baseProperties,', " action: { type: 'string', enum: ['replacement_action'] }", ' },', " required: ['action']", ' }', '};', '' ].join('\n') ] ]); writeFiles(root, files); return root; } function stringArray(value: unknown): string[] { if (!Array.isArray(value) || !value.every((entry) => typeof entry === 'string')) { return []; } return [...value].sort(); } function canonicalSchema(tool: (typeof allToolDefinitions)[number]) { const properties = tool.inputSchema['properties']; const propertyRecord = properties && typeof properties === 'object' && !Array.isArray(properties) ? properties as Record : {}; const action = propertyRecord['action']; const actionRecord = action && typeof action === 'object' && !Array.isArray(action) ? action as Record : {}; return { name: tool.name, actions: stringArray(actionRecord['enum']), properties: Object.keys(propertyRecord).sort(), required: stringArray(tool.inputSchema['required']) }; } afterEach(() => { for (const directory of temporaryDirectories.splice(0)) { fs.rmSync(directory, { recursive: true, force: true }); } }); describe('parameter audit schema discovery', () => { it('matches every canonical tool schema exactly', async () => { const { extractToolSchemas } = await import('../parameter-audit-schema.mjs'); const schemas = extractToolSchemas(); expect(schemas).toEqual( allToolDefinitions .map(canonicalSchema) .sort((left, right) => left.name.localeCompare(right.name)) ); }); it('resolves import aliases without conflating same-named helpers in nested modules', async () => { const { extractToolSchemas } = await import('../parameter-audit-schema.mjs'); const definitionsRoot = createDefinitionsFixture(); expect(extractToolSchemas({ definitionsRoot })).toEqual([ { name: 'first_tool', actions: ['first_action'], properties: ['action', 'firstOnly'], required: ['action', 'firstOnly'] }, { name: 'second_tool', actions: ['second_action', 'shared_action'], properties: ['action', 'secondOnly'], required: ['action'] } ]); }); it('applies explicit action overrides after spread-provided enums', async () => { const { extractToolSchemas } = await import('../parameter-audit-schema.mjs'); const definitionsRoot = createActionOverrideFixture(); expect(extractToolSchemas({ definitionsRoot })).toEqual([ { name: 'nested_spread', actions: [], properties: ['action', 'sharedOnly'], required: ['action'] }, { name: 'with_enum', actions: ['replacement_action'], properties: ['action', 'sharedOnly'], required: ['action'] }, { name: 'without_enum', actions: [], properties: ['action', 'sharedOnly'], required: ['action'] } ]); }); });