ShutdownSimulatorMCP.e2e.test.ts•5.33 kB
/**
* E2E Test for Shutdown Simulator through MCP Protocol
*
* Tests critical user journey: Shutting down a simulator through MCP
* Following testing philosophy: E2E tests for critical paths only (10%)
*
* Focus: MCP protocol interaction, not simulator shutdown logic
* The controller tests already verify shutdown works with real simulators
* This test verifies the MCP transport/serialization/protocol works
*
* NO MOCKS - Uses real MCP server, real simulators
*/
import { describe, it, expect, beforeAll, afterAll } from '@jest/globals';
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
import { CallToolResultSchema } from '@modelcontextprotocol/sdk/types.js';
import { createAndConnectClient, cleanupClientAndTransport } from '../../../../shared/tests/utils/testHelpers.js';
import { TestSimulatorManager } from '../../../../shared/tests/utils/TestSimulatorManager.js';
import { exec } from 'child_process';
import { promisify } from 'util';
const execAsync = promisify(exec);
describe('Shutdown Simulator MCP E2E', () => {
let client: Client;
let transport: StdioClientTransport;
let testSimManager: TestSimulatorManager;
beforeAll(async () => {
// Build the server
const { execSync } = await import('child_process');
execSync('npm run build', { stdio: 'inherit' });
// Set up test simulator
testSimManager = new TestSimulatorManager();
await testSimManager.getOrCreateSimulator('TestSimulator-ShutdownMCP');
// Connect to MCP server
({ client, transport } = await createAndConnectClient());
});
afterAll(async () => {
// Cleanup test simulator
await testSimManager.cleanup();
// Cleanup MCP connection
await cleanupClientAndTransport(client, transport);
});
describe('shutdown simulator through MCP', () => {
it('should shutdown simulator via MCP protocol', async () => {
// Arrange - Boot the simulator first
await testSimManager.bootAndWait(30);
// Act - Call tool through MCP
const result = await client.callTool({
name: 'shutdown_simulator',
arguments: {
deviceId: testSimManager.getSimulatorName()
}
});
// Assert - Verify MCP response
const parsed = CallToolResultSchema.parse(result);
expect(parsed.content[0].type).toBe('text');
expect(parsed.content[0].text).toBe(`✅ Successfully shutdown simulator: ${testSimManager.getSimulatorName()} (${testSimManager.getSimulatorId()})`);
// Verify simulator is actually shutdown
const listResult = await execAsync('xcrun simctl list devices --json');
const devices = JSON.parse(listResult.stdout);
let found = false;
for (const runtime of Object.values(devices.devices) as any[]) {
const device = runtime.find((d: any) => d.udid === testSimManager.getSimulatorId());
if (device) {
expect(device.state).toBe('Shutdown');
found = true;
break;
}
}
expect(found).toBe(true);
});
it('should handle already shutdown simulator via MCP', async () => {
// Arrange - ensure simulator is shutdown
await testSimManager.shutdownAndWait();
// Act - Call tool through MCP
const result = await client.callTool({
name: 'shutdown_simulator',
arguments: {
deviceId: testSimManager.getSimulatorId()
}
});
// Assert
const parsed = CallToolResultSchema.parse(result);
expect(parsed.content[0].text).toBe(`✅ Simulator already shutdown: ${testSimManager.getSimulatorName()} (${testSimManager.getSimulatorId()})`);
});
it('should shutdown simulator by UUID via MCP', async () => {
// Arrange - Boot the simulator first
await testSimManager.bootAndWait(30);
// Act - Call tool with UUID
const result = await client.callTool({
name: 'shutdown_simulator',
arguments: {
deviceId: testSimManager.getSimulatorId()
}
});
// Assert
const parsed = CallToolResultSchema.parse(result);
expect(parsed.content[0].text).toBe(`✅ Successfully shutdown simulator: ${testSimManager.getSimulatorName()} (${testSimManager.getSimulatorId()})`);
// Verify simulator is actually shutdown
const listResult = await execAsync('xcrun simctl list devices --json');
const devices = JSON.parse(listResult.stdout);
for (const runtime of Object.values(devices.devices) as any[]) {
const device = runtime.find((d: any) => d.udid === testSimManager.getSimulatorId());
if (device) {
expect(device.state).toBe('Shutdown');
break;
}
}
});
});
describe('error handling through MCP', () => {
it('should return error for non-existent simulator', async () => {
// Act
const result = await client.callTool({
name: 'shutdown_simulator',
arguments: {
deviceId: 'NonExistentSimulator-MCP'
}
});
// Assert
const parsed = CallToolResultSchema.parse(result);
expect(parsed.content[0].text).toBe('❌ Simulator not found: NonExistentSimulator-MCP');
});
});
});