Skip to main content
Glama
voice-tts.integration.test.ts8.9 kB
/** * Integration Tests: Voice Feedback System (TTS) * * Tests for Text-to-Speech functionality including: * - TTS audio generation * - Media player integration * - Cache management * - Error handling * - Multi-language support */ import { describe, it, expect, beforeEach, afterEach } from "bun:test"; /** * Mock Home Assistant TTS Service */ class MockHomeAssistantTTSService { private cache: Map<string, string> = new Map(); private playLog: Array<{ entityId: string; url: string }> = []; async generateAudio(text: string, language: string, provider: string): Promise<string> { const cacheKey = `${provider}_${language}_${text}`; if (this.cache.has(cacheKey)) { return this.cache.get(cacheKey)!; } const url = `https://ha.local/api/tts/audio/${provider}_${language}_${Buffer.from(text).toString("base64")}`; this.cache.set(cacheKey, url); return url; } async playAudio(entityId: string, mediaUrl: string): Promise<void> { this.playLog.push({ entityId, url: mediaUrl }); } getCacheSize(): number { return this.cache.size; } getPlayLog(): Array<{ entityId: string; url: string }> { return this.playLog; } clearCache(): void { this.cache.clear(); } clearPlayLog(): void { this.playLog = []; } } describe("TTS Integration Tests", () => { let mockService: MockHomeAssistantTTSService; beforeEach(() => { mockService = new MockHomeAssistantTTSService(); }); afterEach(() => { mockService.clearCache(); mockService.clearPlayLog(); }); describe("Audio Generation", () => { it("should generate audio URL for text", async () => { const text = "Turn on the living room light"; const url = await mockService.generateAudio(text, "en", "google_translate"); expect(url).toBeTruthy(); expect(url).toContain("google_translate"); expect(url).toContain("en"); }); it("should support multiple languages", async () => { const languages = ["en", "de", "es", "fr"]; const text = "Test audio"; const urls = await Promise.all( languages.map((lang) => mockService.generateAudio(text, lang, "google_translate")), ); expect(urls).toHaveLength(4); // Check all URLs are unique const uniqueUrls = new Set(urls); expect(uniqueUrls.size).toBe(4); urls.forEach((url, idx) => { expect(url).toContain(languages[idx]); }); }); it("should handle special characters and unicode", async () => { const specialTexts = [ "Turn on the light!", "What's happening?", "Café café", "日本語テキスト", "Русский текст", ]; const urls = await Promise.all( specialTexts.map((text) => mockService.generateAudio(text, "en", "google_translate")), ); expect(urls).toHaveLength(5); urls.forEach((url) => { expect(url).toBeTruthy(); }); }); it("should generate long text audio", async () => { const longText = "Please turn on the lights in the living room, bedroom, and kitchen, then set the temperature to 22 degrees and close the blinds."; const url = await mockService.generateAudio(longText, "en", "google_translate"); expect(url).toBeTruthy(); expect(url.length).toBeGreaterThan(50); }); }); describe("Audio Playback", () => { it("should play audio on specified media player", async () => { const text = "Audio test"; const url = await mockService.generateAudio(text, "en", "google_translate"); await mockService.playAudio("media_player.living_room", url); const log = mockService.getPlayLog(); expect(log).toHaveLength(1); expect(log[0].entityId).toBe("media_player.living_room"); expect(log[0].url).toBe(url); }); it("should support multiple concurrent playbacks", async () => { const urls = await Promise.all([ mockService.generateAudio("Bedroom", "en", "google_translate"), mockService.generateAudio("Kitchen", "en", "google_translate"), mockService.generateAudio("Living room", "en", "google_translate"), ]); await Promise.all([ mockService.playAudio("media_player.bedroom", urls[0]), mockService.playAudio("media_player.kitchen", urls[1]), mockService.playAudio("media_player.living_room", urls[2]), ]); const log = mockService.getPlayLog(); expect(log).toHaveLength(3); }); }); describe("Caching", () => { it("should cache generated audio", async () => { const text = "Cached text"; const url1 = await mockService.generateAudio(text, "en", "google_translate"); const url2 = await mockService.generateAudio(text, "en", "google_translate"); expect(url1).toBe(url2); expect(mockService.getCacheSize()).toBe(1); }); it("should distinguish cache by language and provider", async () => { const text = "Test text"; const url1 = await mockService.generateAudio(text, "en", "google_translate"); const url2 = await mockService.generateAudio(text, "de", "google_translate"); const url3 = await mockService.generateAudio(text, "en", "microsoft_tts"); expect(url1 === url2).toBe(false); expect(url1 === url3).toBe(false); expect(mockService.getCacheSize()).toBe(3); }); it("should clear cache when requested", async () => { await mockService.generateAudio("Test 1", "en", "google_translate"); await mockService.generateAudio("Test 2", "en", "google_translate"); expect(mockService.getCacheSize()).toBe(2); mockService.clearCache(); expect(mockService.getCacheSize()).toBe(0); }); }); describe("Multi-Language Support", () => { it("should generate audio in all supported languages", async () => { const languages = ["en", "de", "es", "fr", "it", "pt", "nl", "ja", "zh", "ru"]; const text = "Test message"; const urls = await Promise.all( languages.map((lang) => mockService.generateAudio(text, lang, "google_translate")), ); expect(urls).toHaveLength(10); urls.forEach((url, idx) => { expect(url).toBeTruthy(); expect(url).toContain(languages[idx]); // Check that the language is in the URL }); }); it("should handle language variants (pt-BR, zh-TW)", async () => { const variants = ["pt", "pt-BR", "zh", "zh-TW"]; const text = "Variant test"; const urls = await Promise.all( variants.map((lang) => mockService.generateAudio(text, lang, "google_translate")), ); expect(urls).toHaveLength(4); }); }); describe("Error Handling", () => { it("should handle empty text gracefully", async () => { const text = ""; const url = await mockService.generateAudio(text, "en", "google_translate"); // Should still generate URL, even for empty text expect(url).toBeTruthy(); }); it("should handle very long text", async () => { const longText = "A".repeat(5000); const url = await mockService.generateAudio(longText, "en", "google_translate"); expect(url).toBeTruthy(); }); it("should handle invalid media player gracefully", async () => { const url = await mockService.generateAudio("Test", "en", "google_translate"); // Should not throw on invalid entity try { await mockService.playAudio("invalid_entity", url); expect(true).toBe(true); // Success - no throw } catch (error) { expect(error).toBeUndefined(); // Fail if error } }); }); describe("Performance", () => { it("should generate audio within acceptable time", async () => { const start = performance.now(); await mockService.generateAudio("Performance test", "en", "google_translate"); const elapsed = performance.now() - start; expect(elapsed).toBeLessThan(100); // Should be very fast (mock) }); it("should handle bulk audio generation", async () => { const texts = Array.from({ length: 100 }, (_, i) => `Message ${i}`); const start = performance.now(); const urls = await Promise.all( texts.map((text) => mockService.generateAudio(text, "en", "google_translate")), ); const elapsed = performance.now() - start; expect(urls).toHaveLength(100); expect(elapsed).toBeLessThan(1000); // Bulk should complete quickly }); it("should serve cached audio instantly", async () => { const text = "Cached performance test"; await mockService.generateAudio(text, "en", "google_translate"); const start = performance.now(); await mockService.generateAudio(text, "en", "google_translate"); const elapsed = performance.now() - start; expect(elapsed).toBeLessThan(10); // Cache hit should be instant }); }); });

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/jango-blockchained/advanced-homeassistant-mcp'

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