Skip to main content
Glama
voice-e2e.integration.test.ts20.1 kB
/** * End-to-End Integration Tests: Complete Voice Command Flow * * Tests the full voice interaction pipeline: * 1. Wake word detection → Speech-to-Text * 2. STT → Natural Language Processing → Intent Classification * 3. Intent → Command Execution * 4. Execution Result → Text-to-Speech Feedback * 5. Session Management throughout the flow */ import { describe, it, expect, beforeEach, afterEach } from "bun:test"; /** * Mock Voice Pipeline Components */ interface VoiceCommand { audio: ArrayBuffer; language: string; } interface TranscriptionResult { text: string; confidence: number; language: string; } interface Intent { type: string; action: string; target: string; parameters?: Record<string, any>; } interface ExecutionResult { success: boolean; message: string; details?: any; } interface FeedbackOptions { text: string; language: string; mediaPlayer?: string; } /** * Mock Wake Word Detector */ class MockWakeWordDetector { private isActive: boolean = false; async start(): Promise<void> { this.isActive = true; } async stop(): Promise<void> { this.isActive = false; } isListening(): boolean { return this.isActive; } simulateWakeWord(): void { if (this.isActive) { // Simulate wake word detection } } } /** * Mock Speech-to-Text Service */ class MockSpeechToText { async transcribe(command: VoiceCommand): Promise<TranscriptionResult> { // Simulate STT processing const sampleTranscriptions: Record<string, string> = { "turn_on_lights": "Turn on the living room lights", "set_temperature": "Set the temperature to 22 degrees", "play_music": "Play some relaxing music", "check_status": "What's the status of the kitchen", "turn_off_all": "Turn off all the lights", }; const key = new TextDecoder().decode(command.audio); const text = sampleTranscriptions[key] || "Unknown command"; return { text, confidence: 0.92, language: command.language, }; } } /** * Mock Intent Classifier */ class MockIntentClassifier { async classify(transcription: TranscriptionResult): Promise<Intent> { const text = transcription.text.toLowerCase(); if (text.includes("turn on")) { return { type: "device_control", action: "turn_on", target: this.extractTarget(text), }; } else if (text.includes("set") && text.includes("temperature")) { return { type: "climate_control", action: "set_temperature", target: "climate.thermostat", parameters: { temperature: this.extractNumber(text), }, }; } else if (text.includes("play")) { return { type: "media_control", action: "play", target: "media_player.living_room", parameters: { content: "music", }, }; } else if (text.includes("status")) { return { type: "query", action: "get_status", target: this.extractTarget(text), }; } else if (text.includes("turn off")) { return { type: "device_control", action: "turn_off", target: this.extractTarget(text), }; } return { type: "unknown", action: "unknown", target: "unknown", }; } private extractTarget(text: string): string { if (text.includes("living room")) return "light.living_room"; if (text.includes("kitchen")) return "light.kitchen"; if (text.includes("bedroom")) return "light.bedroom"; if (text.includes("all")) return "group.all_lights"; return "unknown"; } private extractNumber(text: string): number { const match = text.match(/\d+/); return match ? parseInt(match[0], 10) : 0; } } /** * Mock Command Executor */ class MockCommandExecutor { private deviceStates: Map<string, any> = new Map(); async execute(intent: Intent): Promise<ExecutionResult> { switch (intent.action) { case "turn_on": this.deviceStates.set(intent.target, { state: "on" }); return { success: true, message: `Successfully turned on ${intent.target}`, }; case "turn_off": this.deviceStates.set(intent.target, { state: "off" }); return { success: true, message: `Successfully turned off ${intent.target}`, }; case "set_temperature": this.deviceStates.set(intent.target, { temperature: intent.parameters?.temperature, }); return { success: true, message: `Temperature set to ${intent.parameters?.temperature} degrees`, }; case "play": this.deviceStates.set(intent.target, { playing: true }); return { success: true, message: "Playing music", }; case "get_status": const state = this.deviceStates.get(intent.target); return { success: true, message: state ? `Status: ${JSON.stringify(state)}` : "Device not found or inactive", details: state, }; default: return { success: false, message: "Unknown command", }; } } getDeviceState(target: string): any { return this.deviceStates.get(target); } } /** * Mock Text-to-Speech Service */ class MockTextToSpeech { private feedbackHistory: FeedbackOptions[] = []; async speak(options: FeedbackOptions): Promise<void> { this.feedbackHistory.push(options); // Simulate TTS playback delay await new Promise((resolve) => setTimeout(resolve, 100)); } getFeedbackHistory(): FeedbackOptions[] { return this.feedbackHistory; } clearHistory(): void { this.feedbackHistory = []; } } /** * Mock Session Manager */ class MockSessionManager { private sessions: Map<string, any> = new Map(); private currentSessionId: string | null = null; startSession(room?: string): string { const sessionId = `session_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; this.sessions.set(sessionId, { id: sessionId, room, commands: [], startTime: Date.now(), }); this.currentSessionId = sessionId; return sessionId; } addCommand(sessionId: string, command: any): void { const session = this.sessions.get(sessionId); if (session) { session.commands.push({ ...command, timestamp: Date.now(), }); } } getSession(sessionId: string): any { return this.sessions.get(sessionId); } endSession(sessionId: string): void { const session = this.sessions.get(sessionId); if (session) { session.endTime = Date.now(); session.active = false; } if (this.currentSessionId === sessionId) { this.currentSessionId = null; } } getCurrentSessionId(): string | null { return this.currentSessionId; } } /** * Voice Pipeline Integration */ class VoicePipeline { constructor( private wakeWord: MockWakeWordDetector, private stt: MockSpeechToText, private classifier: MockIntentClassifier, private executor: MockCommandExecutor, private tts: MockTextToSpeech, private sessionManager: MockSessionManager ) {} async processVoiceCommand( audioData: string, language: string = "en", room?: string ): Promise<{ sessionId: string; transcription: TranscriptionResult; intent: Intent; executionResult: ExecutionResult; feedback: string; }> { // Start session const sessionId = this.sessionManager.startSession(room); // Transcribe audio const command: VoiceCommand = { audio: new TextEncoder().encode(audioData).buffer, language, }; const transcription = await this.stt.transcribe(command); // Classify intent const intent = await this.classifier.classify(transcription); // Execute command const executionResult = await this.executor.execute(intent); // Generate feedback const feedbackText = executionResult.success ? executionResult.message : `Failed: ${executionResult.message}`; await this.tts.speak({ text: feedbackText, language, }); // Record in session this.sessionManager.addCommand(sessionId, { transcription: transcription.text, intent, result: executionResult, }); return { sessionId, transcription, intent, executionResult, feedback: feedbackText, }; } } describe("Voice Command E2E Integration", () => { let wakeWord: MockWakeWordDetector; let stt: MockSpeechToText; let classifier: MockIntentClassifier; let executor: MockCommandExecutor; let tts: MockTextToSpeech; let sessionManager: MockSessionManager; let pipeline: VoicePipeline; beforeEach(() => { wakeWord = new MockWakeWordDetector(); stt = new MockSpeechToText(); classifier = new MockIntentClassifier(); executor = new MockCommandExecutor(); tts = new MockTextToSpeech(); sessionManager = new MockSessionManager(); pipeline = new VoicePipeline(wakeWord, stt, classifier, executor, tts, sessionManager); }); afterEach(async () => { await wakeWord.stop(); tts.clearHistory(); }); describe("Complete Voice Flow", () => { it("should process a complete turn on lights command", async () => { const result = await pipeline.processVoiceCommand("turn_on_lights", "en", "living_room"); expect(result.transcription.text).toBe("Turn on the living room lights"); expect(result.intent.action).toBe("turn_on"); expect(result.intent.target).toBe("light.living_room"); expect(result.executionResult.success).toBe(true); expect(result.feedback).toContain("Successfully turned on"); // Verify TTS was called const feedbackHistory = tts.getFeedbackHistory(); expect(feedbackHistory).toHaveLength(1); expect(feedbackHistory[0].text).toContain("Successfully"); }); it("should process a set temperature command", async () => { const result = await pipeline.processVoiceCommand("set_temperature", "en"); expect(result.transcription.text).toBe("Set the temperature to 22 degrees"); expect(result.intent.action).toBe("set_temperature"); expect(result.intent.parameters?.temperature).toBe(22); expect(result.executionResult.success).toBe(true); expect(result.feedback).toContain("Temperature set to 22"); }); it("should track session throughout the flow", async () => { const result = await pipeline.processVoiceCommand("turn_on_lights", "en", "bedroom"); const session = sessionManager.getSession(result.sessionId); expect(session).toBeDefined(); expect(session.room).toBe("bedroom"); expect(session.commands).toHaveLength(1); expect(session.commands[0].transcription).toBe("Turn on the living room lights"); }); it("should handle multiple commands in sequence", async () => { const result1 = await pipeline.processVoiceCommand("turn_on_lights", "en"); const result2 = await pipeline.processVoiceCommand("set_temperature", "en"); const result3 = await pipeline.processVoiceCommand("play_music", "en"); expect(result1.executionResult.success).toBe(true); expect(result2.executionResult.success).toBe(true); expect(result3.executionResult.success).toBe(true); const feedbackHistory = tts.getFeedbackHistory(); expect(feedbackHistory).toHaveLength(3); }); it("should maintain device state across commands", async () => { // Turn on lights await pipeline.processVoiceCommand("turn_on_lights", "en"); let state = executor.getDeviceState("light.living_room"); expect(state.state).toBe("on"); // Turn off lights await pipeline.processVoiceCommand("turn_off_all", "en"); state = executor.getDeviceState("group.all_lights"); expect(state.state).toBe("off"); }); }); describe("Multi-Language Support", () => { it("should process commands in different languages", async () => { const languages = ["en", "de", "es", "fr"]; for (const lang of languages) { const result = await pipeline.processVoiceCommand("turn_on_lights", lang); expect(result.transcription.language).toBe(lang); expect(result.executionResult.success).toBe(true); } const feedbackHistory = tts.getFeedbackHistory(); expect(feedbackHistory).toHaveLength(4); }); it("should provide feedback in the same language as command", async () => { const result = await pipeline.processVoiceCommand("turn_on_lights", "de"); const feedbackHistory = tts.getFeedbackHistory(); expect(feedbackHistory[0].language).toBe("de"); }); }); describe("Error Handling", () => { it("should handle unknown commands gracefully", async () => { const result = await pipeline.processVoiceCommand("unknown_command", "en"); expect(result.intent.type).toBe("unknown"); expect(result.executionResult.success).toBe(false); expect(result.feedback).toContain("Failed"); }); it("should provide error feedback via TTS", async () => { await pipeline.processVoiceCommand("unknown_command", "en"); const feedbackHistory = tts.getFeedbackHistory(); expect(feedbackHistory).toHaveLength(1); expect(feedbackHistory[0].text).toContain("Failed"); }); }); describe("Session Management", () => { it("should create unique sessions for each command", async () => { const result1 = await pipeline.processVoiceCommand("turn_on_lights", "en"); const result2 = await pipeline.processVoiceCommand("set_temperature", "en"); expect(result1.sessionId).not.toBe(result2.sessionId); }); it("should track room context in sessions", async () => { const rooms = ["living_room", "bedroom", "kitchen"]; for (const room of rooms) { const result = await pipeline.processVoiceCommand("turn_on_lights", "en", room); const session = sessionManager.getSession(result.sessionId); expect(session.room).toBe(room); } }); it("should end sessions properly", async () => { const result = await pipeline.processVoiceCommand("turn_on_lights", "en"); const sessionId = result.sessionId; sessionManager.endSession(sessionId); const session = sessionManager.getSession(sessionId); expect(session.active).toBe(false); expect(session.endTime).toBeDefined(); }); }); describe("Performance", () => { it("should process commands within acceptable time", async () => { const start = performance.now(); await pipeline.processVoiceCommand("turn_on_lights", "en"); const elapsed = performance.now() - start; expect(elapsed).toBeLessThan(1000); // Should complete in less than 1 second }); it("should handle concurrent commands", async () => { const promises = [ pipeline.processVoiceCommand("turn_on_lights", "en"), pipeline.processVoiceCommand("set_temperature", "en"), pipeline.processVoiceCommand("play_music", "en"), ]; const results = await Promise.all(promises); expect(results).toHaveLength(3); results.forEach((result) => { expect(result.executionResult.success).toBe(true); }); }); it("should process bulk commands efficiently", async () => { const start = performance.now(); const promises = Array.from({ length: 10 }, (_, i) => pipeline.processVoiceCommand(`turn_on_lights`, "en", `room_${i}`) ); await Promise.all(promises); const elapsed = performance.now() - start; expect(elapsed).toBeLessThan(2000); // 10 commands in less than 2 seconds }); }); describe("Wake Word Integration", () => { it("should start wake word detection", async () => { await wakeWord.start(); expect(wakeWord.isListening()).toBe(true); }); it("should stop wake word detection", async () => { await wakeWord.start(); await wakeWord.stop(); expect(wakeWord.isListening()).toBe(false); }); it("should process command after wake word detection", async () => { await wakeWord.start(); wakeWord.simulateWakeWord(); const result = await pipeline.processVoiceCommand("turn_on_lights", "en"); expect(result.executionResult.success).toBe(true); }); }); describe("Transcription Quality", () => { it("should have high confidence for clear commands", async () => { const result = await pipeline.processVoiceCommand("turn_on_lights", "en"); expect(result.transcription.confidence).toBeGreaterThan(0.8); }); it("should transcribe various command types", async () => { const commands = ["turn_on_lights", "set_temperature", "play_music", "check_status"]; for (const cmd of commands) { const result = await pipeline.processVoiceCommand(cmd, "en"); expect(result.transcription.text).toBeTruthy(); expect(result.transcription.confidence).toBeGreaterThan(0); } }); }); describe("Intent Classification", () => { it("should classify device control intents", async () => { const result = await pipeline.processVoiceCommand("turn_on_lights", "en"); expect(result.intent.type).toBe("device_control"); expect(result.intent.action).toBe("turn_on"); }); it("should classify climate control intents", async () => { const result = await pipeline.processVoiceCommand("set_temperature", "en"); expect(result.intent.type).toBe("climate_control"); expect(result.intent.action).toBe("set_temperature"); }); it("should classify media control intents", async () => { const result = await pipeline.processVoiceCommand("play_music", "en"); expect(result.intent.type).toBe("media_control"); expect(result.intent.action).toBe("play"); }); it("should classify query intents", async () => { const result = await pipeline.processVoiceCommand("check_status", "en"); expect(result.intent.type).toBe("query"); expect(result.intent.action).toBe("get_status"); }); }); describe("Command Execution", () => { it("should execute turn on commands", async () => { const result = await pipeline.processVoiceCommand("turn_on_lights", "en"); const state = executor.getDeviceState("light.living_room"); expect(state.state).toBe("on"); }); it("should execute turn off commands", async () => { const result = await pipeline.processVoiceCommand("turn_off_all", "en"); const state = executor.getDeviceState("group.all_lights"); expect(state.state).toBe("off"); }); it("should execute climate control commands", async () => { const result = await pipeline.processVoiceCommand("set_temperature", "en"); const state = executor.getDeviceState("climate.thermostat"); expect(state.temperature).toBe(22); }); it("should execute media control commands", async () => { const result = await pipeline.processVoiceCommand("play_music", "en"); const state = executor.getDeviceState("media_player.living_room"); expect(state.playing).toBe(true); }); }); describe("Feedback Generation", () => { it("should generate success feedback", async () => { await pipeline.processVoiceCommand("turn_on_lights", "en"); const feedbackHistory = tts.getFeedbackHistory(); expect(feedbackHistory[0].text).toContain("Successfully"); }); it("should generate error feedback", async () => { await pipeline.processVoiceCommand("unknown_command", "en"); const feedbackHistory = tts.getFeedbackHistory(); expect(feedbackHistory[0].text).toContain("Failed"); }); it("should generate context-specific feedback", async () => { const result = await pipeline.processVoiceCommand("set_temperature", "en"); expect(result.feedback).toContain("22 degrees"); }); }); });

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