Skip to main content
Glama
smoke-test.ts8.46 kB
/** * Smoke Test Module * * Verifies WP Navigator connection after configuration is saved. * Calls the introspect endpoint and displays site summary. * * @package WP_Navigator_MCP * @since 2.1.0 */ import { detectPlugin, type PluginEdition, type PluginDetectionResult, } from '../../plugin-detection.js'; import { toolRegistry } from '../../tool-registry/index.js'; import { success, error as errorMessage, info, keyValue } from '../tui/components.js'; // ============================================================================= // Types // ============================================================================= /** * Smoke test result */ export interface SmokeTestResult { success: boolean; siteName?: string; siteUrl?: string; wordpressVersion?: string; pluginVersion?: string; pluginEdition?: PluginEdition; toolCount?: number; error?: SmokeTestError; } /** * Smoke test error with remediation guidance */ export interface SmokeTestError { code: | 'AUTH_FAILED' | 'NOT_FOUND' | 'NETWORK_ERROR' | 'SSL_ERROR' | 'INVALID_RESPONSE' | 'UNKNOWN'; message: string; remediation: string; } // ============================================================================= // Smoke Test Implementation // ============================================================================= /** * Run smoke test by calling the introspect endpoint * * @param siteUrl - WordPress site URL * @param username - WordPress username * @param password - Application password * @param timeoutMs - Request timeout (default: 15000) * @returns Smoke test result with site info or error */ export async function runSmokeTest( siteUrl: string, username: string, password: string, timeoutMs: number = 15000 ): Promise<SmokeTestResult> { try { // Use detectPlugin which calls the introspect endpoint const result: PluginDetectionResult = await detectPlugin( siteUrl, username, password, timeoutMs ); if (!result.detected) { return { success: false, error: mapDetectionError(result), }; } // Get tool count from registry const toolCount = toolRegistry.getAllDefinitions().length; // Extract WordPress version from full response const wpVersion = result.fullResponse?.site?.version || 'Unknown'; return { success: true, siteName: result.siteName || result.fullResponse?.site?.name || 'Unknown', siteUrl: result.siteUrl || result.fullResponse?.site?.url || siteUrl, wordpressVersion: wpVersion, pluginVersion: result.version, pluginEdition: result.edition, toolCount, }; } catch (err) { return { success: false, error: parseError(err), }; } } /** * Map detection result error to smoke test error */ function mapDetectionError(result: PluginDetectionResult): SmokeTestError { const errorCode = result.errorCode || 'UNKNOWN'; const message = result.error || 'Unknown error occurred'; switch (errorCode) { case 'AUTH_FAILED': return { code: 'AUTH_FAILED', message, remediation: 'Application password invalid or expired.\n' + '\n' + 'How to fix:\n' + '1. Go to WordPress Admin → Users → Profile\n' + '2. Scroll to "Application Passwords" section\n' + '3. Revoke existing "wp-navigator" password if present\n' + '4. Generate a new password and update .wpnav.env', }; case 'NOT_FOUND': return { code: 'NOT_FOUND', message, remediation: 'WP Navigator plugin not found or not activated.\n' + '\n' + 'How to fix:\n' + '1. Go to WordPress Admin → Plugins\n' + '2. Install and activate "WP Navigator" (Free or Pro)\n' + '3. Download from: https://wpnav.ai/download', }; case 'NETWORK_ERROR': // Check for SSL-specific errors if (message.includes('certificate') || message.includes('SSL')) { return { code: 'SSL_ERROR', message: 'SSL certificate error', remediation: 'SSL certificate validation failed.\n' + '\n' + 'For local development:\n' + '• Use http:// instead of https://\n' + '• Or set ALLOW_INSECURE_HTTP=1 in .wpnav.env\n' + '\n' + 'For production:\n' + '• Ensure your SSL certificate is valid\n' + '• Check certificate chain is complete', }; } return { code: 'NETWORK_ERROR', message, remediation: 'Cannot reach the WordPress site.\n' + '\n' + 'How to fix:\n' + '1. Verify the URL is correct\n' + '2. Check the site is accessible in your browser\n' + '3. Check for firewall or VPN issues\n' + '4. For local dev, ensure the server is running', }; case 'INVALID_RESPONSE': return { code: 'INVALID_RESPONSE', message, remediation: 'WordPress returned an unexpected response.\n' + '\n' + 'How to fix:\n' + '1. Check WP Navigator plugin is activated\n' + '2. Verify REST API is not disabled\n' + '3. Check for plugin conflicts\n' + '4. Review WordPress debug.log for errors', }; default: return { code: 'UNKNOWN', message, remediation: 'An unexpected error occurred.\n' + '\n' + 'Run `wpnav doctor` for detailed diagnostics.', }; } } /** * Parse raw error into SmokeTestError */ function parseError(err: unknown): SmokeTestError { const message = err instanceof Error ? err.message : String(err); // Check for specific error patterns if (message.includes('401') || message.includes('Unauthorized')) { return mapDetectionError({ detected: false, errorCode: 'AUTH_FAILED', error: message, }); } if (message.includes('404') || message.includes('Not Found')) { return mapDetectionError({ detected: false, errorCode: 'NOT_FOUND', error: message, }); } if (message.includes('certificate') || message.includes('SSL') || message.includes('CERT')) { return { code: 'SSL_ERROR', message: 'SSL certificate error', remediation: 'SSL certificate validation failed.\n' + '\n' + 'For local development:\n' + '• Use http:// instead of https://\n' + '• Or set ALLOW_INSECURE_HTTP=1 in .wpnav.env', }; } if ( message.includes('ECONNREFUSED') || message.includes('ENOTFOUND') || message.includes('timeout') || message.includes('getaddrinfo') ) { return mapDetectionError({ detected: false, errorCode: 'NETWORK_ERROR', error: message, }); } return { code: 'UNKNOWN', message, remediation: 'Run `wpnav doctor` for detailed diagnostics.', }; } // ============================================================================= // Display Functions // ============================================================================= /** * Display smoke test result in TUI format * * Success format: * ``` * ✓ Connection verified! * Site: example.com * WordPress: 6.4.2 * WP Navigator: Pro v1.5.0 * Tools available: 65 * ``` * * @param result - Smoke test result */ export function displaySmokeTestResult(result: SmokeTestResult): void { if (result.success) { success('Connection verified!'); if (result.siteName) { keyValue('Site', result.siteName); } if (result.wordpressVersion && result.wordpressVersion !== 'Unknown') { keyValue('WordPress', result.wordpressVersion); } if (result.pluginVersion && result.pluginEdition) { const editionLabel = result.pluginEdition === 'pro' ? 'Pro' : 'Free'; keyValue('WP Navigator', `${editionLabel} v${result.pluginVersion}`); } if (result.toolCount) { keyValue('Tools available', String(result.toolCount)); } } else if (result.error) { errorMessage(`Connection failed: ${result.error.message}`); console.error(''); info('How to fix:'); // Split remediation into lines and display each const lines = result.error.remediation.split('\n'); for (const line of lines) { console.error(` ${line}`); } } }

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