import { describe, test, expect, beforeEach, afterEach, jest } from '@jest/globals';
import { promises as fs } from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
describe('MCP Audio Inspector Server', () => {
let originalArgv;
let testFilesDir;
beforeEach(() => {
originalArgv = process.argv;
testFilesDir = path.join(__dirname, 'assets');
});
afterEach(() => {
process.argv = originalArgv;
jest.clearAllMocks();
});
describe('CLI Interface', () => {
test('should export main functionality for CLI usage', async () => {
// Test that the main module can be imported without errors
expect(() => {
// Import would happen here in real tests
// For now, just verify the file exists and is readable
}).not.toThrow();
});
test('should handle standalone file analysis via CLI', async () => {
// Create a mock audio file for testing
const testFile = path.join(testFilesDir, 'cli-test.mp3');
await fs.mkdir(testFilesDir, { recursive: true });
await fs.writeFile(testFile, 'fake mp3 content');
try {
// Test would simulate CLI execution here
// For unit tests, we focus on the underlying functionality
expect(testFile).toBeDefined();
} finally {
await fs.unlink(testFile).catch(() => {});
}
});
test('should handle batch analysis via CLI', async () => {
await fs.mkdir(testFilesDir, { recursive: true });
// Create multiple test files
const files = ['test1.mp3', 'test2.wav', 'test3.flac'];
for (const file of files) {
await fs.writeFile(path.join(testFilesDir, file), 'fake audio content');
}
try {
// Test would simulate batch CLI execution here
expect(files.length).toBe(3);
} finally {
for (const file of files) {
await fs.unlink(path.join(testFilesDir, file)).catch(() => {});
}
}
});
});
describe('MCP Tool Definitions', () => {
test('should define correct tool schemas', () => {
// Expected tool definitions
const expectedTools = [
'analyze_audio_file',
'analyze_audio_batch',
'get_supported_formats'
];
// Test that tools are properly defined (structure validation)
expectedTools.forEach(toolName => {
expect(typeof toolName).toBe('string');
expect(toolName.length).toBeGreaterThan(0);
});
});
test('analyze_audio_file tool should have correct schema', () => {
const expectedSchema = {
type: 'object',
properties: {
filePath: {
type: 'string',
description: 'Path to the audio file to analyze'
},
includeGameAnalysis: {
type: 'boolean',
description: 'Include game-specific audio analysis',
default: true
}
},
required: ['filePath']
};
// Validate schema structure
expect(expectedSchema.type).toBe('object');
expect(expectedSchema.properties.filePath.type).toBe('string');
expect(expectedSchema.required).toContain('filePath');
});
test('analyze_audio_batch tool should have correct schema', () => {
const expectedSchema = {
type: 'object',
properties: {
directoryPath: {
type: 'string',
description: 'Path to directory containing audio files'
},
recursive: {
type: 'boolean',
description: 'Search subdirectories recursively',
default: false
},
includeGameAnalysis: {
type: 'boolean',
description: 'Include game-specific audio analysis',
default: true
}
},
required: ['directoryPath']
};
// Validate schema structure
expect(expectedSchema.type).toBe('object');
expect(expectedSchema.properties.directoryPath.type).toBe('string');
expect(expectedSchema.required).toContain('directoryPath');
});
});
describe('MCP Server Functionality', () => {
test('should handle tool execution errors gracefully', () => {
// Mock tool execution with error
const mockError = new Error('Test error');
expect(() => {
throw new Error(`Tool execution failed: ${mockError.message}`);
}).toThrow('Tool execution failed: Test error');
});
test('should validate tool arguments', () => {
// Test argument validation
const validArgs = {
filePath: '/path/to/audio.mp3',
includeGameAnalysis: true
};
const invalidArgs = {
// missing filePath
includeGameAnalysis: true
};
expect(validArgs.filePath).toBeDefined();
expect(invalidArgs.filePath).toBeUndefined();
});
test('should return properly formatted responses', () => {
// Test response format
const mockResult = {
file: { name: 'test.mp3' },
format: { container: 'MP3' },
tags: { title: 'Test' }
};
const expectedResponse = {
content: [
{
type: 'text',
text: JSON.stringify(mockResult, null, 2)
}
]
};
expect(expectedResponse.content).toHaveLength(1);
expect(expectedResponse.content[0].type).toBe('text');
expect(typeof expectedResponse.content[0].text).toBe('string');
});
});
describe('Error Handling and Edge Cases', () => {
test('should handle unknown tool names', () => {
const unknownToolName = 'unknown_tool';
expect(() => {
throw new Error(`Unknown tool: ${unknownToolName}`);
}).toThrow(`Unknown tool: ${unknownToolName}`);
});
test('should handle server initialization errors', () => {
// Test server error handling
const mockError = new Error('Server initialization failed');
expect(() => {
console.error('Server error:', mockError);
// process.exit(1) would be called here
}).not.toThrow();
});
test('should handle missing required arguments', () => {
const testCases = [
{ name: 'analyze_audio_file', args: {} }, // missing filePath
{ name: 'analyze_audio_batch', args: {} }, // missing directoryPath
];
testCases.forEach(testCase => {
expect(testCase.args.filePath || testCase.args.directoryPath).toBeFalsy();
});
});
test('should handle file system errors', async () => {
const nonExistentPath = '/path/that/does/not/exist';
try {
await fs.access(nonExistentPath);
// Should not reach here
expect(false).toBe(true);
} catch (error) {
expect(error).toBeDefined();
expect(error.code).toBe('ENOENT');
}
});
});
describe('Integration Points', () => {
test('should properly integrate with AudioInspector', () => {
// Test that the server correctly uses AudioInspector methods
const expectedMethods = [
'analyzeFile',
'analyzeBatch',
'getSupportedFormats'
];
expectedMethods.forEach(method => {
expect(typeof method).toBe('string');
});
});
test('should handle stdio transport correctly', () => {
// Test stdio transport setup
// This would test the actual server connection in a real scenario
expect(true).toBe(true); // Placeholder for transport tests
});
test('should handle different output formats', () => {
const formats = ['json', 'yaml', 'csv'];
const defaultFormat = 'json';
expect(formats).toContain(defaultFormat);
expect(formats.length).toBe(3);
});
});
describe('Performance and Scalability', () => {
test('should handle large batch operations', async () => {
// Test batch processing performance considerations
const largeFileList = Array.from({ length: 100 }, (_, i) => `file${i}.mp3`);
expect(largeFileList.length).toBe(100);
expect(largeFileList[0]).toBe('file0.mp3');
expect(largeFileList[99]).toBe('file99.mp3');
});
test('should handle concurrent requests appropriately', () => {
// Test concurrent processing considerations
const mockRequests = Array.from({ length: 5 }, (_, i) => ({
id: i,
tool: 'analyze_audio_file',
args: { filePath: `test${i}.mp3` }
}));
expect(mockRequests.length).toBe(5);
mockRequests.forEach((request, index) => {
expect(request.id).toBe(index);
});
});
});
describe('Configuration and Setup', () => {
test('should handle server configuration correctly', () => {
const expectedConfig = {
name: 'mcp-audio-inspector',
version: '1.0.0',
capabilities: {
tools: {}
}
};
expect(expectedConfig.name).toBe('mcp-audio-inspector');
expect(expectedConfig.version).toBe('1.0.0');
expect(expectedConfig.capabilities).toHaveProperty('tools');
});
test('should validate environment setup', () => {
// Test Node.js version requirements
const nodeVersion = process.version;
const majorVersion = parseInt(nodeVersion.slice(1).split('.')[0]);
expect(majorVersion).toBeGreaterThanOrEqual(18);
});
});
});