Skip to main content
Glama
resolver.test.ts6.71 kB
/** * Role Resolver Tests * * Tests for the role resolution logic (CLI > env > config priority). * * @package WP_Navigator_MCP * @since 1.2.0 */ import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'; import { resolveRole, formatRoleInfo, RoleNotFoundError } from './resolver.js'; import * as loader from './loader.js'; import type { LoadedRole } from './types.js'; // Mock the loader module vi.mock('./loader.js', async (importOriginal) => { const original = await importOriginal<typeof loader>(); return { ...original, getRole: vi.fn(), listAvailableRoles: vi.fn(), }; }); const mockGetRole = vi.mocked(loader.getRole); const mockListAvailableRoles = vi.mocked(loader.listAvailableRoles); // Sample role for testing const contentEditorRole: LoadedRole = { name: 'content-editor', description: 'Content editing focus', context: 'You are a content editor...', source: 'bundled', sourcePath: '/path/to/content-editor.yaml', focus_areas: ['writing', 'editing'], avoid: ['technical changes'], tools: { allowed: ['wpnav_list_posts'] }, }; const developerRole: LoadedRole = { name: 'developer', description: 'Developer focus', context: 'You are a developer...', source: 'bundled', sourcePath: '/path/to/developer.yaml', }; describe('resolveRole', () => { beforeEach(() => { vi.resetAllMocks(); // Default: roles exist mockGetRole.mockImplementation((name) => { if (name === 'content-editor') return contentEditorRole; if (name === 'developer') return developerRole; return null; }); mockListAvailableRoles.mockReturnValue(['content-editor', 'developer', 'site-admin']); // Clear env delete process.env.WPNAV_ROLE; }); afterEach(() => { delete process.env.WPNAV_ROLE; }); describe('priority resolution', () => { it('returns CLI role when provided (highest priority)', () => { process.env.WPNAV_ROLE = 'developer'; const result = resolveRole({ cliRole: 'content-editor', configDefaultRole: 'site-admin', }); expect(result.role).toBe(contentEditorRole); expect(result.source).toBe('cli'); expect(result.roleName).toBe('content-editor'); }); it('returns env role when CLI is not provided', () => { process.env.WPNAV_ROLE = 'developer'; const result = resolveRole({ configDefaultRole: 'content-editor', }); expect(result.role).toBe(developerRole); expect(result.source).toBe('env'); expect(result.roleName).toBe('developer'); }); it('returns config default role when CLI and env are not set', () => { const result = resolveRole({ configDefaultRole: 'content-editor', }); expect(result.role).toBe(contentEditorRole); expect(result.source).toBe('config'); expect(result.roleName).toBe('content-editor'); }); it('returns null role when nothing is set', () => { const result = resolveRole({}); expect(result.role).toBeNull(); expect(result.source).toBe('none'); expect(result.roleName).toBeNull(); }); }); describe('error handling', () => { it('throws RoleNotFoundError for invalid CLI role', () => { mockGetRole.mockImplementation(() => null); expect(() => resolveRole({ cliRole: 'nonexistent' })).toThrow(RoleNotFoundError); try { resolveRole({ cliRole: 'nonexistent' }); } catch (e) { expect(e).toBeInstanceOf(RoleNotFoundError); const err = e as RoleNotFoundError; expect(err.roleName).toBe('nonexistent'); expect(err.source).toBe('cli'); expect(err.availableRoles).toEqual(['content-editor', 'developer', 'site-admin']); } }); it('throws RoleNotFoundError for invalid env role', () => { mockGetRole.mockImplementation(() => null); process.env.WPNAV_ROLE = 'nonexistent'; expect(() => resolveRole({})).toThrow(RoleNotFoundError); try { resolveRole({}); } catch (e) { const err = e as RoleNotFoundError; expect(err.source).toBe('env'); } }); it('throws RoleNotFoundError for invalid config role', () => { mockGetRole.mockImplementation(() => null); expect(() => resolveRole({ configDefaultRole: 'nonexistent' })).toThrow(RoleNotFoundError); try { resolveRole({ configDefaultRole: 'nonexistent' }); } catch (e) { const err = e as RoleNotFoundError; expect(err.source).toBe('config'); } }); }); describe('empty options', () => { it('works with no options', () => { const result = resolveRole(); expect(result.role).toBeNull(); expect(result.source).toBe('none'); }); }); }); describe('RoleNotFoundError', () => { it('includes helpful error message', () => { const err = new RoleNotFoundError('test-role', ['content-editor', 'developer'], 'cli'); expect(err.message).toContain('test-role'); expect(err.message).toContain('--role flag'); expect(err.message).toContain('content-editor, developer'); expect(err.name).toBe('RoleNotFoundError'); }); it('handles empty available roles', () => { const err = new RoleNotFoundError('test-role', [], 'env'); expect(err.message).toContain('(none)'); }); it('shows correct source label for each source', () => { const cliErr = new RoleNotFoundError('r', [], 'cli'); const envErr = new RoleNotFoundError('r', [], 'env'); const configErr = new RoleNotFoundError('r', [], 'config'); expect(cliErr.message).toContain('--role flag'); expect(envErr.message).toContain('WPNAV_ROLE environment variable'); expect(configErr.message).toContain('config file default_role'); }); }); describe('formatRoleInfo', () => { it('formats active role', () => { const result = formatRoleInfo({ role: contentEditorRole, source: 'cli', roleName: 'content-editor', }); expect(result).toContain('content-editor'); expect(result).toContain('CLI --role'); expect(result).toContain('Content editing focus'); }); it('formats no role', () => { const result = formatRoleInfo({ role: null, source: 'none', roleName: null, }); expect(result).toBe('No role active'); }); it('shows different sources correctly', () => { expect( formatRoleInfo({ role: contentEditorRole, source: 'env', roleName: 'content-editor', }) ).toContain('WPNAV_ROLE'); expect( formatRoleInfo({ role: contentEditorRole, source: 'config', roleName: 'content-editor', }) ).toContain('config default_role'); }); });

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