Skip to main content
Glama
config-loader.test.ts11.6 kB
import { describe, it, vi, beforeEach, afterEach, expect } from 'vitest'; import * as cliParser from '../cli-parser'; import { readFile } from 'fs/promises'; import { homedir } from 'os'; // Mock dependencies vi.mock('fs/promises'); vi.mock('os', () => ({ homedir: vi.fn(() => '/home/user') })); vi.mock('../cli-parser'); // Import after mocking to ensure mocks are applied import { loadConfiguration, getConfig, resetConfig } from '../config-loader'; describe('config-loader', () => { let originalConsoleError: typeof console.error; beforeEach(() => { vi.clearAllMocks(); // Reset config cache before each test resetConfig(); // Mock console.error to suppress expected error messages during tests originalConsoleError = console.error; console.error = vi.fn(); // Setup default mocks vi.mocked(cliParser.parseCliArgs).mockResolvedValue({}); vi.mocked(cliParser.getConfigPathFromArgs).mockReturnValue(undefined); vi.mocked(homedir).mockReturnValue('/home/user'); vi.mocked(readFile).mockRejectedValue(new Error('File not found')); }); afterEach(() => { vi.restoreAllMocks(); // Restore console.error console.error = originalConsoleError; // Clean up environment variables delete process.env.VITEST_MCP_CONFIG; delete process.env.VITEST_MCP_TEST_FORMAT; delete process.env.VITEST_MCP_TEST_TIMEOUT; delete process.env.VITEST_MCP_COVERAGE_THRESHOLD; delete process.env.VITEST_MCP_VERBOSE; }); describe('loadConfiguration - Core Functionality', () => { it('should return default configuration when no config files exist', async () => { // Arrange vi.mocked(cliParser.parseCliArgs).mockResolvedValue({}); vi.mocked(cliParser.getConfigPathFromArgs).mockReturnValue(undefined); // Act const config = await loadConfiguration([]); // Assert expect(config).toBeDefined(); expect(config.testDefaults.format).toBe('summary'); expect(config.testDefaults.timeout).toBe(30000); expect(config.server.verbose).toBe(false); }); it('should load and parse valid JSON configuration file', async () => { // Arrange const fileConfig = { testDefaults: { format: 'detailed', timeout: 60000 } }; vi.mocked(readFile).mockResolvedValue(JSON.stringify(fileConfig)); // Act const config = await loadConfiguration([]); // Assert expect(config.testDefaults.format).toBe('detailed'); expect(config.testDefaults.timeout).toBe(60000); }); it('should handle malformed JSON gracefully', async () => { // Arrange vi.mocked(readFile).mockResolvedValue('{ invalid json }'); // Act const config = await loadConfiguration([]); // Assert expect(config).toBeDefined(); expect(config.testDefaults.format).toBe('summary'); // Should fall back to defaults }); it('should prioritize CLI args over file configuration', async () => { // Arrange const fileConfig = { testDefaults: { format: 'detailed' } }; vi.mocked(readFile).mockResolvedValue(JSON.stringify(fileConfig)); vi.mocked(cliParser.parseCliArgs).mockResolvedValue({ testDefaults: { format: 'summary' } }); // Act const config = await loadConfiguration(['--format', 'summary']); // Assert expect(config.testDefaults.format).toBe('summary'); }); it('should load configuration from explicit CLI path', async () => { // Arrange const fileConfig = { testDefaults: { timeout: 45000 } }; vi.mocked(cliParser.getConfigPathFromArgs).mockReturnValue('/custom/config.json'); vi.mocked(readFile).mockResolvedValue(JSON.stringify(fileConfig)); // Act const config = await loadConfiguration(['--config', '/custom/config.json']); // Assert expect(config.testDefaults.timeout).toBe(45000); }); }); describe('Environment Variable Loading', () => { it('should load test format from environment variables', async () => { // Arrange process.env.VITEST_MCP_TEST_FORMAT = 'detailed'; // Act const config = await loadConfiguration([]); // Assert expect(config.testDefaults.format).toBe('detailed'); }); it.skip('should load coverage threshold from environment variables', async () => { // Arrange process.env.VITEST_MCP_COVERAGE_THRESHOLD = '90'; // Act const config = await loadConfiguration([]); // Assert expect(config.coverageDefaults.threshold).toBe(90); expect(config.coverageDefaults.thresholdsExplicitlySet).toBe(true); }); it.skip('should load specific coverage thresholds from environment', async () => { // Arrange process.env.VITEST_MCP_COVERAGE_THRESHOLD_LINES = '85'; process.env.VITEST_MCP_COVERAGE_THRESHOLD_BRANCHES = '75'; // Act const config = await loadConfiguration([]); // Assert expect(config.coverageDefaults.thresholds.lines).toBe(85); expect(config.coverageDefaults.thresholds.branches).toBe(75); expect(config.coverageDefaults.thresholdsExplicitlySet).toBe(true); }); it('should load server settings from environment variables', async () => { // Arrange process.env.VITEST_MCP_VERBOSE = 'true'; process.env.VITEST_MCP_WORKING_DIR = '/custom/dir'; // Act const config = await loadConfiguration([]); // Assert expect(config.server.verbose).toBe(true); expect(config.server.workingDirectory).toBe('/custom/dir'); }); it('should load configuration from environment variable path', async () => { // Arrange const fileConfig = { testDefaults: { timeout: 25000 } }; process.env.VITEST_MCP_CONFIG = '/env/config.json'; vi.mocked(readFile).mockResolvedValue(JSON.stringify(fileConfig)); // Act const config = await loadConfiguration([]); // Assert expect(config.testDefaults.timeout).toBe(25000); }); }); describe('Configuration Merging and Precedence', () => { it('should merge nested configuration objects correctly', async () => { // Arrange const fileConfig = { testDefaults: { format: 'detailed', timeout: 60000 } }; vi.mocked(readFile).mockResolvedValue(JSON.stringify(fileConfig)); vi.mocked(cliParser.parseCliArgs).mockResolvedValue({ testDefaults: { format: 'summary' } // Only override format }); // Act const config = await loadConfiguration(['--format', 'summary']); // Assert expect(config.testDefaults.format).toBe('summary'); // CLI override expect(config.testDefaults.timeout).toBe(60000); // From file }); it('should handle configuration hierarchy correctly', async () => { // Arrange const fileConfig = { testDefaults: { timeout: 45000 } }; process.env.VITEST_MCP_TEST_FORMAT = 'detailed'; vi.mocked(readFile).mockResolvedValue(JSON.stringify(fileConfig)); vi.mocked(cliParser.parseCliArgs).mockResolvedValue({ server: { verbose: true } }); // Act const config = await loadConfiguration(['--verbose']); // Assert // Defaults < file < env < CLI expect(config.testDefaults.timeout).toBe(45000); // From file expect(config.testDefaults.format).toBe('detailed'); // From env expect(config.server.verbose).toBe(true); // From CLI }); it('should preserve default exclusion patterns when merging', async () => { // Arrange const fileConfig = { coverageDefaults: {} }; vi.mocked(readFile).mockResolvedValue(JSON.stringify(fileConfig)); // Act const config = await loadConfiguration([]); // Assert expect(config.coverageDefaults.exclude).toContain('**/*.stories.*'); expect(config.coverageDefaults.exclude).toContain('**/e2e/**'); }); it('should handle null and undefined values in merging', async () => { // Arrange const fileConfig = { testDefaults: { format: null, timeout: 50000 }, coverageDefaults: {} }; vi.mocked(readFile).mockResolvedValue(JSON.stringify(fileConfig)); // Act const config = await loadConfiguration([]); // Assert expect(config.testDefaults.format).toBe('summary'); // Should use default expect(config.testDefaults.timeout).toBe(50000); // Should use file value }); }); describe('Caching and Performance', () => { it('should cache configuration for repeated access', async () => { // Arrange const fileConfig = { testDefaults: { format: 'detailed' } }; vi.mocked(readFile).mockResolvedValue(JSON.stringify(fileConfig)); // Act const config1 = await getConfig([]); const config2 = await getConfig([]); // Assert expect(config1).toBe(config2); // Should be same instance expect(vi.mocked(readFile)).toHaveBeenCalledTimes(1); // File should only be read once }); it('should reload configuration when CLI args change', async () => { // Arrange vi.mocked(cliParser.parseCliArgs) .mockResolvedValueOnce({ testDefaults: { format: 'summary' } }) .mockResolvedValueOnce({ testDefaults: { format: 'detailed' } }); // Act const config1 = await getConfig(['--format', 'summary']); const config2 = await getConfig(['--format', 'detailed']); // Assert expect(config1.testDefaults.format).toBe('summary'); expect(config2.testDefaults.format).toBe('detailed'); expect(vi.mocked(cliParser.parseCliArgs)).toHaveBeenCalledTimes(2); }); it('should not reload configuration for identical CLI args', async () => { // Arrange vi.mocked(cliParser.parseCliArgs).mockResolvedValue({ testDefaults: { format: 'summary' } }); // Act await getConfig(['--format', 'summary']); await getConfig(['--format', 'summary']); // Assert expect(vi.mocked(cliParser.parseCliArgs)).toHaveBeenCalledTimes(1); }); }); describe('Error Handling', () => { it('should handle file permission errors gracefully', async () => { // Arrange const permissionError = new Error('Permission denied'); (permissionError as any).code = 'EACCES'; vi.mocked(readFile).mockRejectedValue(permissionError); // Act const config = await loadConfiguration([]); // Assert expect(config).toBeDefined(); expect(config.testDefaults.format).toBe('summary'); // Should fall back to defaults }); it('should handle explicit config file errors gracefully', async () => { // Arrange vi.mocked(cliParser.getConfigPathFromArgs).mockReturnValue('/explicit/config.json'); vi.mocked(readFile).mockRejectedValue(new Error('File not found')); // Act const config = await loadConfiguration(['--config', '/explicit/config.json']); // Assert expect(config).toBeDefined(); expect(config.testDefaults.format).toBe('summary'); // Should fall back to defaults }); it('should handle environment config file errors gracefully', async () => { // Arrange process.env.VITEST_MCP_CONFIG = '/env/config.json'; vi.mocked(readFile).mockRejectedValue(new Error('Access denied')); // Act const config = await loadConfiguration([]); // Assert expect(config).toBeDefined(); expect(config.testDefaults.format).toBe('summary'); // Should fall back to defaults }); }); });

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/djankies/vitest-mcp'

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