@meldscience/meld
Version:
pipeable one-shot prompt scripting toolkit
150 lines (124 loc) • 4.99 kB
text/typescript
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
import { loadConfig, setupConfig, RC_FILE } from '../src/config';
import { writeFileSync, unlinkSync, existsSync, readFileSync } from 'fs';
import { join } from 'path';
import { homedir } from 'os';
import { execSync } from 'child_process';
// Mock execSync
vi.mock('child_process', () => ({
execSync: vi.fn()
}));
const RC_PATH = join(homedir(), RC_FILE);
describe('Config', () => {
let originalRcContent: string | null = null;
let originalEnv: NodeJS.ProcessEnv;
beforeEach(() => {
// Backup environment
originalEnv = { ...process.env };
// Clear environment variables we're testing
delete process.env.ANTHROPIC_API_KEY;
delete process.env.OPENAI_API_KEY;
delete process.env.DEFAULT_MODEL;
// Backup existing config if it exists
if (existsSync(RC_PATH)) {
originalRcContent = readFileSync(RC_PATH, 'utf-8');
unlinkSync(RC_PATH); // Remove the file for clean tests
}
// Reset execSync mock
vi.mocked(execSync).mockReset();
});
afterEach(() => {
// Restore environment
process.env = originalEnv;
// Restore original config or remove test config
if (originalRcContent) {
writeFileSync(RC_PATH, originalRcContent);
} else if (existsSync(RC_PATH)) {
unlinkSync(RC_PATH);
}
});
it('should load config from environment variables', () => {
// Set environment variables
process.env.ANTHROPIC_API_KEY = 'test-anthropic-key';
process.env.OPENAI_API_KEY = 'test-openai-key';
process.env.DEFAULT_MODEL = 'test-model';
// Debug: Print environment variables
console.log('Environment variables before loadConfig:');
console.log('ANTHROPIC_API_KEY:', process.env.ANTHROPIC_API_KEY);
console.log('OPENAI_API_KEY:', process.env.OPENAI_API_KEY);
console.log('DEFAULT_MODEL:', process.env.DEFAULT_MODEL);
const config = loadConfig();
// Debug: Print loaded config
console.log('Loaded config:');
console.log('config.anthropicApiKey:', config.anthropicApiKey);
console.log('config.openaiApiKey:', config.openaiApiKey);
console.log('config.defaultModel:', config.defaultModel);
expect(config.anthropicApiKey).toBe('test-anthropic-key');
expect(config.openaiApiKey).toBe('test-openai-key');
expect(config.defaultModel).toBe('test-model');
});
it('should load config from rc file', () => {
const testConfig = {
anthropicApiKey: 'test-anthropic-key',
openaiApiKey: 'test-openai-key',
defaultModel: 'test-model'
};
writeFileSync(RC_PATH, JSON.stringify(testConfig));
const config = loadConfig();
expect(config.anthropicApiKey).toBe('test-anthropic-key');
expect(config.openaiApiKey).toBe('test-openai-key');
expect(config.defaultModel).toBe('test-model');
});
it('should resolve 1Password secret references', () => {
// Mock 1Password CLI responses
vi.mocked(execSync)
.mockReturnValueOnce('test-anthropic-key')
.mockReturnValueOnce('test-openai-key');
const testConfig = {
anthropicApiKey: 'op://vault/anthropic/api-key',
openaiApiKey: 'op://vault/openai/api-key',
defaultModel: 'test-model'
};
writeFileSync(RC_PATH, JSON.stringify(testConfig));
const config = loadConfig();
expect(config.anthropicApiKey).toBe('test-anthropic-key');
expect(config.openaiApiKey).toBe('test-openai-key');
expect(config.defaultModel).toBe('test-model');
// Verify 1Password CLI was called correctly
expect(execSync).toHaveBeenCalledWith('op read "op://vault/anthropic/api-key"', { encoding: 'utf-8' });
expect(execSync).toHaveBeenCalledWith('op read "op://vault/openai/api-key"', { encoding: 'utf-8' });
});
it('should handle 1Password CLI errors gracefully', () => {
// Mock 1Password CLI error
vi.mocked(execSync).mockImplementation(() => {
const error = new Error('1Password CLI not found');
error.message = '1Password CLI not found';
throw error;
});
const testConfig = {
anthropicApiKey: 'op://vault/anthropic/api-key'
};
writeFileSync(RC_PATH, JSON.stringify(testConfig));
expect(() => loadConfig()).toThrow('Failed to resolve 1Password secret reference');
});
it('should override rc file with passed options', () => {
const testConfig = {
anthropicApiKey: 'test-anthropic-key',
openaiApiKey: 'test-openai-key',
defaultModel: 'test-model'
};
writeFileSync(RC_PATH, JSON.stringify(testConfig));
const config = loadConfig({
anthropicApiKey: 'override-key'
});
expect(config.anthropicApiKey).toBe('override-key');
expect(config.openaiApiKey).toBe('test-openai-key');
expect(config.defaultModel).toBe('test-model');
});
it('should handle missing rc file gracefully', () => {
if (existsSync(RC_PATH)) {
unlinkSync(RC_PATH);
}
expect(() => loadConfig()).not.toThrow();
});
});