Files
turbostarter/packages/cognitive-context/src/config.test.ts
Alejandro Gutiérrez 3527e732d4 feat: turbostarter boilerplate
Production-ready Next.js boilerplate with:
- Runtime env validation (fail-fast on missing vars)
- Feature-gated config (S3, Stripe, email, OAuth)
- Docker + Coolify deployment pipeline
- PostgreSQL + pgvector, MinIO S3, Better Auth
- TypeScript strict mode (no ignoreBuildErrors)
- i18n (en/es), AI modules, billing, monitoring

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 01:01:55 +01:00

287 lines
8.2 KiB
TypeScript

/**
* Tests for configuration loader
*/
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { mkdir, writeFile, rm } from 'node:fs/promises';
import { join } from 'node:path';
import { tmpdir } from 'node:os';
import {
findConfigFile,
loadConfig,
initConfig,
loadConfigOrDefault,
ConfigNotFoundError,
ConfigParseError,
ConfigValidationError,
} from './config.js';
import { DEFAULT_CONFIG } from './config.schema.js';
// ============================================
// Test Fixtures
// ============================================
const VALID_CONFIG = `
version: "1.0"
sourceDirs:
- src
- lib
includePatterns:
- "**/*.ts"
tokenBudget:
summary: 400
total: 25000
tools:
- name: cursor
enabled: true
`;
const MINIMAL_CONFIG = `
version: "1.0"
`;
const INVALID_YAML = `
version: "1.0"
sourceDirs:
- src
invalid yaml here
- missing colon
`;
const INVALID_CONFIG = `
version: "2.0"
tokenBudget:
summary: 50
`;
// ============================================
// Test Helpers
// ============================================
async function createTempDir(): Promise<string> {
const tempDir = join(tmpdir(), `cognitive-test-${Date.now()}-${Math.random().toString(36).slice(2)}`);
await mkdir(tempDir, { recursive: true });
return tempDir;
}
async function createNestedDirs(base: string, ...paths: string[]): Promise<string> {
const fullPath = join(base, ...paths);
await mkdir(fullPath, { recursive: true });
return fullPath;
}
// ============================================
// Tests
// ============================================
describe('findConfigFile', () => {
let tempDir: string;
beforeEach(async () => {
tempDir = await createTempDir();
});
afterEach(async () => {
await rm(tempDir, { recursive: true, force: true });
});
it('should find cognitive.config.yaml in current directory', async () => {
const configPath = join(tempDir, 'cognitive.config.yaml');
await writeFile(configPath, VALID_CONFIG);
const result = await findConfigFile(tempDir);
expect(result).toBe(configPath);
});
it('should find cognitive.config.yml as fallback', async () => {
const configPath = join(tempDir, 'cognitive.config.yml');
await writeFile(configPath, VALID_CONFIG);
const result = await findConfigFile(tempDir);
expect(result).toBe(configPath);
});
it('should find .cognitive/config.yaml as fallback', async () => {
const cognitiveDir = join(tempDir, '.cognitive');
await mkdir(cognitiveDir);
const configPath = join(cognitiveDir, 'config.yaml');
await writeFile(configPath, VALID_CONFIG);
const result = await findConfigFile(tempDir);
expect(result).toBe(configPath);
});
it('should prefer cognitive.config.yaml over alternatives', async () => {
const yamlPath = join(tempDir, 'cognitive.config.yaml');
const ymlPath = join(tempDir, 'cognitive.config.yml');
await writeFile(yamlPath, VALID_CONFIG);
await writeFile(ymlPath, VALID_CONFIG);
const result = await findConfigFile(tempDir);
expect(result).toBe(yamlPath);
});
it('should walk up directory tree to find config', async () => {
const nestedDir = await createNestedDirs(tempDir, 'a', 'b', 'c');
const configPath = join(tempDir, 'cognitive.config.yaml');
await writeFile(configPath, VALID_CONFIG);
const result = await findConfigFile(nestedDir);
expect(result).toBe(configPath);
});
it('should return null when no config exists', async () => {
const result = await findConfigFile(tempDir);
expect(result).toBeNull();
});
});
describe('loadConfig', () => {
let tempDir: string;
beforeEach(async () => {
tempDir = await createTempDir();
});
afterEach(async () => {
await rm(tempDir, { recursive: true, force: true });
});
it('should load and validate a valid config', async () => {
const configPath = join(tempDir, 'cognitive.config.yaml');
await writeFile(configPath, VALID_CONFIG);
const config = await loadConfig(tempDir);
expect(config.version).toBe('1.0');
expect(config.sourceDirs).toEqual(['src', 'lib']);
expect(config.includePatterns).toEqual(['**/*.ts']);
expect(config.tokenBudget.summary).toBe(400);
expect(config.tokenBudget.total).toBe(25000);
expect(config.projectRoot).toBe(tempDir);
});
it('should apply defaults for missing fields', async () => {
const configPath = join(tempDir, 'cognitive.config.yaml');
await writeFile(configPath, MINIMAL_CONFIG);
const config = await loadConfig(tempDir);
expect(config.version).toBe('1.0');
expect(config.sourceDirs).toEqual(DEFAULT_CONFIG.sourceDirs);
expect(config.includePatterns).toEqual(DEFAULT_CONFIG.includePatterns);
expect(config.excludePatterns).toEqual(DEFAULT_CONFIG.excludePatterns);
expect(config.tokenBudget).toEqual(DEFAULT_CONFIG.tokenBudget);
expect(config.outputDir).toBe('.cognitive');
});
it('should throw ConfigNotFoundError when no config exists', async () => {
await expect(loadConfig(tempDir)).rejects.toThrow(ConfigNotFoundError);
});
it('should throw ConfigParseError for invalid YAML', async () => {
const configPath = join(tempDir, 'cognitive.config.yaml');
await writeFile(configPath, INVALID_YAML);
await expect(loadConfig(tempDir)).rejects.toThrow(ConfigParseError);
});
it('should throw ConfigValidationError for invalid config values', async () => {
const configPath = join(tempDir, 'cognitive.config.yaml');
await writeFile(configPath, INVALID_CONFIG);
await expect(loadConfig(tempDir)).rejects.toThrow(ConfigValidationError);
});
it('should handle empty config file', async () => {
const configPath = join(tempDir, 'cognitive.config.yaml');
await writeFile(configPath, '');
const config = await loadConfig(tempDir);
// Should apply all defaults
expect(config.version).toBe('1.0');
expect(config.sourceDirs).toEqual(DEFAULT_CONFIG.sourceDirs);
});
it('should resolve relative projectRoot to absolute path', async () => {
const configPath = join(tempDir, 'cognitive.config.yaml');
await writeFile(configPath, `
version: "1.0"
projectRoot: "./subdir"
`);
await mkdir(join(tempDir, 'subdir'));
const config = await loadConfig(tempDir);
expect(config.projectRoot).toBe(join(tempDir, 'subdir'));
});
});
describe('initConfig', () => {
let tempDir: string;
beforeEach(async () => {
tempDir = await createTempDir();
});
afterEach(async () => {
await rm(tempDir, { recursive: true, force: true });
});
it('should create a default config file', async () => {
const configPath = await initConfig(tempDir);
expect(configPath).toBe(join(tempDir, 'cognitive.config.yaml'));
// Should be loadable
const config = await loadConfig(tempDir);
expect(config.version).toBe('1.0');
});
it('should throw if config already exists', async () => {
await writeFile(join(tempDir, 'cognitive.config.yaml'), VALID_CONFIG);
await expect(initConfig(tempDir)).rejects.toThrow('already exists');
});
it('should throw if directory does not exist', async () => {
const nonExistent = join(tempDir, 'nonexistent');
await expect(initConfig(nonExistent)).rejects.toThrow('does not exist');
});
});
describe('loadConfigOrDefault', () => {
let tempDir: string;
beforeEach(async () => {
tempDir = await createTempDir();
});
afterEach(async () => {
await rm(tempDir, { recursive: true, force: true });
});
it('should load config when it exists', async () => {
const configPath = join(tempDir, 'cognitive.config.yaml');
await writeFile(configPath, VALID_CONFIG);
const config = await loadConfigOrDefault(tempDir);
expect(config.tokenBudget.summary).toBe(400);
});
it('should return defaults when no config exists', async () => {
const config = await loadConfigOrDefault(tempDir);
expect(config.version).toBe(DEFAULT_CONFIG.version);
expect(config.sourceDirs).toEqual(DEFAULT_CONFIG.sourceDirs);
expect(config.projectRoot).toBe(tempDir);
});
it('should still throw for parse errors', async () => {
const configPath = join(tempDir, 'cognitive.config.yaml');
await writeFile(configPath, INVALID_YAML);
await expect(loadConfigOrDefault(tempDir)).rejects.toThrow(ConfigParseError);
});
});