Skip to main content
Glama
integration.test.js15.3 kB
import { describe, test, expect, beforeAll, afterAll, beforeEach, afterEach } from '@jest/globals'; import { AudioInspector } from '../lib/inspector.js'; import { promises as fs } from 'fs'; import path from 'path'; import { fileURLToPath } from 'url'; const __dirname = path.dirname(fileURLToPath(import.meta.url)); describe('Audio Inspector Integration Tests', () => { let inspector; let testAssetsDir; let tempTestDir; beforeAll(async () => { testAssetsDir = path.join(__dirname, 'assets'); tempTestDir = path.join(__dirname, 'temp-integration'); await fs.mkdir(testAssetsDir, { recursive: true }); await fs.mkdir(tempTestDir, { recursive: true }); }); afterAll(async () => { await fs.rmdir(tempTestDir, { recursive: true }).catch(() => {}); }); beforeEach(() => { inspector = new AudioInspector(); }); afterEach(async () => { // Clean up any test files created during tests const files = await fs.readdir(tempTestDir).catch(() => []); for (const file of files) { await fs.unlink(path.join(tempTestDir, file)).catch(() => {}); } }); describe('End-to-End File Analysis Workflow', () => { test('should complete full analysis workflow for various audio formats', async () => { const testFiles = [ { name: 'test-mp3.mp3', content: 'ID3\x03\x00\x00\x00', format: 'MP3' }, { name: 'test-wav.wav', content: 'RIFF\x24\x08\x00\x00WAVE', format: 'WAV' }, { name: 'test-flac.flac', content: 'fLaC\x00\x00\x00\x22', format: 'FLAC' }, { name: 'test-ogg.ogg', content: 'OggS\x00\x02\x00\x00', format: 'OGG' } ]; const results = []; for (const testFile of testFiles) { const filePath = path.join(tempTestDir, testFile.name); await fs.writeFile(filePath, testFile.content); const result = await inspector.analyzeFile(filePath); results.push(result); // Verify result structure expect(result).toHaveProperty('file'); expect(result).toHaveProperty('format'); expect(result).toHaveProperty('tags'); expect(result.file.name).toBe(testFile.name); expect(result.file.path).toContain(testFile.name); } expect(results.length).toBe(testFiles.length); }); test('should handle mixed file types in batch analysis', async () => { // Create a mix of audio files and non-audio files const files = [ { name: 'audio1.mp3', content: 'fake mp3', isAudio: true }, { name: 'audio2.wav', content: 'fake wav', isAudio: true }, { name: 'document.txt', content: 'text content', isAudio: false }, { name: 'image.jpg', content: 'fake jpg', isAudio: false }, { name: 'audio3.flac', content: 'fake flac', isAudio: true } ]; for (const file of files) { await fs.writeFile(path.join(tempTestDir, file.name), file.content); } const batchResult = await inspector.analyzeBatch(tempTestDir); // Should only process audio files const expectedAudioFiles = files.filter(f => f.isAudio).length; expect(batchResult.summary.totalFiles).toBe(expectedAudioFiles); expect(batchResult.results.length).toBe(expectedAudioFiles); // Verify all results are for audio files batchResult.results.forEach(result => { const extension = path.extname(result.file.name); expect(inspector.supportedFormats).toContain(extension); }); }); test('should maintain consistency across different analysis methods', async () => { const testFile = path.join(tempTestDir, 'consistency-test.mp3'); await fs.writeFile(testFile, 'fake mp3 content for consistency test'); // Analyze same file using different methods const singleResult = await inspector.analyzeFile(testFile); const batchResult = await inspector.analyzeBatch(tempTestDir); // Find the same file in batch results const batchFileResult = batchResult.results.find( r => r.file.name === 'consistency-test.mp3' ); expect(batchFileResult).toBeDefined(); // Compare key properties (accounting for potential timing differences) expect(singleResult.file.name).toBe(batchFileResult.file.name); expect(singleResult.file.size).toBe(batchFileResult.file.size); expect(singleResult.format.container).toBe(batchFileResult.format.container); }); }); describe('Error Recovery and Resilience', () => { test('should recover from individual file failures in batch processing', async () => { const files = [ { name: 'good1.mp3', content: 'fake mp3 content' }, { name: 'corrupted.mp3', content: '\x00\x00\x00\x00' }, // Corrupted { name: 'good2.wav', content: 'fake wav content' }, { name: 'empty.flac', content: '' }, // Empty file { name: 'good3.ogg', content: 'fake ogg content' } ]; for (const file of files) { await fs.writeFile(path.join(tempTestDir, file.name), file.content); } const batchResult = await inspector.analyzeBatch(tempTestDir); // Should process all files, some may have errors expect(batchResult.summary.totalFiles).toBe(files.length); expect(batchResult.results.length).toBe(files.length); expect(batchResult.summary.successful + batchResult.summary.failed).toBe(files.length); // Should have some successful results expect(batchResult.summary.successful).toBeGreaterThan(0); }); test('should handle permission errors gracefully', async () => { const testFile = path.join(tempTestDir, 'permission-test.mp3'); await fs.writeFile(testFile, 'test content'); // Try to simulate permission error (OS-dependent) try { await fs.chmod(testFile, 0o000); // Remove all permissions const result = await inspector.analyzeFile(testFile); // Should handle permission error gracefully if (result.error) { expect(result.message).toBeDefined(); } } finally { // Restore permissions for cleanup await fs.chmod(testFile, 0o644).catch(() => {}); } }); test('should handle extremely large file paths', async () => { // Create a deeply nested directory structure let deepPath = tempTestDir; const maxDepth = 10; // Reasonable depth for testing for (let i = 0; i < maxDepth; i++) { deepPath = path.join(deepPath, `level${i}`); await fs.mkdir(deepPath, { recursive: true }); } const deepFile = path.join(deepPath, 'deep-file.mp3'); await fs.writeFile(deepFile, 'deep file content'); const result = await inspector.analyzeFile(deepFile); // Should handle deep paths correctly expect(result.file.path).toContain('deep-file.mp3'); }); }); describe('Game Development Workflow Integration', () => { test('should provide comprehensive game audio analysis', async () => { const gameAudioFiles = [ { name: 'ui-click.wav', content: 'short ui sound', expectedProperties: ['suitableForLoop', 'recommendedCompressionFormat'] }, { name: 'background-music.mp3', content: 'longer background track', expectedProperties: ['platformOptimizations', 'gameDevNotes'] }, { name: 'voice-dialogue.wav', content: 'voice recording', expectedProperties: ['estimatedMemoryUsage', 'compressionRatio'] } ]; for (const audioFile of gameAudioFiles) { const filePath = path.join(tempTestDir, audioFile.name); await fs.writeFile(filePath, audioFile.content); const result = await inspector.analyzeFile(filePath, true); // includeGameAnalysis = true expect(result).toHaveProperty('gameAudio'); // Check for expected game-specific properties audioFile.expectedProperties.forEach(prop => { expect(result.gameAudio).toHaveProperty(prop); }); // Verify game analysis completeness expect(result.gameAudio).toHaveProperty('suitableForLoop'); expect(result.gameAudio).toHaveProperty('recommendedCompressionFormat'); expect(result.gameAudio).toHaveProperty('platformOptimizations'); expect(typeof result.gameAudio.estimatedMemoryUsage).toBe('number'); } }); test('should optimize recommendations for different game contexts', async () => { const contextFiles = [ { name: 'sfx-short.wav', expectedLoop: true }, { name: 'music-long.mp3', expectedLoop: false }, { name: 'voice-medium.wav', expectedLoop: false } ]; for (const contextFile of contextFiles) { const filePath = path.join(tempTestDir, contextFile.name); await fs.writeFile(filePath, 'context test content'); const result = await inspector.analyzeFile(filePath, true); // Verify context-appropriate recommendations expect(result.gameAudio).toHaveProperty('platformOptimizations'); expect(result.gameAudio.platformOptimizations).toHaveProperty('mobile'); expect(result.gameAudio.platformOptimizations).toHaveProperty('desktop'); expect(result.gameAudio.platformOptimizations).toHaveProperty('console'); } }); }); describe('Performance Under Load', () => { test('should handle large batch operations efficiently', async () => { const fileCount = 20; // Reasonable number for CI/CD const files = []; // Create multiple test files for (let i = 0; i < fileCount; i++) { const fileName = `perf-test-${i}.mp3`; const filePath = path.join(tempTestDir, fileName); await fs.writeFile(filePath, `fake mp3 content ${i}`); files.push(fileName); } const startTime = Date.now(); const batchResult = await inspector.analyzeBatch(tempTestDir); const endTime = Date.now(); const processingTime = endTime - startTime; const avgTimePerFile = processingTime / fileCount; expect(batchResult.summary.totalFiles).toBe(fileCount); expect(avgTimePerFile).toBeLessThan(1000); // Should process each file in under 1 second on average // Verify all files were processed expect(batchResult.results.length).toBe(fileCount); }); test('should handle concurrent file analysis requests', async () => { const concurrentFiles = Array.from({ length: 5 }, (_, i) => ({ name: `concurrent-${i}.mp3`, path: path.join(tempTestDir, `concurrent-${i}.mp3`) })); // Create test files for (const file of concurrentFiles) { await fs.writeFile(file.path, `concurrent test content ${file.name}`); } // Analyze files concurrently const startTime = Date.now(); const promises = concurrentFiles.map(file => inspector.analyzeFile(file.path)); const results = await Promise.all(promises); const endTime = Date.now(); expect(results.length).toBe(concurrentFiles.length); // All results should be valid results.forEach((result, index) => { expect(result.file.name).toBe(concurrentFiles[index].name); expect(result).toHaveProperty('format'); expect(result).toHaveProperty('tags'); }); // Concurrent processing should be reasonably fast const totalTime = endTime - startTime; expect(totalTime).toBeLessThan(10000); // Should complete within 10 seconds }); }); describe('Data Consistency and Validation', () => { test('should maintain data consistency across multiple runs', async () => { const testFile = path.join(tempTestDir, 'consistency-check.mp3'); await fs.writeFile(testFile, 'consistent test content'); // Analyze the same file multiple times const runs = 3; const results = []; for (let i = 0; i < runs; i++) { const result = await inspector.analyzeFile(testFile); results.push(result); } // Compare results for consistency const firstResult = results[0]; for (let i = 1; i < runs; i++) { const currentResult = results[i]; // File properties should be identical expect(currentResult.file.name).toBe(firstResult.file.name); expect(currentResult.file.size).toBe(firstResult.file.size); // Format detection should be consistent expect(currentResult.format.container).toBe(firstResult.format.container); expect(currentResult.format.codec).toBe(firstResult.format.codec); } }); test('should validate output schema compliance', async () => { const testFile = path.join(tempTestDir, 'schema-test.mp3'); await fs.writeFile(testFile, 'schema validation content'); const result = await inspector.analyzeFile(testFile); // Validate required top-level properties expect(result).toHaveProperty('file'); expect(result).toHaveProperty('format'); expect(result).toHaveProperty('tags'); expect(result).toHaveProperty('source'); // Validate file object structure expect(result.file).toHaveProperty('path'); expect(result.file).toHaveProperty('name'); expect(result.file).toHaveProperty('size'); // Validate format object structure expect(result.format).toHaveProperty('container'); expect(result.format).toHaveProperty('codec'); expect(result.format).toHaveProperty('duration'); expect(result.format).toHaveProperty('bitrate'); // Validate tags object structure expect(result.tags).toHaveProperty('title'); expect(result.tags).toHaveProperty('artist'); expect(result.tags).toHaveProperty('album'); // Validate data types expect(typeof result.file.size).toBe('number'); expect(typeof result.format.duration).toBe('number'); expect(typeof result.format.bitrate).toBe('number'); }); }); describe('Fallback Mechanisms', () => { test('should fallback to FFprobe when music-metadata fails', async () => { const problematicFile = path.join(tempTestDir, 'problematic.unknown'); await fs.writeFile(problematicFile, 'unknown format content'); const result = await inspector.analyzeFile(problematicFile); // Should handle unknown format gracefully expect(result).toBeDefined(); if (!result.error) { // If analysis succeeded, it should indicate fallback usage expect(result).toHaveProperty('source'); } else { // If analysis failed, should have error information expect(result).toHaveProperty('error', true); expect(result).toHaveProperty('message'); } }); test('should maintain functionality when external dependencies are unavailable', async () => { // Test graceful degradation when FFprobe is not available const testFile = path.join(tempTestDir, 'dependency-test.mp3'); await fs.writeFile(testFile, 'dependency test content'); // The inspector should still function even if FFprobe fails const result = await inspector.analyzeFile(testFile); expect(result).toBeDefined(); expect(result.file).toHaveProperty('name'); }); }); });

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/DeveloperZo/mcp-audio-inspector'

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