Skip to main content
Glama
test-helpers.ts6.75 kB
/** * Test helper utilities for HTTP mocking and file system operations */ import nock from 'nock'; import { tmpdir } from 'os'; import { join } from 'path'; import { mkdtemp, writeFile, readFile, rm } from 'fs/promises'; import { createMockToken, createMockWordPressResponse, createMockErrorResponse } from './mock-factories.js'; /** * Sets up HTTP mocks for WordPress API */ export class WordPressMockServer { public scope: nock.Scope; constructor(baseUrl: string = 'https://api.example.com') { this.scope = nock(baseUrl); } /** * Mock successful OAuth token exchange */ mockTokenExchange(code: string, clientId: string, clientSecret: string) { return this.scope .post('/oauth2/token') .query(true) .reply((uri, requestBody: any) => { const body = new URLSearchParams(requestBody); if ( body.get('grant_type') === 'authorization_code' && body.get('code') === code && body.get('client_id') === clientId && body.get('client_secret') === clientSecret ) { return [200, createMockToken()]; } return [400, { error: 'invalid_grant', error_description: 'Invalid authorization code' }]; }); } /** * Mock token refresh */ mockTokenRefresh(refreshToken: string, clientId: string, clientSecret: string) { return this.scope .post('/oauth2/token') .query(true) .reply((uri, requestBody: any) => { const body = new URLSearchParams(requestBody); if ( body.get('grant_type') === 'refresh_token' && body.get('refresh_token') === refreshToken && body.get('client_id') === clientId && body.get('client_secret') === clientSecret ) { return [200, createMockToken({ refresh_token: refreshToken })]; } return [400, { error: 'invalid_grant', error_description: 'Invalid refresh token' }]; }); } /** * Mock user info endpoint */ mockUserInfo(accessToken: string, userInfo: any = {}) { return this.scope .get('/rest/v1.1/me') .matchHeader('authorization', `Bearer ${accessToken}`) .reply(200, { ID: 987654321, login: 'testuser', email: 'test@example.com', display_name: 'Test User', ...userInfo, }); } /** * Mock site info endpoint */ mockSiteInfo(siteId: string, accessToken: string, siteInfo: any = {}) { return this.scope .get(`/rest/v1.1/sites/${siteId}`) .matchHeader('authorization', `Bearer ${accessToken}`) .reply(200, { ID: parseInt(siteId), name: 'Test WordPress Site', URL: `https://test-site-${siteId}.com`, ...siteInfo, }); } /** * Mock posts endpoint */ mockPosts(siteId: string, accessToken: string, posts: any[] = []) { return this.scope .get(`/rest/v1.1/sites/${siteId}/posts`) .matchHeader('authorization', `Bearer ${accessToken}`) .query(true) .reply(200, { posts, found: posts.length, meta: { links: { self: `https://api.example.com/rest/v1.1/sites/${siteId}/posts`, }, }, }); } /** * Mock API error responses */ mockError(path: string, status: number, error: any) { return this.scope .get(path) .reply(status, error); } /** * Clean up all mocks */ cleanup() { nock.cleanAll(); } } /** * Creates a temporary directory for testing */ export async function createTempDir(prefix: string = 'mcp-wp-test-'): Promise<string> { return mkdtemp(join(tmpdir(), prefix)); } /** * Creates a temporary file with content */ export async function createTempFile( dir: string, filename: string, content: string ): Promise<string> { const filepath = join(dir, filename); await writeFile(filepath, content, 'utf8'); return filepath; } /** * Reads a temporary file */ export async function readTempFile(filepath: string): Promise<string> { return readFile(filepath, 'utf8'); } /** * Cleans up temporary directory */ export async function cleanupTempDir(dir: string): Promise<void> { try { await rm(dir, { recursive: true, force: true }); } catch (error) { // Ignore cleanup errors in tests } } /** * Mock environment variables for a test */ export function mockEnv(vars: Record<string, string>): () => void { const originalEnv = { ...process.env }; // Set test environment variables Object.assign(process.env, vars); // Return cleanup function return () => { process.env = originalEnv; }; } /** * Wait for a specified amount of time */ export function sleep(ms: number): Promise<void> { return new Promise(resolve => setTimeout(resolve, ms)); } /** * Generate a random test string */ export function randomString(length: number = 10): string { return Math.random().toString(36).substring(2, 2 + length); } /** * Generate a test port number */ export function randomPort(): number { return 3000 + Math.floor(Math.random() * 1000); } /** * Mock console methods to capture output */ export class ConsoleCapture { private originalConsole: any; public logs: string[] = []; public errors: string[] = []; public warns: string[] = []; start() { this.originalConsole = { ...console }; console.log = (...args) => this.logs.push(args.join(' ')); console.error = (...args) => this.errors.push(args.join(' ')); console.warn = (...args) => this.warns.push(args.join(' ')); } stop() { Object.assign(console, this.originalConsole); } clear() { this.logs = []; this.errors = []; this.warns = []; } } /** * Asserts that a promise throws an error with a specific message */ export async function expectToThrow( promise: Promise<any>, expectedMessage?: string | RegExp ): Promise<Error> { try { await promise; throw new Error('Expected promise to throw, but it resolved'); } catch (error) { if (expectedMessage) { if (typeof expectedMessage === 'string') { expect((error as Error).message).toContain(expectedMessage); } else { expect((error as Error).message).toMatch(expectedMessage); } } return error as Error; } } /** * Create a mock timer for testing time-dependent code */ export class MockTimer { private originalNow: () => number; private currentTime: number; constructor(startTime: number = Date.now()) { this.originalNow = Date.now; this.currentTime = startTime; Date.now = () => this.currentTime; } advance(ms: number) { this.currentTime += ms; } set(time: number) { this.currentTime = time; } restore() { Date.now = this.originalNow; } }

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/Automattic/mcp-wordpress-remote'

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