/** * Tests for Config Validation */ import { describe, it, expect } from '@jest/globals'; import { DEFAULT_APP_CONFIG } from './types'; import { validateConfigKey, validateConfigValue, validateAppConfig } from './validation'; describe('validateConfigKey', () => { it('should accept valid JavaScript identifiers', () => { expect(validateConfigKey('apiKey').valid).toBe(true); expect(validateConfigKey('API_KEY').valid).toBe(true); expect(validateConfigKey('_privateKey').valid).toBe(true); expect(validateConfigKey('$special').valid).toBe(true); expect(validateConfigKey('key123').valid).toBe(true); }); it('should reject empty keys', () => { const result = validateConfigKey(''); expect(result.valid).toBe(false); expect(result.errors[0]).toContain('cannot be empty'); }); it('should reject invalid identifiers', () => { expect(validateConfigKey('123key').valid).toBe(false); expect(validateConfigKey('my-key').valid).toBe(false); expect(validateConfigKey('my key').valid).toBe(false); expect(validateConfigKey('my.key').valid).toBe(false); }); it('should reject reserved keys', () => { const result = validateConfigKey('appName'); expect(result.valid).toBe(false); expect(result.errors[0]).toContain('reserved'); }); }); describe('validateConfigValue', () => { describe('number type', () => { it('should accept valid numbers', () => { expect(validateConfigValue(42, 'number').valid).toBe(true); expect(validateConfigValue(0, 'number').valid).toBe(true); expect(validateConfigValue(-10.5, 'number').valid).toBe(true); }); it('should reject non-numbers', () => { expect(validateConfigValue('42', 'number').valid).toBe(false); expect(validateConfigValue(NaN, 'number').valid).toBe(false); }); it('should enforce min/max validation', () => { expect(validateConfigValue(5, 'number', { min: 0, max: 10 }).valid).toBe(true); expect(validateConfigValue(-1, 'number', { min: 0 }).valid).toBe(false); expect(validateConfigValue(11, 'number', { max: 10 }).valid).toBe(false); }); }); describe('string type', () => { it('should accept valid strings', () => { expect(validateConfigValue('hello', 'string').valid).toBe(true); expect(validateConfigValue('', 'string').valid).toBe(true); }); it('should reject non-strings', () => { expect(validateConfigValue(42, 'string').valid).toBe(false); }); it('should enforce pattern validation', () => { const result = validateConfigValue('abc', 'string', { pattern: '^[0-9]+$' }); expect(result.valid).toBe(false); const validResult = validateConfigValue('123', 'string', { pattern: '^[0-9]+$' }); expect(validResult.valid).toBe(true); }); }); describe('boolean type', () => { it('should accept booleans', () => { expect(validateConfigValue(true, 'boolean').valid).toBe(true); expect(validateConfigValue(false, 'boolean').valid).toBe(true); }); it('should reject non-booleans', () => { expect(validateConfigValue('true', 'boolean').valid).toBe(false); expect(validateConfigValue(1, 'boolean').valid).toBe(false); }); }); describe('color type', () => { it('should accept valid hex colors', () => { expect(validateConfigValue('#ff0000', 'color').valid).toBe(true); expect(validateConfigValue('#FF0000', 'color').valid).toBe(true); expect(validateConfigValue('#ff0000ff', 'color').valid).toBe(true); }); it('should reject invalid colors', () => { expect(validateConfigValue('red', 'color').valid).toBe(false); expect(validateConfigValue('#ff00', 'color').valid).toBe(false); expect(validateConfigValue('ff0000', 'color').valid).toBe(false); }); }); describe('array type', () => { it('should accept arrays', () => { expect(validateConfigValue([], 'array').valid).toBe(true); expect(validateConfigValue([1, 2, 3], 'array').valid).toBe(true); }); it('should reject non-arrays', () => { expect(validateConfigValue('[]', 'array').valid).toBe(false); expect(validateConfigValue({}, 'array').valid).toBe(false); }); }); describe('object type', () => { it('should accept objects', () => { expect(validateConfigValue({}, 'object').valid).toBe(true); expect(validateConfigValue({ key: 'value' }, 'object').valid).toBe(true); }); it('should reject non-objects', () => { expect(validateConfigValue([], 'object').valid).toBe(false); expect(validateConfigValue(null, 'object').valid).toBe(false); expect(validateConfigValue('{}', 'object').valid).toBe(false); }); }); describe('required validation', () => { it('should require non-empty values', () => { expect(validateConfigValue('', 'string', { required: true }).valid).toBe(false); expect(validateConfigValue(null, 'string', { required: true }).valid).toBe(false); expect(validateConfigValue(undefined, 'string', { required: true }).valid).toBe(false); }); it('should allow empty values when not required', () => { expect(validateConfigValue('', 'string').valid).toBe(true); expect(validateConfigValue(null, 'string').valid).toBe(true); }); }); }); describe('validateAppConfig', () => { it('should accept valid config', () => { const config = { ...DEFAULT_APP_CONFIG, identity: { appName: 'Test App', description: 'Test Description' } }; expect(validateAppConfig(config).valid).toBe(true); }); it('should require app name', () => { const config = { ...DEFAULT_APP_CONFIG, identity: { appName: '', description: '' } }; const result = validateAppConfig(config); expect(result.valid).toBe(false); expect(result.errors[0]).toContain('App name'); }); it('should detect duplicate variable keys', () => { const config = { ...DEFAULT_APP_CONFIG, identity: { appName: 'Test', description: '' }, variables: [ { key: 'apiKey', type: 'string' as const, value: 'key1' }, { key: 'apiKey', type: 'string' as const, value: 'key2' } ] }; const result = validateAppConfig(config); expect(result.valid).toBe(false); expect(result.errors.some((e) => e.includes('Duplicate'))).toBe(true); }); it('should validate individual variables', () => { const config = { ...DEFAULT_APP_CONFIG, identity: { appName: 'Test', description: '' }, variables: [{ key: 'my-invalid-key', type: 'string' as const, value: 'test' }] }; const result = validateAppConfig(config); expect(result.valid).toBe(false); }); it('should reject non-object configs', () => { const result = validateAppConfig(null); expect(result.valid).toBe(false); expect(result.errors[0]).toContain('must be an object'); }); });