Skip to main content
Glama

Decent-Sampler Drums MCP Server

wav-analysis.test.ts8.84 kB
import { jest } from '@jest/globals'; import { McpError } from '@modelcontextprotocol/sdk/types.js'; import { ErrorCode } from '@modelcontextprotocol/sdk/types.js'; import path from 'path'; // Mock fs module jest.mock('fs', () => ({ promises: { readFile: jest.fn() } })); // Import fs mock for typing import { promises as fs } from 'fs'; const mockReadFile = fs.readFile as jest.MockedFunction<typeof fs.readFile>; // Import our function to test import { analyzeWavFile } from '../src/wav-analysis.js'; // Helper to create WAV header buffer function createWavBuffer({ numChannels = 2, sampleRate = 44100, bitsPerSample = 24, dataSize = 1000 } = {}) { const buffer = Buffer.alloc(44 + dataSize); // Standard WAV header + data // RIFF header buffer.write('RIFF', 0); buffer.writeUInt32LE(36 + dataSize, 4); // File size - 8 buffer.write('WAVE', 8); // fmt chunk buffer.write('fmt ', 12); buffer.writeUInt32LE(16, 16); // fmt chunk size buffer.writeUInt16LE(1, 20); // PCM format buffer.writeUInt16LE(numChannels, 22); buffer.writeUInt32LE(sampleRate, 24); buffer.writeUInt32LE((sampleRate * numChannels * bitsPerSample) / 8, 28); // Byte rate buffer.writeUInt16LE((numChannels * bitsPerSample) / 8, 32); // Block align buffer.writeUInt16LE(bitsPerSample, 34); // data chunk buffer.write('data', 36); buffer.writeUInt32LE(dataSize, 40); return buffer; } describe('WAV File Analysis', () => { // Test file paths const validWavPath = path.join(__dirname, 'fixtures', 'valid.wav'); const corruptedWavPath = path.join(__dirname, 'fixtures', 'corrupted.wav'); const invalidFormatWavPath = path.join(__dirname, 'fixtures', 'invalid_format.wav'); const missingChunksWavPath = path.join(__dirname, 'fixtures', 'missing_chunks.wav'); const invalidSizesWavPath = path.join(__dirname, 'fixtures', 'invalid_sizes.wav'); beforeEach(() => { jest.clearAllMocks(); }); describe('Success Cases', () => { it('should successfully analyze a valid WAV file', async () => { const wavBuffer = createWavBuffer({ numChannels: 2, sampleRate: 44100, bitsPerSample: 24, dataSize: 396900 // 1.5 seconds of audio (44100 * 1.5 * 2 channels * 3 bytes per sample) }); mockReadFile.mockResolvedValue(wavBuffer); const result = await analyzeWavFile(validWavPath); expect(result).toEqual({ path: validWavPath, sampleLength: 66150, // dataSize / (channels * bytesPerSample) sampleRate: 44100, channels: 2, bitDepth: 24 }); }); it('should handle different bit depths', async () => { const wavBuffer = createWavBuffer({ numChannels: 1, sampleRate: 44100, bitsPerSample: 16, dataSize: 88200 // 1 second of audio }); mockReadFile.mockResolvedValue(wavBuffer); const result = await analyzeWavFile(validWavPath); expect(result).toEqual({ path: validWavPath, sampleLength: 44100, sampleRate: 44100, channels: 1, bitDepth: 16 }); }); it('should handle high sample rates', async () => { const wavBuffer = createWavBuffer({ numChannels: 2, sampleRate: 96000, bitsPerSample: 24, dataSize: 576000 // 1 second of audio }); mockReadFile.mockResolvedValue(wavBuffer); const result = await analyzeWavFile(validWavPath); expect(result).toEqual({ path: validWavPath, sampleLength: 96000, sampleRate: 96000, channels: 2, bitDepth: 24 }); }); }); describe('Error Cases', () => { it('should handle JUNK chunk error case (real world error)', async () => { const buffer = Buffer.alloc(44); buffer.write('RIFF', 0); buffer.writeUInt32LE(36, 4); buffer.write('WAVE', 8); buffer.write('JUNK', 12); // JUNK instead of fmt mockReadFile.mockResolvedValue(buffer); await expect(analyzeWavFile(corruptedWavPath)) .rejects .toThrow(McpError); try { await analyzeWavFile(corruptedWavPath); } catch (error) { expect(error).toBeInstanceOf(McpError); expect(error).toHaveProperty('code', ErrorCode.InvalidRequest); expect((error as McpError).message).toContain('Missing fmt chunk'); } }); it('should handle invalid audio format', async () => { const buffer = createWavBuffer(); buffer.writeUInt16LE(2, 20); // Non-PCM format mockReadFile.mockResolvedValue(buffer); await expect(analyzeWavFile(invalidFormatWavPath)) .rejects .toThrow(McpError); try { await analyzeWavFile(invalidFormatWavPath); } catch (error) { expect(error).toBeInstanceOf(McpError); expect(error).toHaveProperty('code', ErrorCode.InvalidRequest); expect((error as McpError).message).toContain('Unsupported audio format: 2'); } }); it('should handle missing data chunk', async () => { const buffer = Buffer.alloc(44); // Minimum size but no data chunk buffer.write('RIFF', 0); buffer.writeUInt32LE(36, 4); buffer.write('WAVE', 8); buffer.write('fmt ', 12); buffer.writeUInt32LE(16, 16); // fmt chunk size buffer.writeUInt16LE(1, 20); // PCM format buffer.writeUInt16LE(2, 22); // channels buffer.writeUInt32LE(44100, 24); // sample rate buffer.writeUInt32LE(264600, 28); // byte rate buffer.writeUInt16LE(6, 32); // block align buffer.writeUInt16LE(24, 34); // bits per sample mockReadFile.mockResolvedValue(buffer); await expect(analyzeWavFile(missingChunksWavPath)) .rejects .toThrow(McpError); try { await analyzeWavFile(missingChunksWavPath); } catch (error) { expect(error).toBeInstanceOf(McpError); expect(error).toHaveProperty('code', ErrorCode.InvalidRequest); expect((error as McpError).message).toContain('Missing data chunk'); } }); it('should handle file too small', async () => { const buffer = Buffer.alloc(40); // Less than minimum WAV header size mockReadFile.mockResolvedValue(buffer); await expect(analyzeWavFile(invalidSizesWavPath)) .rejects .toThrow(McpError); try { await analyzeWavFile(invalidSizesWavPath); } catch (error) { expect(error).toBeInstanceOf(McpError); expect(error).toHaveProperty('code', ErrorCode.InvalidRequest); expect((error as McpError).message).toContain('File too small to be a valid WAV file'); } }); it('should handle missing RIFF header', async () => { const buffer = createWavBuffer(); buffer.write('JUNK', 0); // Invalid RIFF header mockReadFile.mockResolvedValue(buffer); await expect(analyzeWavFile(validWavPath)) .rejects .toThrow(McpError); try { await analyzeWavFile(validWavPath); } catch (error) { expect(error).toBeInstanceOf(McpError); expect(error).toHaveProperty('code', ErrorCode.InvalidRequest); expect((error as McpError).message).toContain('Missing RIFF header'); } }); }); describe('Edge Cases', () => { it('should handle extremely short data chunks', async () => { const wavBuffer = createWavBuffer({ dataSize: 4 // Extremely small data chunk }); mockReadFile.mockResolvedValue(wavBuffer); const result = await analyzeWavFile(validWavPath); expect(result.sampleLength).toBe(1); // 4 bytes / (2 channels * 3 bytes per sample) }); it('should handle extremely large data chunks', async () => { const wavBuffer = createWavBuffer({ dataSize: 7200 * 44100 * 6 // 2 hours of stereo 24-bit audio }); mockReadFile.mockResolvedValue(wavBuffer); const result = await analyzeWavFile(validWavPath); expect(result.sampleLength).toBe(317520000); // 7200 * 44100 }); it('should handle mono files', async () => { const wavBuffer = createWavBuffer({ numChannels: 1, dataSize: 44100 * 3 // 1 second of mono 24-bit audio }); mockReadFile.mockResolvedValue(wavBuffer); const result = await analyzeWavFile(validWavPath); expect(result.channels).toBe(1); expect(result.sampleLength).toBe(44100); }); it('should handle multi-channel files', async () => { const wavBuffer = createWavBuffer({ numChannels: 8, dataSize: 44100 * 24 // 1 second of 8-channel 24-bit audio }); mockReadFile.mockResolvedValue(wavBuffer); const result = await analyzeWavFile(validWavPath); expect(result.channels).toBe(8); expect(result.sampleLength).toBe(44100); }); }); });

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/dandeliongold/mcp-decent-sampler-drums'

If you have feedback or need assistance with the MCP directory API, please join our Discord server