aios-core
Version:
Synkra AIOS: AI-Orchestrated System for Full Stack Development - Core Framework
192 lines (141 loc) • 6.14 kB
JavaScript
/**
* @fileoverview Tests for EnvMerger
* Story 9.2: .env File Merge Implementation
*/
const { EnvMerger } = require('../../../src/merger/strategies/env-merger.js');
const { parseEnvFile } = require('../../../src/merger/parsers/env-parser.js');
describe('EnvMerger', () => {
let merger;
beforeEach(() => {
merger = new EnvMerger();
});
describe('canMerge', () => {
it('should always return true for .env files', () => {
expect(merger.canMerge('', '')).toBe(true);
expect(merger.canMerge('KEY=value', 'OTHER=value')).toBe(true);
});
});
describe('merge', () => {
it('should add new variables from new content', async () => {
const existing = 'EXISTING_VAR=existing_value';
const newContent = 'NEW_VAR=new_value';
const result = await merger.merge(existing, newContent);
expect(result.content).toContain('EXISTING_VAR=existing_value');
expect(result.content).toContain('NEW_VAR=new_value');
expect(result.stats.preserved).toBe(1);
expect(result.stats.added).toBe(1);
});
it('should preserve existing values over new values', async () => {
const existing = 'API_KEY=my_secret_key';
const newContent = 'API_KEY=placeholder_key';
const result = await merger.merge(existing, newContent);
// The original value is preserved at the start
expect(result.content.startsWith('API_KEY=my_secret_key')).toBe(true);
// The suggested value appears in AIOS_SUGGESTED comment
expect(result.content).toContain('AIOS_SUGGESTED: API_KEY=placeholder_key');
expect(result.stats.preserved).toBe(1);
});
it('should add AIOS_SUGGESTED comment for differing values', async () => {
const existing = 'PORT=3000';
const newContent = 'PORT=8080';
const result = await merger.merge(existing, newContent);
expect(result.content).toContain('PORT=3000');
expect(result.content).toContain('# AIOS_SUGGESTED: PORT=8080');
expect(result.stats.conflicts).toBe(1);
});
it('should preserve comments from existing file', async () => {
const existing = `# Database configuration
DB_HOST=localhost
DB_PORT=5432`;
const newContent = 'NEW_VAR=value';
const result = await merger.merge(existing, newContent);
expect(result.content).toContain('# Database configuration');
expect(result.content).toContain('DB_HOST=localhost');
});
it('should add new variables in AIOS Variables section', async () => {
const existing = 'EXISTING=value';
const newContent = `AIOS_VAR1=value1
AIOS_VAR2=value2`;
const result = await merger.merge(existing, newContent);
// Header includes date: "# === AIOS Variables (added YYYY-MM-DD) ==="
expect(result.content).toMatch(/# === AIOS Variables \(added \d{4}-\d{2}-\d{2}\) ===/);
expect(result.content).toContain('AIOS_VAR1=value1');
expect(result.content).toContain('AIOS_VAR2=value2');
});
it('should handle empty existing content', async () => {
const existing = '';
const newContent = 'NEW_VAR=value';
const result = await merger.merge(existing, newContent);
expect(result.content).toContain('NEW_VAR=value');
expect(result.stats.added).toBe(1);
});
it('should handle empty new content', async () => {
const existing = 'EXISTING=value';
const newContent = '';
const result = await merger.merge(existing, newContent);
expect(result.content).toContain('EXISTING=value');
expect(result.stats.preserved).toBe(1);
});
it('should handle multiline values', async () => {
const existing = `PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----
MIIEpQIBAAKCAQEA
-----END RSA PRIVATE KEY-----"`;
const newContent = 'OTHER=value';
const result = await merger.merge(existing, newContent);
expect(result.content).toContain('PRIVATE_KEY=');
expect(result.content).toContain('OTHER=value');
});
});
describe('preview', () => {
it('should return same result as merge with preview option', async () => {
const existing = 'A=1';
const newContent = 'B=2';
const previewResult = await merger.preview(existing, newContent);
const mergeResult = await merger.merge(existing, newContent, { preview: true });
expect(previewResult.content).toBe(mergeResult.content);
expect(previewResult.stats).toEqual(mergeResult.stats);
});
});
});
describe('parseEnvFile', () => {
it('should parse simple key-value pairs', () => {
const content = `KEY1=value1
KEY2=value2`;
const result = parseEnvFile(content);
expect(result.variables.get('KEY1').value).toBe('value1');
expect(result.variables.get('KEY2').value).toBe('value2');
});
it('should handle quoted values', () => {
const content = `QUOTED="value with spaces"
SINGLE='single quoted'`;
const result = parseEnvFile(content);
expect(result.variables.get('QUOTED').value).toBe('"value with spaces"');
expect(result.variables.get('SINGLE').value).toBe("'single quoted'");
});
it('should preserve comments', () => {
const content = `# This is a comment
KEY=value
# Another comment`;
const result = parseEnvFile(content);
expect(result.comments.length).toBeGreaterThan(0);
expect(result.variables.get('KEY').value).toBe('value');
});
it('should handle empty lines', () => {
const content = `KEY1=value1
KEY2=value2`;
const result = parseEnvFile(content);
expect(result.variables.get('KEY1').value).toBe('value1');
expect(result.variables.get('KEY2').value).toBe('value2');
});
it('should handle values with equals signs', () => {
const content = 'URL=https://example.com?param=value';
const result = parseEnvFile(content);
expect(result.variables.get('URL').value).toBe('https://example.com?param=value');
});
it('should handle empty values', () => {
const content = 'EMPTY_VAR=';
const result = parseEnvFile(content);
expect(result.variables.has('EMPTY_VAR')).toBe(true);
expect(result.variables.get('EMPTY_VAR').value).toBe('');
});
});