import { describe, it, expect } from 'vitest'; import { ProfileRegistry, defaultRegistry, profileTemplateSchema, validateTemplate, getMissingEnvVars, instantiateProfile, generateMcpJsonEntry, filesystemTemplate, githubTemplate, postgresTemplate, slackTemplate, memoryTemplate, fetchTemplate, } from '../src/profiles/index.js'; const allTemplates = [ filesystemTemplate, githubTemplate, postgresTemplate, slackTemplate, memoryTemplate, fetchTemplate, ]; describe('ProfileTemplate schema', () => { it.each(allTemplates)('validates $id template', (template) => { const result = profileTemplateSchema.safeParse(template); expect(result.success).toBe(true); }); it('rejects template with missing required fields', () => { const result = profileTemplateSchema.safeParse({ id: 'x' }); expect(result.success).toBe(false); }); it('rejects template with invalid id format', () => { const result = profileTemplateSchema.safeParse({ ...filesystemTemplate, id: 'Invalid ID!', }); expect(result.success).toBe(false); }); it('rejects template with invalid category', () => { const result = profileTemplateSchema.safeParse({ ...filesystemTemplate, category: 'nonexistent', }); expect(result.success).toBe(false); }); }); describe('ProfileRegistry', () => { it('default registry contains all builtin templates', () => { const ids = defaultRegistry.getAll().map((t) => t.id); expect(ids).toContain('filesystem'); expect(ids).toContain('github'); expect(ids).toContain('postgres'); expect(ids).toContain('slack'); expect(ids).toContain('memory'); expect(ids).toContain('fetch'); }); it('has no duplicate IDs', () => { const ids = defaultRegistry.getAll().map((t) => t.id); expect(new Set(ids).size).toBe(ids.length); }); it('getById returns correct template', () => { expect(defaultRegistry.getById('github')).toBe(githubTemplate); expect(defaultRegistry.getById('nonexistent')).toBeUndefined(); }); it('getByCategory filters correctly', () => { const integrations = defaultRegistry.getByCategory('integration'); expect(integrations.map((t) => t.id)).toEqual( expect.arrayContaining(['github', 'slack']), ); for (const t of integrations) { expect(t.category).toBe('integration'); } }); it('getCategories returns unique categories', () => { const cats = defaultRegistry.getCategories(); expect(cats.length).toBeGreaterThan(0); expect(new Set(cats).size).toBe(cats.length); }); it('search finds by name', () => { const results = defaultRegistry.search('git'); expect(results.some((t) => t.id === 'github')).toBe(true); }); it('search finds by description', () => { const results = defaultRegistry.search('knowledge graph'); expect(results.some((t) => t.id === 'memory')).toBe(true); }); it('search returns empty for no match', () => { expect(defaultRegistry.search('zzzznotfound')).toEqual([]); }); it('register adds a custom template', () => { const registry = new ProfileRegistry([]); registry.register(filesystemTemplate); expect(registry.has('filesystem')).toBe(true); expect(registry.getAll()).toHaveLength(1); }); }); describe('validateTemplate', () => { it('returns success for valid template', () => { const result = validateTemplate(filesystemTemplate); expect(result.success).toBe(true); }); it('returns errors for invalid template', () => { const result = validateTemplate({ id: '' }); expect(result.success).toBe(false); if (!result.success) { expect(result.errors.length).toBeGreaterThan(0); } }); }); describe('getMissingEnvVars', () => { it('returns empty for template with no required env vars', () => { expect(getMissingEnvVars(filesystemTemplate, {})).toEqual([]); }); it('returns missing vars for github template', () => { const missing = getMissingEnvVars(githubTemplate, {}); expect(missing).toContain('GITHUB_PERSONAL_ACCESS_TOKEN'); }); it('returns empty when all vars provided', () => { const missing = getMissingEnvVars(githubTemplate, { GITHUB_PERSONAL_ACCESS_TOKEN: 'ghp_xxx', }); expect(missing).toEqual([]); }); }); describe('instantiateProfile', () => { it('creates profile from template without env vars', () => { const profile = instantiateProfile(filesystemTemplate, {}); expect(profile.name).toBe('filesystem'); expect(profile.templateId).toBe('filesystem'); expect(profile.command).toBe('npx'); expect(profile.args).toEqual(['-y', '@modelcontextprotocol/server-filesystem']); expect(profile.env).toEqual({}); }); it('creates profile with env vars', () => { const profile = instantiateProfile(githubTemplate, { GITHUB_PERSONAL_ACCESS_TOKEN: 'ghp_test123', }); expect(profile.env.GITHUB_PERSONAL_ACCESS_TOKEN).toBe('ghp_test123'); }); it('throws on missing required env vars', () => { expect(() => instantiateProfile(githubTemplate, {})).toThrow( 'Missing required environment variables', ); }); it('includes optional env vars when provided', () => { const profile = instantiateProfile(filesystemTemplate, { SOME_OPTIONAL: 'value', }); // Optional vars not in template are not included expect(profile.env).toEqual({}); }); }); describe('generateMcpJsonEntry', () => { it('generates valid .mcp.json entry', () => { const profile = instantiateProfile(githubTemplate, { GITHUB_PERSONAL_ACCESS_TOKEN: 'ghp_abc', }); const entry = generateMcpJsonEntry(profile); expect(entry).toEqual({ github: { command: 'npx', args: ['-y', '@modelcontextprotocol/server-github'], env: { GITHUB_PERSONAL_ACCESS_TOKEN: 'ghp_abc', }, }, }); }); it('generates entry with empty env for no-env template', () => { const profile = instantiateProfile(memoryTemplate, {}); const entry = generateMcpJsonEntry(profile); expect(entry).toEqual({ memory: { command: 'npx', args: ['-y', '@modelcontextprotocol/server-memory'], env: {}, }, }); }); });