Skip to main content
Glama
build-cycle.test.ts10.2 kB
/** * E2E Test: Build-Test-Debug Cycle * Tests tool registration and basic workflow */ import { describe, it, expect, beforeAll, afterAll } from 'vitest'; import { getToolRegistry } from '../../src/tools/register.js'; import { resetConfig, setConfig } from '../../src/config.js'; // Import tool registration functions directly import { registerBuildAppTool } from '../../src/tools/build/build-app.js'; import { registerInstallAppTool } from '../../src/tools/build/install-app.js'; import { registerLaunchAppTool } from '../../src/tools/build/launch-app.js'; import { registerGetUIContextTool } from '../../src/tools/ui/get-ui-context.js'; import { registerInteractWithUITool } from '../../src/tools/ui/interact-with-ui.js'; import { registerRunUnitTestsTool } from '../../src/tools/testing/run-unit-tests.js'; import { registerRunMaestroFlowTool } from '../../src/tools/testing/run-maestro-flow.js'; import { registerRunLinterTool } from '../../src/tools/testing/run-linter.js'; import { registerListDevicesTool } from '../../src/tools/environment/list-devices.js'; import { registerManageEnvTool } from '../../src/tools/environment/manage-env.js'; import { registerCleanProjectTool } from '../../src/tools/environment/clean-project.js'; import { registerAnalyzeCrashTool } from '../../src/tools/crash/analyze-crash.js'; import { registerDeepLinkNavigateTool } from '../../src/tools/navigation/deep-link-navigate.js'; import { registerInspectAppStateTool } from '../../src/tools/observability/inspect-app-state.js'; import { registerInspectLogsTool } from '../../src/tools/observability/inspect-logs.js'; /** * Register all tools for E2E testing */ function registerTestTools(): void { const registry = getToolRegistry(); registry.clear(); registerBuildAppTool(); registerInstallAppTool(); registerLaunchAppTool(); registerGetUIContextTool(); registerInteractWithUITool(); registerRunUnitTestsTool(); registerRunMaestroFlowTool(); registerRunLinterTool(); registerListDevicesTool(); registerManageEnvTool(); registerCleanProjectTool(); registerAnalyzeCrashTool(); registerDeepLinkNavigateTool(); registerInspectAppStateTool(); registerInspectLogsTool(); } describe('Build-Test-Debug Cycle E2E', () => { beforeAll(() => { resetConfig(); setConfig({ debug: false, logLevel: 'error' }); registerTestTools(); }); afterAll(() => { resetConfig(); getToolRegistry().clear(); }); describe('Tool Registration', () => { it('should register all 15 expected tools', () => { const registry = getToolRegistry(); const tools = registry.listTools(); const expectedTools = [ 'build_app', 'install_app', 'launch_app', 'get_ui_context', 'interact_with_ui', 'run_unit_tests', 'run_maestro_flow', 'run_linter', 'list_devices', 'manage_env', 'clean_project', 'analyze_crash', 'deep_link_navigate', 'inspect_app_state', 'inspect_logs', ]; expect(tools.length).toBe(expectedTools.length); for (const toolName of expectedTools) { expect(registry.hasTool(toolName)).toBe(true); } }); it('should have valid schemas for all tools', () => { const registry = getToolRegistry(); const tools = registry.listTools(); for (const tool of tools) { expect(tool.name).toBeTruthy(); expect(tool.description).toBeTruthy(); expect(tool.inputSchema).toBeDefined(); expect(tool.inputSchema.type).toBe('object'); expect(tool.inputSchema.properties).toBeDefined(); } }); it('should have handlers for all tools', () => { const registry = getToolRegistry(); const tools = registry.listTools(); for (const tool of tools) { const registered = registry.getTool(tool.name); expect(registered).toBeDefined(); expect(registered!.handler).toBeInstanceOf(Function); } }); }); describe('Tool Schema Validation', () => { it('build_app should have platform and variant in schema properties', () => { const registry = getToolRegistry(); const tool = registry.getTool('build_app'); expect(tool!.definition.inputSchema.properties).toHaveProperty('platform'); expect(tool!.definition.inputSchema.properties).toHaveProperty('variant'); }); it('list_devices should have platform in schema properties', () => { const registry = getToolRegistry(); const tool = registry.getTool('list_devices'); expect(tool!.definition.inputSchema.properties).toHaveProperty('platform'); }); it('deep_link_navigate should have required fields', () => { const registry = getToolRegistry(); const tool = registry.getTool('deep_link_navigate'); expect(tool!.definition.inputSchema.properties).toHaveProperty('platform'); expect(tool!.definition.inputSchema.properties).toHaveProperty('uri'); }); it('inspect_app_state should require platform and appId', () => { const registry = getToolRegistry(); const tool = registry.getTool('inspect_app_state'); expect(tool!.definition.inputSchema.required).toContain('platform'); expect(tool!.definition.inputSchema.required).toContain('appId'); }); it('inspect_logs should require platform', () => { const registry = getToolRegistry(); const tool = registry.getTool('inspect_logs'); expect(tool!.definition.inputSchema.required).toContain('platform'); }); it('analyze_crash should require platform', () => { const registry = getToolRegistry(); const tool = registry.getTool('analyze_crash'); expect(tool!.definition.inputSchema.required).toContain('platform'); }); }); describe('Tool Categories', () => { it('should have build tools', () => { const registry = getToolRegistry(); const buildTools = ['build_app', 'install_app', 'launch_app']; for (const name of buildTools) { expect(registry.hasTool(name)).toBe(true); } }); it('should have UI tools', () => { const registry = getToolRegistry(); const uiTools = ['get_ui_context', 'interact_with_ui']; for (const name of uiTools) { expect(registry.hasTool(name)).toBe(true); } }); it('should have testing tools', () => { const registry = getToolRegistry(); const testTools = ['run_unit_tests', 'run_maestro_flow', 'run_linter']; for (const name of testTools) { expect(registry.hasTool(name)).toBe(true); } }); it('should have environment tools', () => { const registry = getToolRegistry(); const envTools = ['list_devices', 'manage_env', 'clean_project']; for (const name of envTools) { expect(registry.hasTool(name)).toBe(true); } }); it('should have observability tools', () => { const registry = getToolRegistry(); const obsTools = ['inspect_app_state', 'inspect_logs', 'analyze_crash']; for (const name of obsTools) { expect(registry.hasTool(name)).toBe(true); } }); it('should have navigation tools', () => { const registry = getToolRegistry(); expect(registry.hasTool('deep_link_navigate')).toBe(true); }); }); describe('Build Tool Flow (T037, T038)', () => { it('T037: build_app should return structured BuildResult on success', async () => { const registry = getToolRegistry(); const tool = registry.getTool('build_app'); // The handler returns a BuildResult with success/failure // In test environment without Gradle, it will fail but still return structured result try { const result = await tool!.handler({ platform: 'android', variant: 'debug', }); // Verify structured result format expect(result).toHaveProperty('success'); expect(result).toHaveProperty('platform'); expect(result).toHaveProperty('variant'); expect(result).toHaveProperty('durationMs'); expect(result).toHaveProperty('command'); expect(result).toHaveProperty('exitCode'); } catch (error) { // Build fails in test env (no Gradle), but error should be structured expect(error).toBeDefined(); } }); it('T038: build_app should return structured error summary on failure', async () => { const registry = getToolRegistry(); const tool = registry.getTool('build_app'); // Test that invalid inputs return proper validation errors await expect( tool!.handler({ platform: 'android', variant: 'invalid-variant', }) ).rejects.toThrow(/Invalid variant/); }); it('build_app result should include errorSummary on build failure', async () => { const registry = getToolRegistry(); const tool = registry.getTool('build_app'); try { const result = await tool!.handler({ platform: 'android', variant: 'debug', timeoutMs: 1000, // Short timeout to trigger failure }); // If build fails, it should have error summary if (!(result as { success: boolean }).success) { expect(result).toHaveProperty('exitCode'); // errorSummary is present when build fails expect(result).toHaveProperty('errorSummary'); } } catch { // Expected in test environment without build tools } }); it('install_app should validate platform argument', async () => { const registry = getToolRegistry(); const tool = registry.getTool('install_app'); await expect( tool!.handler({ platform: 'windows', artifactPath: '/path/to/app.apk', }) ).rejects.toThrow(/Invalid platform/); }); it('launch_app should validate required appId', async () => { const registry = getToolRegistry(); const tool = registry.getTool('launch_app'); await expect( tool!.handler({ platform: 'android', }) ).rejects.toThrow(); }); }); });

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/abd3lraouf/specter-mcp'

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