Skip to main content
Glama
sound.test.tsβ€’5.89 kB
import { describe, it, expect, vi, beforeEach } from 'vitest'; import { spawn } from 'child_process'; import { EventEmitter } from 'events'; // Mock child_process vi.mock('child_process'); const mockSpawn = vi.mocked(spawn); // Mock fs/promises for file tests vi.mock('node:fs/promises', () => ({ stat: vi.fn(), })); import { stat } from 'node:fs/promises'; const mockStat = vi.mocked(stat); // We need to import these functions - let's create a testable version // Since the main file doesn't export functions, we'll test the core logic const ALLOWED_SYSTEM_SOUNDS = new Set([ 'Basso', 'Blow', 'Bottle', 'Frog', 'Funk', 'Glass', 'Hero', 'Morse', 'Ping', 'Pop', 'Purr', 'Sosumi', 'Submarine', 'Tink' ]); const MAX_TTS_TEXT_LENGTH = 1000; // Mock process for testing class MockChildProcess extends EventEmitter { stdout = new EventEmitter(); stderr = new EventEmitter(); once(event: string, callback: (...args: unknown[]) => void): this { super.once(event, callback); return this; } } describe('Sound System', () => { beforeEach(() => { vi.clearAllMocks(); }); describe('System Sound Validation', () => { it('should allow valid system sounds', () => { expect(ALLOWED_SYSTEM_SOUNDS.has('Glass')).toBe(true); expect(ALLOWED_SYSTEM_SOUNDS.has('Ping')).toBe(true); expect(ALLOWED_SYSTEM_SOUNDS.has('Sosumi')).toBe(true); }); it('should reject invalid system sounds', () => { expect(ALLOWED_SYSTEM_SOUNDS.has('InvalidSound')).toBe(false); expect(ALLOWED_SYSTEM_SOUNDS.has('')).toBe(false); }); }); describe('TTS Text Validation', () => { it('should allow text within length limit', () => { const shortText = 'Hello world'; expect(shortText.length <= MAX_TTS_TEXT_LENGTH).toBe(true); }); it('should reject text exceeding length limit', () => { const longText = 'a'.repeat(MAX_TTS_TEXT_LENGTH + 1); expect(longText.length > MAX_TTS_TEXT_LENGTH).toBe(true); }); }); describe('Sound Playback Simulation', () => { it('should spawn afplay for system sounds', () => { const mockProcess = new MockChildProcess(); mockSpawn.mockReturnValue(mockProcess as unknown as ReturnType<typeof spawn>); // Simulate system sound playback const soundName = 'Glass'; const expectedArgs = [`/System/Library/Sounds/${soundName}.aiff`]; // This would be called in the actual implementation spawn('afplay', expectedArgs); expect(mockSpawn).toHaveBeenCalledWith('afplay', expectedArgs); }); it('should spawn say command for TTS', () => { const mockProcess = new MockChildProcess(); mockSpawn.mockReturnValue(mockProcess as unknown as ReturnType<typeof spawn>); const text = 'Hello world'; const voice = 'Samantha'; // This would be called in the actual implementation spawn('say', ['-v', voice, text]); expect(mockSpawn).toHaveBeenCalledWith('say', ['-v', voice, text]); }); it('should handle successful sound playback', () => { const mockProcess = new MockChildProcess(); mockSpawn.mockReturnValue(mockProcess as unknown as ReturnType<typeof spawn>); spawn('afplay', ['/System/Library/Sounds/Glass.aiff']); // Simulate successful completion setTimeout(() => { mockProcess.emit('close', 0); }, 0); expect(mockSpawn).toHaveBeenCalled(); }); it('should handle failed sound playback', () => { const mockProcess = new MockChildProcess(); mockSpawn.mockReturnValue(mockProcess as unknown as ReturnType<typeof spawn>); spawn('afplay', ['/System/Library/Sounds/Glass.aiff']); // Simulate failure setTimeout(() => { mockProcess.emit('close', 1); }, 0); expect(mockSpawn).toHaveBeenCalled(); }); }); describe('File Path Validation', () => { it('should validate absolute paths', async () => { const absolutePath = '/absolute/path/to/file.mp3'; mockStat.mockResolvedValue({ isFile: () => true } as Awaited<ReturnType<typeof stat>>); await stat(absolutePath); expect(mockStat).toHaveBeenCalledWith(absolutePath); }); it('should handle file stat errors', async () => { const invalidPath = '/invalid/path.mp3'; mockStat.mockRejectedValue(new Error('File not found')); try { await stat(invalidPath); } catch (error) { expect(error).toBeInstanceOf(Error); } }); }); describe('Input Validation', () => { it('should validate sound type parameter', () => { const validTypes = ['system', 'tts', 'file']; const invalidType = 'invalid'; expect(validTypes.includes('system')).toBe(true); expect(validTypes.includes('tts')).toBe(true); expect(validTypes.includes('file')).toBe(true); expect(validTypes.includes(invalidType)).toBe(false); }); it('should require name for system type', () => { const systemSoundWithName = { type: 'system', name: 'Glass' }; const systemSoundWithoutName = { type: 'system' }; expect(systemSoundWithName.name).toBeDefined(); expect((systemSoundWithoutName as Record<string, unknown>).name).toBeUndefined(); }); it('should require text for tts type', () => { const ttsWithText = { type: 'tts', text: 'Hello' }; const ttsWithoutText = { type: 'tts' }; expect(ttsWithText.text).toBeDefined(); expect((ttsWithoutText as Record<string, unknown>).text).toBeUndefined(); }); it('should require path for file type', () => { const fileWithPath = { type: 'file', path: '/path/to/file.mp3' }; const fileWithoutPath = { type: 'file' }; expect(fileWithPath.path).toBeDefined(); expect((fileWithoutPath as Record<string, unknown>).path).toBeUndefined(); }); }); });

Latest Blog Posts

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/nocoo/mcp-make-sound'

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