Back to home page

EIC code displayed by LXR

 
 

    


Warning, /firebird/firebird-ng/src/app/services/config.service.spec.ts is written in an unsupported language. File is not indexed.

0001 import { TestBed } from '@angular/core/testing';
0002 import { ConfigService, ConfigSnapshot } from './config.service';
0003 import { ConfigProperty } from '../utils/config-property';
0004 
0005 // Mock storage for testing
0006 class MockStorage {
0007     private storage: Map<string, string> = new Map();
0008 
0009     getItem(key: string): string | null {
0010         return this.storage.get(key) || null;
0011     }
0012 
0013     setItem(key: string, value: string): void {
0014         this.storage.set(key, value);
0015     }
0016 
0017     clear(): void {
0018         this.storage.clear();
0019     }
0020 }
0021 
0022 describe('ConfigService', () => {
0023     let service: ConfigService;
0024     let mockStorage: MockStorage;
0025 
0026     beforeEach(() => {
0027         TestBed.configureTestingModule({});
0028         service = TestBed.inject(ConfigService);
0029         mockStorage = new MockStorage();
0030     });
0031 
0032     it('should be created', () => {
0033         expect(service).toBeTruthy();
0034     });
0035 
0036     describe('Config Management', () => {
0037         it('should add and retrieve config properties', () => {
0038             const config = new ConfigProperty('testKey', 'defaultValue', undefined, undefined, mockStorage);
0039 
0040             service.addConfig(config);
0041 
0042             const retrieved = service.getConfig<string>('testKey');
0043             expect(retrieved).toBe(config);
0044             expect(retrieved?.value).toBe('defaultValue');
0045         });
0046 
0047         it('should return undefined for non-existent config', () => {
0048             const retrieved = service.getConfig<string>('nonExistent');
0049             expect(retrieved).toBeUndefined();
0050         });
0051 
0052         it('should throw error when getting non-existent config with getConfigOrThrow', () => {
0053             expect(() => service.getConfigOrThrow<string>('nonExistent'))
0054                 .toThrowError("Property 'nonExistent' not found");
0055         });
0056 
0057         it('should return config with getConfigOrThrow when it exists', () => {
0058             const config = new ConfigProperty('existingKey', 'value', undefined, undefined, mockStorage);
0059             service.addConfig(config);
0060 
0061             const retrieved = service.getConfigOrThrow<string>('existingKey');
0062             expect(retrieved).toBe(config);
0063         });
0064     });
0065 
0066     describe('loadDefaults', () => {
0067         it('should reset all configs to their default values', () => {
0068             const config1 = new ConfigProperty('key1', 'default1', undefined, undefined, mockStorage);
0069             const config2 = new ConfigProperty('key2', 100, undefined, undefined, mockStorage);
0070 
0071             service.addConfig(config1);
0072             service.addConfig(config2);
0073 
0074             // Change values
0075             config1.setValue('changed1');
0076             config2.setValue(200);
0077 
0078             expect(config1.value).toBe('changed1');
0079             expect(config2.value).toBe(200);
0080 
0081             // Load defaults
0082             service.loadDefaults();
0083 
0084             expect(config1.value).toBe('default1');
0085             expect(config2.value).toBe(100);
0086         });
0087 
0088         it('should handle empty config map', () => {
0089             expect(() => service.loadDefaults()).not.toThrow();
0090         });
0091     });
0092 
0093     describe('loadDefaultsFor', () => {
0094         it('should reset only configs with matching prefix', () => {
0095             const uiConfig1 = new ConfigProperty('ui.theme', 'light', undefined, undefined, mockStorage);
0096             const uiConfig2 = new ConfigProperty('ui.fontSize', 14, undefined, undefined, mockStorage);
0097             const apiConfig = new ConfigProperty('api.endpoint', 'http://localhost', undefined, undefined, mockStorage);
0098 
0099             service.addConfig(uiConfig1);
0100             service.addConfig(uiConfig2);
0101             service.addConfig(apiConfig);
0102 
0103             // Change values
0104             uiConfig1.setValue('dark');
0105             uiConfig2.setValue(16);
0106             apiConfig.setValue('http://production');
0107 
0108             // Load defaults only for ui configs
0109             service.loadDefaultsFor('ui');
0110 
0111             expect(uiConfig1.value).toBe('light');
0112             expect(uiConfig2.value).toBe(14);
0113             expect(apiConfig.value).toBe('http://production'); // Should not be reset
0114         });
0115 
0116         it('should handle prefix with no matching configs', () => {
0117             const config = new ConfigProperty('api.endpoint', 'http://localhost', undefined, undefined, mockStorage);
0118             service.addConfig(config);
0119 
0120             config.setValue('http://production');
0121 
0122             service.loadDefaultsFor('ui');
0123 
0124             expect(config.value).toBe('http://production'); // Should remain unchanged
0125         });
0126 
0127         it('should handle empty prefix', () => {
0128             const config = new ConfigProperty('key', 'default', undefined, undefined, mockStorage);
0129             service.addConfig(config);
0130 
0131             config.setValue('changed');
0132 
0133             // Empty prefix should match all
0134             service.loadDefaultsFor('');
0135 
0136             expect(config.value).toBe('default');
0137         });
0138     });
0139 
0140     describe('saveToJson', () => {
0141         it('should export all configs to JSON snapshot', () => {
0142             const config1 = new ConfigProperty('key1', 'value1', undefined, undefined, mockStorage);
0143             const config2 = new ConfigProperty('key2', 42, undefined, undefined, mockStorage);
0144             const config3 = new ConfigProperty('key3', { nested: 'object' }, undefined, undefined, mockStorage);
0145 
0146             service.addConfig(config1);
0147             service.addConfig(config2);
0148             service.addConfig(config3);
0149 
0150             const snapshot = service.saveToJson();
0151 
0152             expect(snapshot.configs).toBeDefined();
0153             expect(snapshot.configs['key1'].value).toBe('value1');
0154             expect(snapshot.configs['key2'].value).toBe(42);
0155             expect(snapshot.configs['key3'].value).toEqual({ nested: 'object' });
0156             expect(snapshot.version).toBe('1.0');
0157             expect(snapshot.exportedAt).toBeDefined();
0158         });
0159 
0160         it('should include metadata in snapshot', () => {
0161             const snapshot = service.saveToJson();
0162 
0163             expect(snapshot.version).toBe('1.0');
0164             expect(snapshot.exportedAt).toBeDefined();
0165 
0166             // Verify exportedAt is a valid ISO date string
0167             const date = new Date(snapshot.exportedAt!);
0168             expect(date.toISOString()).toBe(snapshot.exportedAt!);
0169         });
0170 
0171         it('should handle empty config map', () => {
0172             const snapshot = service.saveToJson();
0173 
0174             expect(snapshot.configs).toEqual({});
0175             expect(snapshot.version).toBe('1.0');
0176             expect(snapshot.exportedAt).toBeDefined();
0177         });
0178     });
0179 
0180     describe('loadFromJson', () => {
0181         it('should import configs from JSON snapshot', () => {
0182             const config1 = new ConfigProperty('key1', 'default1', undefined, undefined, mockStorage);
0183             const config2 = new ConfigProperty('key2', 0, undefined, undefined, mockStorage);
0184 
0185             service.addConfig(config1);
0186             service.addConfig(config2);
0187 
0188             const snapshot: ConfigSnapshot = {
0189                 configs: {
0190                     key1: { value: 'imported1' },
0191                     key2: { value: 99 }
0192                 },
0193                 version: '1.0',
0194                 exportedAt: new Date().toISOString()
0195             };
0196 
0197             service.loadFromJson(snapshot);
0198 
0199             expect(config1.value).toBe('imported1');
0200             expect(config2.value).toBe(99);
0201         });
0202 
0203         it('should warn about configs not found in service', () => {
0204             const consoleWarnSpy = vi.spyOn(console, 'warn');
0205             const config = new ConfigProperty('existing', 'value', undefined, undefined, mockStorage);
0206             service.addConfig(config);
0207 
0208             const snapshot: ConfigSnapshot = {
0209                 configs: {
0210                     existing: { value: 'updated' },
0211                     nonExisting: { value: 'ignored' }
0212                 }
0213             };
0214 
0215             service.loadFromJson(snapshot);
0216 
0217             expect(config.value).toBe('updated');
0218             expect(consoleWarnSpy).toHaveBeenCalledWith("Config key 'nonExisting' not found in registered configs, skipping...");
0219         });
0220 
0221         it('should respect timestamp-based conflict resolution when overwriteNewer is false', () => {
0222             const config = new ConfigProperty('key', 'default', undefined, undefined, mockStorage);
0223             service.addConfig(config);
0224 
0225             // Set a value with a recent timestamp
0226             config.setValue('current', Date.now());
0227 
0228             // Try to load an older value
0229             const snapshot: ConfigSnapshot = {
0230                 configs: {
0231                     key: {
0232                         value: 'older',
0233                         timestamp: Date.now() - 10000 // 10 seconds ago
0234                     }
0235                 }
0236             };
0237 
0238             service.loadFromJson(snapshot, false);
0239 
0240             // Value should remain 'current' because its timestamp is newer
0241             expect(config.value).toBe('current');
0242         });
0243 
0244         it('should force overwrite when overwriteNewer is true', () => {
0245             const config = new ConfigProperty('key', 'default', undefined, undefined, mockStorage);
0246             service.addConfig(config);
0247 
0248             // Set a value with an explicit far-future timestamp to ensure it's "newer"
0249             const futureTime = Date.now() + 100000; // 100 seconds in the future
0250             config.setValue('current', futureTime);
0251 
0252             // Verify the value was set
0253             expect(config.value).toBe('current');
0254 
0255             // Load value regardless of timestamp - the snapshot has an old timestamp
0256             const snapshot: ConfigSnapshot = {
0257                 configs: {
0258                     key: {
0259                         value: 'forced',
0260                         timestamp: Date.now() - 10000 // 10 seconds ago
0261                     }
0262                 }
0263             };
0264 
0265             // With overwriteNewer=true, should overwrite even though stored time is newer
0266             service.loadFromJson(snapshot, true);
0267 
0268             // Value should be 'forced' despite the stored value having a newer timestamp
0269             expect(config.value).toBe('forced');
0270         });
0271 
0272         it('should throw error for invalid snapshot', () => {
0273             expect(() => service.loadFromJson(null as any))
0274                 .toThrowError('Invalid config snapshot: missing configs object');
0275 
0276             expect(() => service.loadFromJson({} as any))
0277                 .toThrowError('Invalid config snapshot: missing configs object');
0278         });
0279 
0280         it('should handle snapshot without timestamps', () => {
0281             const config = new ConfigProperty('key', 'default', undefined, undefined, mockStorage);
0282             service.addConfig(config);
0283 
0284             const snapshot: ConfigSnapshot = {
0285                 configs: {
0286                     key: { value: 'noTimestamp' }
0287                 }
0288             };
0289 
0290             service.loadFromJson(snapshot);
0291 
0292             expect(config.value).toBe('noTimestamp');
0293         });
0294     });
0295 
0296     describe('Integration tests', () => {
0297         it('should roundtrip configs through JSON', () => {
0298             // Setup initial configs
0299             const stringConfig = new ConfigProperty('str', 'hello', undefined, undefined, mockStorage);
0300             const numberConfig = new ConfigProperty('num', 42, undefined, undefined, mockStorage);
0301             const boolConfig = new ConfigProperty('bool', true, undefined, undefined, mockStorage);
0302             const objectConfig = new ConfigProperty('obj', { a: 1, b: 'test' }, undefined, undefined, mockStorage);
0303 
0304             service.addConfig(stringConfig);
0305             service.addConfig(numberConfig);
0306             service.addConfig(boolConfig);
0307             service.addConfig(objectConfig);
0308 
0309             // Export to JSON
0310             const exported = service.saveToJson();
0311 
0312             // Change all values
0313             stringConfig.setValue('changed');
0314             numberConfig.setValue(100);
0315             boolConfig.setValue(false);
0316             objectConfig.setValue({ c: 3 } as any);
0317 
0318             // Import back from JSON (use overwriteNewer=true to force restore)
0319             service.loadFromJson(exported, true);
0320 
0321             // Verify original values restored
0322             expect(stringConfig.value).toBe('hello');
0323             expect(numberConfig.value).toBe(42);
0324             expect(boolConfig.value).toBe(true);
0325             expect(objectConfig.value).toEqual({ a: 1, b: 'test' });
0326         });
0327 
0328         it('should handle complex workflow with prefix-based defaults', () => {
0329             // Setup configs with different prefixes
0330             const uiTheme = new ConfigProperty('ui.theme', 'light', undefined, undefined, mockStorage);
0331             const uiFont = new ConfigProperty('ui.font', 'Arial', undefined, undefined, mockStorage);
0332             const apiUrl = new ConfigProperty('api.url', 'http://localhost', undefined, undefined, mockStorage);
0333             const apiTimeout = new ConfigProperty('api.timeout', 5000, undefined, undefined, mockStorage);
0334 
0335             console.log("Initial timestamp");
0336             console.log(uiTheme.key);
0337             console.log(uiTheme.value);
0338             console.log(uiTheme.getTimestamp());
0339 
0340             service.addConfig(uiTheme);
0341             service.addConfig(uiFont);
0342             service.addConfig(apiUrl);
0343             service.addConfig(apiTimeout);
0344 
0345             // Modify values
0346             uiTheme.setValue('dark');
0347             uiFont.setValue('Roboto');
0348             apiUrl.setValue('http://production');
0349             apiTimeout.setValue(10000);
0350 
0351             console.log("timestamp after update (before save)");
0352             console.log(uiTheme.key);
0353             console.log(uiTheme.value);
0354             console.log(uiTheme.getTimestamp());
0355 
0356             // Export current state
0357             const snapshot = service.saveToJson();
0358 
0359             // Reset UI configs only
0360             service.loadDefaultsFor('ui');
0361             expect(uiTheme.value).toBe('light');
0362             expect(uiFont.value).toBe('Arial');
0363             expect(apiUrl.value).toBe('http://production');
0364             expect(apiTimeout.value).toBe(10000);
0365 
0366             console.log("timestamp after .loadDefaultsFor('ui');");
0367             console.log(uiTheme.key);
0368             console.log(uiTheme.value);
0369             console.log(uiTheme.getTimestamp());
0370 
0371             // Restore from snapshot (use overwriteNewer=true to force restore)
0372             service.loadFromJson(snapshot, true);
0373             expect(uiTheme.value).toBe('dark');
0374             expect(uiFont.value).toBe('Roboto');
0375             expect(apiUrl.value).toBe('http://production');
0376             expect(apiTimeout.value).toBe(10000);
0377 
0378 
0379             console.log("timestamp after loadFromJson");
0380             console.log(uiTheme.value);
0381             console.log(uiTheme.getTimestamp());
0382 
0383             // Reset all to defaults
0384             service.loadDefaults();
0385             expect(uiTheme.value).toBe('light');
0386             expect(uiFont.value).toBe('Arial');
0387             expect(apiUrl.value).toBe('http://localhost');
0388             expect(apiTimeout.value).toBe(5000);
0389         });
0390     });
0391 });