import { describe, it, expect, vi } from 'vitest'; import { resolveServerEnv } from '../src/services/env-resolver.js'; import type { ISecretRepository } from '../src/repositories/interfaces.js'; import type { McpServer } from '@prisma/client'; function makeServer(env: unknown[]): McpServer { return { id: 'srv-1', name: 'test-server', description: '', packageName: null, dockerImage: 'test:latest', transport: 'STDIO', repositoryUrl: null, externalUrl: null, command: null, containerPort: null, replicas: 1, env, version: 1, createdAt: new Date(), updatedAt: new Date(), } as McpServer; } function mockSecretRepo(secrets: Record>): ISecretRepository { return { findAll: vi.fn(async () => []), findById: vi.fn(async () => null), findByName: vi.fn(async (name: string) => { const data = secrets[name]; if (!data) return null; return { id: `sec-${name}`, name, data, version: 1, createdAt: new Date(), updatedAt: new Date() }; }), create: vi.fn(async () => ({} as never)), update: vi.fn(async () => ({} as never)), delete: vi.fn(async () => {}), }; } describe('resolveServerEnv', () => { it('resolves inline values', async () => { const server = makeServer([ { name: 'FOO', value: 'bar' }, { name: 'BAZ', value: 'qux' }, ]); const repo = mockSecretRepo({}); const result = await resolveServerEnv(server, repo); expect(result).toEqual({ FOO: 'bar', BAZ: 'qux' }); }); it('resolves secret references', async () => { const server = makeServer([ { name: 'TOKEN', valueFrom: { secretRef: { name: 'ha-creds', key: 'HOMEASSISTANT_TOKEN' } } }, ]); const repo = mockSecretRepo({ 'ha-creds': { HOMEASSISTANT_TOKEN: 'secret-token-123' }, }); const result = await resolveServerEnv(server, repo); expect(result).toEqual({ TOKEN: 'secret-token-123' }); }); it('handles mixed inline and secret refs', async () => { const server = makeServer([ { name: 'URL', value: 'https://ha.local' }, { name: 'TOKEN', valueFrom: { secretRef: { name: 'creds', key: 'TOKEN' } } }, ]); const repo = mockSecretRepo({ creds: { TOKEN: 'my-token' }, }); const result = await resolveServerEnv(server, repo); expect(result).toEqual({ URL: 'https://ha.local', TOKEN: 'my-token' }); }); it('caches secret lookups', async () => { const server = makeServer([ { name: 'A', valueFrom: { secretRef: { name: 'shared', key: 'KEY_A' } } }, { name: 'B', valueFrom: { secretRef: { name: 'shared', key: 'KEY_B' } } }, ]); const repo = mockSecretRepo({ shared: { KEY_A: 'val-a', KEY_B: 'val-b' }, }); const result = await resolveServerEnv(server, repo); expect(result).toEqual({ A: 'val-a', B: 'val-b' }); expect(repo.findByName).toHaveBeenCalledTimes(1); }); it('throws when secret not found', async () => { const server = makeServer([ { name: 'TOKEN', valueFrom: { secretRef: { name: 'missing', key: 'TOKEN' } } }, ]); const repo = mockSecretRepo({}); await expect(resolveServerEnv(server, repo)).rejects.toThrow("Secret 'missing' not found"); }); it('throws when secret key not found', async () => { const server = makeServer([ { name: 'TOKEN', valueFrom: { secretRef: { name: 'creds', key: 'NONEXISTENT' } } }, ]); const repo = mockSecretRepo({ creds: { OTHER_KEY: 'val' }, }); await expect(resolveServerEnv(server, repo)).rejects.toThrow("Key 'NONEXISTENT' not found in secret 'creds'"); }); it('returns empty map for empty env', async () => { const server = makeServer([]); const repo = mockSecretRepo({}); const result = await resolveServerEnv(server, repo); expect(result).toEqual({}); }); });