Skip to main content
Glama
context.test.ts16.3 kB
/** * Context Command Tests * * @package WP_Navigator_MCP * @since 2.7.0 */ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; import { handleContext, type ContextCommandOptions } from './context.js'; import type { WPConfig } from '../../config.js'; import { toolRegistry } from '../../tool-registry/index.js'; // ============================================================================= // Test Fixtures // ============================================================================= function createMockConfig(): WPConfig { return { baseUrl: 'https://example.com', restApi: 'https://example.com/wp-json', wpnavBase: 'https://example.com/wp-json/wpnav/v1', wpnavIntrospect: 'https://example.com/wp-json/wpnav/v1/introspect', auth: { username: 'testuser', password: 'xxxx xxxx xxxx xxxx', }, toggles: { enableWrites: false, toolTimeoutMs: 600000, maxResponseKb: 64, allowInsecureHttp: false, }, featureFlags: { workflowsEnabled: false, bulkValidatorEnabled: false, seoAuditEnabled: false, contentReviewerEnabled: false, migrationPlannerEnabled: false, performanceAnalyzerEnabled: false, }, }; } function createMockIntrospect(): Record<string, unknown> { return { site_name: 'Test Site', version: '1.5.0', edition: 'pro', plugin_version: '1.5.0', page_builder: 'elementor', detected_plugins: ['woocommerce', 'elementor'], policy: { allow_writes: true, }, capabilities: { can_manage_options: true, }, }; } function createMockWpRequest(introspect: Record<string, unknown>) { // Note: wpRequest returns parsed JSON directly, not a Response object return vi.fn().mockImplementation(async (endpoint: string) => { if (endpoint === '/wpnav/v1/introspect') { return introspect; } throw new Error(`Endpoint not found: ${endpoint}`); }); } // ============================================================================= // Tests // ============================================================================= describe('handleContext', () => { let consoleLogSpy: ReturnType<typeof vi.spyOn>; let consoleErrorSpy: ReturnType<typeof vi.spyOn>; beforeEach(() => { consoleLogSpy = vi.spyOn(console, 'log').mockImplementation(() => {}); consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {}); }); afterEach(() => { consoleLogSpy.mockRestore(); consoleErrorSpy.mockRestore(); }); describe('without context (connection required)', () => { it('should return error when no context provided (TUI)', async () => { const result = await handleContext({}); expect(result).toBe(1); expect(consoleErrorSpy).toHaveBeenCalledWith(expect.stringContaining('connection required')); }); it('should return JSON error when no context provided', async () => { const result = await handleContext({ json: true }); expect(result).toBe(1); // Check JSON output expect(consoleLogSpy).toHaveBeenCalled(); const output = JSON.parse(consoleLogSpy.mock.calls[0][0] as string); expect(output.success).toBe(false); expect(output.error.code).toBe('CONNECTION_REQUIRED'); }); }); describe('with valid context', () => { it('should return success with JSON output', async () => { const config = createMockConfig(); const introspect = createMockIntrospect(); const wpRequest = createMockWpRequest(introspect); const result = await handleContext({ json: true }, { config, wpRequest }); expect(result).toBe(0); // Check JSON output expect(consoleLogSpy).toHaveBeenCalled(); const output = JSON.parse(consoleLogSpy.mock.calls[0][0] as string); expect(output.success).toBe(true); expect(output.data).toBeDefined(); }); it('should include focus_mode in output', async () => { const config = createMockConfig(); const introspect = createMockIntrospect(); const wpRequest = createMockWpRequest(introspect); await handleContext({ json: true }, { config, wpRequest }); const output = JSON.parse(consoleLogSpy.mock.calls[0][0] as string); expect(output.data.focus_mode).toBeDefined(); expect(output.data.focus_mode.name).toBeDefined(); expect(output.data.focus_mode.description).toBeDefined(); expect(output.data.focus_mode.token_estimate).toBeDefined(); }); it('should include tools in output', async () => { const config = createMockConfig(); const introspect = createMockIntrospect(); const wpRequest = createMockWpRequest(introspect); await handleContext({ json: true }, { config, wpRequest }); const output = JSON.parse(consoleLogSpy.mock.calls[0][0] as string); expect(output.data.tools).toBeDefined(); expect(output.data.tools.total_available).toBeGreaterThanOrEqual(0); expect(output.data.tools.enabled).toBeGreaterThanOrEqual(0); expect(output.data.tools.by_category).toBeDefined(); }); it('should include tool list when includeTools option is true', async () => { const config = createMockConfig(); const introspect = createMockIntrospect(); const wpRequest = createMockWpRequest(introspect); await handleContext({ json: true, includeTools: true }, { config, wpRequest }); const output = JSON.parse(consoleLogSpy.mock.calls[0][0] as string); expect(output.data.tools.list).toBeDefined(); expect(Array.isArray(output.data.tools.list)).toBe(true); }); it('should include tool list when format is full', async () => { const config = createMockConfig(); const introspect = createMockIntrospect(); const wpRequest = createMockWpRequest(introspect); await handleContext({ json: true, format: 'full' }, { config, wpRequest }); const output = JSON.parse(consoleLogSpy.mock.calls[0][0] as string); expect(output.data.tools.list).toBeDefined(); }); it('should include site info from introspect', async () => { const config = createMockConfig(); const introspect = createMockIntrospect(); const wpRequest = createMockWpRequest(introspect); await handleContext({ json: true }, { config, wpRequest }); const output = JSON.parse(consoleLogSpy.mock.calls[0][0] as string); expect(output.data.site).toBeDefined(); expect(output.data.site.name).toBe('Test Site'); expect(output.data.site.url).toBe('https://example.com'); expect(output.data.site.plugin_version).toBe('1.5.0'); expect(output.data.site.plugin_edition).toBe('pro'); expect(output.data.site.page_builder).toBe('elementor'); }); it('should include detected plugins', async () => { const config = createMockConfig(); const introspect = createMockIntrospect(); const wpRequest = createMockWpRequest(introspect); await handleContext({ json: true }, { config, wpRequest }); const output = JSON.parse(consoleLogSpy.mock.calls[0][0] as string); expect(output.data.site.detected_plugins).toContain('woocommerce'); expect(output.data.site.detected_plugins).toContain('elementor'); }); it('should include safety settings', async () => { const config = createMockConfig(); const introspect = createMockIntrospect(); const wpRequest = createMockWpRequest(introspect); await handleContext({ json: true }, { config, wpRequest }); const output = JSON.parse(consoleLogSpy.mock.calls[0][0] as string); expect(output.data.safety).toBeDefined(); expect(output.data.safety.mode).toBeDefined(); expect(output.data.safety.enable_writes).toBe(false); expect(output.data.safety.allowed_operations).toBeDefined(); }); it('should include cookbooks info', async () => { const config = createMockConfig(); const introspect = createMockIntrospect(); const wpRequest = createMockWpRequest(introspect); await handleContext({ json: true }, { config, wpRequest }); const output = JSON.parse(consoleLogSpy.mock.calls[0][0] as string); expect(output.data.cookbooks).toBeDefined(); expect(output.data.cookbooks.available).toBeDefined(); expect(output.data.cookbooks.loaded).toBeDefined(); expect(output.data.cookbooks.recommended).toBeDefined(); }); it('should include environment', async () => { const config = createMockConfig(); const introspect = createMockIntrospect(); const wpRequest = createMockWpRequest(introspect); await handleContext({ json: true }, { config, wpRequest }); const output = JSON.parse(consoleLogSpy.mock.calls[0][0] as string); // Without manifest, environment defaults to 'production' expect(output.data.environment).toBe('production'); }); it('should render TUI output without error', async () => { const config = createMockConfig(); const introspect = createMockIntrospect(); const wpRequest = createMockWpRequest(introspect); const result = await handleContext({}, { config, wpRequest }); expect(result).toBe(0); // TUI outputs to stderr expect(consoleErrorSpy).toHaveBeenCalled(); }); }); describe('with connection failure', () => { it('should return error when introspect fails', async () => { const config = createMockConfig(); // wpRequest throws on error responses (401, 500, etc.) const wpRequest = vi.fn().mockRejectedValue(new Error('Authentication required (401)')); const result = await handleContext({ json: true }, { config, wpRequest }); expect(result).toBe(1); const output = JSON.parse(consoleLogSpy.mock.calls[0][0] as string); expect(output.success).toBe(false); expect(output.error.code).toBe('CONNECTION_FAILED'); }); it('should return error on network failure', async () => { const config = createMockConfig(); const wpRequest = vi.fn().mockRejectedValue(new Error('Network error')); const result = await handleContext({ json: true }, { config, wpRequest }); expect(result).toBe(1); const output = JSON.parse(consoleLogSpy.mock.calls[0][0] as string); expect(output.success).toBe(false); expect(output.error.message).toContain('Network error'); }); }); describe('safety operations', () => { it('should block batch operations in normal mode', async () => { const config = createMockConfig(); const introspect = createMockIntrospect(); const wpRequest = createMockWpRequest(introspect); await handleContext({ json: true }, { config, wpRequest }); const output = JSON.parse(consoleLogSpy.mock.calls[0][0] as string); expect(output.data.safety.blocked_operations).toContain('batch'); }); }); // =========================================================================== // Additional v2.7.0 Tests // =========================================================================== describe('format options', () => { it('should output JSON when format is compact', async () => { const config = createMockConfig(); const introspect = createMockIntrospect(); const wpRequest = createMockWpRequest(introspect); const result = await handleContext({ format: 'compact' }, { config, wpRequest }); expect(result).toBe(0); // compact format outputs JSON like --json flag expect(consoleLogSpy).toHaveBeenCalled(); const output = JSON.parse(consoleLogSpy.mock.calls[0][0] as string); expect(output.success).toBe(true); }); it('should NOT include tool list by default', async () => { const config = createMockConfig(); const introspect = createMockIntrospect(); const wpRequest = createMockWpRequest(introspect); await handleContext({ json: true }, { config, wpRequest }); const output = JSON.parse(consoleLogSpy.mock.calls[0][0] as string); expect(output.data.tools.list).toBeUndefined(); }); }); describe('AI settings', () => { it('should include AI settings from introspect', async () => { const config = createMockConfig(); const introspect = createMockIntrospect(); const wpRequest = createMockWpRequest(introspect); await handleContext({ json: true }, { config, wpRequest }); const output = JSON.parse(consoleLogSpy.mock.calls[0][0] as string); expect(output.data.ai).toBeDefined(); expect(output.data.ai).toHaveProperty('instructions'); expect(output.data.ai).toHaveProperty('prompts_path'); }); }); describe('role information', () => { it('should include null role when no active role', async () => { const config = createMockConfig(); const introspect = createMockIntrospect(); const wpRequest = createMockWpRequest(introspect); await handleContext({ json: true }, { config, wpRequest }); const output = JSON.parse(consoleLogSpy.mock.calls[0][0] as string); expect(output.data.role).toBeNull(); }); }); describe('with writes enabled', () => { it('should reflect enableWrites in safety settings', async () => { const config = createMockConfig(); config.toggles.enableWrites = true; const introspect = createMockIntrospect(); const wpRequest = createMockWpRequest(introspect); await handleContext({ json: true }, { config, wpRequest }); const output = JSON.parse(consoleLogSpy.mock.calls[0][0] as string); expect(output.data.safety.enable_writes).toBe(true); }); }); describe('TUI error handling', () => { it('should render TUI error on connection failure', async () => { const config = createMockConfig(); // wpRequest throws on error responses const wpRequest = vi.fn().mockRejectedValue(new Error('Internal Server Error (500)')); const result = await handleContext({}, { config, wpRequest }); expect(result).toBe(1); // TUI error outputs to stderr expect(consoleErrorSpy).toHaveBeenCalled(); const allCalls = consoleErrorSpy.mock.calls.map((c: unknown[]) => c[0]).join('\n'); expect(allCalls).toContain('Internal Server Error'); }); it('should render TUI error on network exception', async () => { const config = createMockConfig(); const wpRequest = vi.fn().mockRejectedValue(new Error('Connection refused')); const result = await handleContext({}, { config, wpRequest }); expect(result).toBe(1); const allCalls = consoleErrorSpy.mock.calls.map((c: unknown[]) => c[0]).join('\n'); expect(allCalls).toContain('Connection refused'); }); }); describe('page builder detection', () => { it('should detect page builder from introspect', async () => { const config = createMockConfig(); const introspect = { ...createMockIntrospect(), page_builder: 'wpbakery', }; const wpRequest = createMockWpRequest(introspect); await handleContext({ json: true }, { config, wpRequest }); const output = JSON.parse(consoleLogSpy.mock.calls[0][0] as string); expect(output.data.site.page_builder).toBe('wpbakery'); }); it('should handle missing page builder', async () => { const config = createMockConfig(); const introspect = { ...createMockIntrospect(), page_builder: undefined, }; const wpRequest = createMockWpRequest(introspect); await handleContext({ json: true }, { config, wpRequest }); const output = JSON.parse(consoleLogSpy.mock.calls[0][0] as string); expect(output.data.site.page_builder).toBeNull(); }); }); describe('tool categorization', () => { it('should categorize tools by category in by_category', async () => { const config = createMockConfig(); const introspect = createMockIntrospect(); const wpRequest = createMockWpRequest(introspect); await handleContext({ json: true }, { config, wpRequest }); const output = JSON.parse(consoleLogSpy.mock.calls[0][0] as string); expect(output.data.tools.by_category).toBeDefined(); expect(typeof output.data.tools.by_category).toBe('object'); }); }); });

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/littlebearapps/wp-navigator-mcp'

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