UNPKG

@sailboat-computer/data-storage

Version:

Shared data storage library for sailboat computer v3

369 lines (313 loc) 11.2 kB
/** * Tests for the StorageConfigManager */ import { StorageConfigManager, DEFAULT_STORAGE_CONFIG, DEFAULT_UNIFIED_STORAGE_MANAGER_OPTIONS } from '../StorageConfigManager'; import { EventBus } from '../../event-bus'; // Mock the config-client jest.mock('@sailboat-computer/config-client', () => { const mockConfigClient = { getConfig: jest.fn(), watchConfig: jest.fn().mockReturnValue(jest.fn()), saveConfig: jest.fn(), }; return { createConfigClient: jest.fn().mockReturnValue(mockConfigClient), }; }); // Mock the config-loader jest.mock('@sailboat-computer/config-loader', () => { return { loadConfig: jest.fn(), saveConfig: jest.fn(), }; }); // Mock fs/promises jest.mock('fs/promises', () => { return { mkdir: jest.fn().mockResolvedValue(undefined), }; }); // Import mocks after mocking const createConfigClient = jest.requireMock('@sailboat-computer/config-client').createConfigClient; const loadConfig = jest.requireMock('@sailboat-computer/config-loader').loadConfig; const saveConfig = jest.requireMock('@sailboat-computer/config-loader').saveConfig; const mkdir = jest.requireMock('fs/promises').mkdir; describe('StorageConfigManager', () => { let configManager: StorageConfigManager; let mockConfigClient: any; beforeEach(() => { jest.clearAllMocks(); // Create config manager configManager = new StorageConfigManager(); // Get mock config client mockConfigClient = createConfigClient.mock.results[0].value; }); it('should use default options when none are provided', () => { // Verify config client was created with default options expect(createConfigClient).toHaveBeenCalledWith(expect.objectContaining({ serviceUrl: 'http://localhost:3000', serviceName: 'data-manager', fallbackDir: './config', environment: 'production', })); }); it('should use provided options', () => { // Create config manager with custom options configManager = new StorageConfigManager({ serviceUrl: 'http://test:3000', localDir: './test-config', environment: 'test', serviceName: 'test-service', configId: 'test-config', }); // Verify config client was created with custom options expect(createConfigClient).toHaveBeenCalledWith(expect.objectContaining({ serviceUrl: 'http://test:3000', serviceName: 'test-service', fallbackDir: './test-config', environment: 'test', })); }); describe('initialize', () => { it('should load configuration from service and cache locally', async () => { // Set up mock config client const mockConfig = { providers: { hot: { type: 'redis', config: { host: 'test-host', port: 6379, }, }, }, localStorage: { directory: './test-data', }, }; mockConfigClient.getConfig.mockResolvedValue(mockConfig); // Initialize config manager await configManager.initialize(); // Verify config client was called expect(mockConfigClient.getConfig).toHaveBeenCalledWith('config'); // Verify watchConfig was called expect(mockConfigClient.watchConfig).toHaveBeenCalledWith( 'config', expect.any(Function) ); // Verify config was cached locally expect(mkdir).toHaveBeenCalledWith(expect.stringContaining('data-manager'), { recursive: true }); expect(saveConfig).toHaveBeenCalledWith( 'data-manager/config.json', mockConfig, 'Cached from configuration service', undefined, expect.objectContaining({ baseDir: './config', }) ); // Verify storage config was extracted correctly expect(configManager.getStorageConfig()).toEqual({ providers: mockConfig.providers, }); // Verify unified storage manager options were extracted correctly expect(configManager.getUnifiedStorageManagerOptions().localStorage).toEqual(mockConfig.localStorage); }); it('should fall back to local file when service is unavailable', async () => { // Set up mock config client to throw error mockConfigClient.getConfig.mockRejectedValue(new Error('Service unavailable')); // Set up mock config loader const mockConfig = { providers: { hot: { type: 'redis', config: { host: 'test-host', port: 6379, }, }, }, localStorage: { directory: './test-data', }, }; loadConfig.mockResolvedValue(mockConfig); // Initialize config manager await configManager.initialize(); // Verify config client was called expect(mockConfigClient.getConfig).toHaveBeenCalledWith('config'); // Verify loadConfig was called expect(loadConfig).toHaveBeenCalledWith( 'data-manager/config.json', expect.anything(), expect.objectContaining({ baseDir: './config', }) ); // Verify watchConfig was called expect(mockConfigClient.watchConfig).toHaveBeenCalledWith( 'config', expect.any(Function) ); // Verify storage config was extracted correctly expect(configManager.getStorageConfig()).toEqual({ providers: mockConfig.providers, }); // Verify unified storage manager options were extracted correctly expect(configManager.getUnifiedStorageManagerOptions().localStorage).toEqual(mockConfig.localStorage); }); it('should use default config when both service and local file are unavailable', async () => { // Set up mock config client to throw error mockConfigClient.getConfig.mockRejectedValue(new Error('Service unavailable')); // Set up mock config loader to throw error loadConfig.mockRejectedValue(new Error('File not found')); // Initialize config manager await configManager.initialize(); // Verify config client was called expect(mockConfigClient.getConfig).toHaveBeenCalledWith('config'); // Verify loadConfig was called expect(loadConfig).toHaveBeenCalledWith( 'data-manager/config.json', expect.anything(), expect.objectContaining({ baseDir: './config', }) ); // Verify default config was cached locally expect(saveConfig).toHaveBeenCalledWith( 'data-manager/config.json', expect.anything(), 'Cached from configuration service', undefined, expect.objectContaining({ baseDir: './config', }) ); // Verify storage config is default expect(configManager.getStorageConfig()).toEqual(DEFAULT_STORAGE_CONFIG); // Verify unified storage manager options are default expect(configManager.getUnifiedStorageManagerOptions()).toEqual(DEFAULT_UNIFIED_STORAGE_MANAGER_OPTIONS); }); }); describe('saveConfig', () => { it('should save configuration to service and locally', async () => { // Set up mock config client mockConfigClient.saveConfig.mockResolvedValue(undefined); // Create test config const testConfig = { providers: { hot: { type: 'redis', config: { host: 'test-host', port: 6379, }, }, }, }; // Save config await configManager.saveConfig(testConfig, 'Test description'); // Verify saveConfig was called on config client expect(mockConfigClient.saveConfig).toHaveBeenCalledWith( 'config', expect.objectContaining({ providers: testConfig.providers, }), 'Test description' ); // Verify saveConfig was called on config loader expect(saveConfig).toHaveBeenCalledWith( 'data-manager/config.json', expect.objectContaining({ providers: testConfig.providers, }), 'Test description', undefined, expect.objectContaining({ baseDir: './config', }) ); // Verify storage config was updated expect(configManager.getStorageConfig()).toEqual(testConfig); }); it('should fall back to local file when service is unavailable', async () => { // Set up mock config client to throw error mockConfigClient.saveConfig.mockRejectedValue(new Error('Service unavailable')); // Create test config const testConfig = { providers: { hot: { type: 'redis', config: { host: 'test-host', port: 6379, }, }, }, }; // Save config await configManager.saveConfig(testConfig, 'Test description'); // Verify saveConfig was called on config client expect(mockConfigClient.saveConfig).toHaveBeenCalledWith( 'config', expect.objectContaining({ providers: testConfig.providers, }), 'Test description' ); // Verify saveConfig was called on config loader expect(saveConfig).toHaveBeenCalledWith( 'data-manager/config.json', expect.objectContaining({ providers: testConfig.providers, }), 'Test description', undefined, expect.objectContaining({ baseDir: './config', }) ); // Verify storage config was updated expect(configManager.getStorageConfig()).toEqual(testConfig); }); }); describe('event bus integration', () => { it('should subscribe to configuration change events', () => { // Create mock event bus const mockEventBus: EventBus = { publish: jest.fn(), subscribe: jest.fn(), unsubscribe: jest.fn(), }; // Set event bus configManager.setEventBus(mockEventBus); // Verify subscribe was called expect(mockEventBus.subscribe).toHaveBeenCalledWith( 'system.config_changed.v1', expect.any(Function) ); }); it('should publish configuration change events', async () => { // Create mock event bus const mockEventBus: EventBus = { publish: jest.fn().mockResolvedValue(undefined), subscribe: jest.fn(), unsubscribe: jest.fn(), }; // Set event bus configManager.setEventBus(mockEventBus); // Publish config changed await configManager.publishConfigChanged(); // Verify publish was called expect(mockEventBus.publish).toHaveBeenCalledWith( 'system.config_changed.v1', expect.objectContaining({ service: 'data-manager', timestamp: expect.any(Date), config: expect.anything(), }), expect.anything() ); }); }); });