UNPKG

@apistudio/apim-cli

Version:

CLI for API Management Products

274 lines (273 loc) 13.2 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const config_loader_impl_1 = require("../core/impl/config-loader.impl"); const promises_1 = __importDefault(require("fs/promises")); const path_1 = __importDefault(require("path")); // Mock fs/promises jest.mock('fs/promises', () => ({ access: jest.fn().mockResolvedValue(undefined), readFile: jest.fn() })); // Mock path jest.mock('path', () => ({ join: jest.fn((base, ...parts) => `${base}/${parts.join('/')}`), isAbsolute: jest.fn(p => p.startsWith('/')), })); describe('ConfigLoader', () => { let configLoader; const baseDir = '/test/base/dir'; beforeEach(() => { jest.clearAllMocks(); // Reset mock implementations to defaults path_1.default.join.mockImplementation((base, ...parts) => `${base}/${parts.join('/')}`); path_1.default.isAbsolute.mockImplementation(p => p.startsWith('/')); promises_1.default.access.mockResolvedValue(undefined); // Create a fresh instance for each test configLoader = new config_loader_impl_1.ConfigLoader(baseDir); }); describe('loadConfig', () => { it('should load JSON config file', async () => { // Setup const configPath = 'test-config.json'; const resolvedPath = `${baseDir}/${configPath}`; const configContent = JSON.stringify({ skipTransform: false, transformations: { mappings: [{ source: '$.spec.value', target: '$.Test.value' }], replacements: [] } }); promises_1.default.readFile.mockResolvedValue(configContent); path_1.default.join.mockReturnValue(resolvedPath); // Execute const result = await configLoader.loadConfig(configPath); // Verify expect(promises_1.default.access).toHaveBeenCalledWith(resolvedPath); expect(promises_1.default.readFile).toHaveBeenCalledWith(resolvedPath, 'utf-8'); expect(result).toEqual({ skipTransform: false, transformations: { mappings: [{ source: '$.spec.value', target: '$.Test.value' }], replacements: [] } }); }); it('should handle config with inheritance', async () => { // Setup const configPath = 'child-config.json'; const parentPath = 'parent-config.json'; const resolvedConfigPath = `${baseDir}/${configPath}`; const resolvedParentPath = `${baseDir}/${parentPath}`; const parentConfig = { skipTransform: false, transformations: { mappings: [{ source: '$.spec.parentValue', target: '$.Test.parentValue' }], replacements: [] } }; const childConfig = { extends: parentPath, transformations: { mappings: [{ source: '$.spec.childValue', target: '$.Test.childValue' }], replacements: [{ target: '$.Test.replacement', value: 'test', precedence: 10, operation: 'replace' }] } }; promises_1.default.readFile.mockImplementation((path) => { if (path === resolvedConfigPath) { return Promise.resolve(JSON.stringify(childConfig)); } else if (path === resolvedParentPath) { return Promise.resolve(JSON.stringify(parentConfig)); } return Promise.reject(new Error(`Unexpected path: ${path}`)); }); path_1.default.join.mockImplementation((base, file) => { if (file === configPath) return resolvedConfigPath; if (file === parentPath) return resolvedParentPath; return `${base}/${file}`; }); // Execute const result = await configLoader.loadConfig(configPath); // Verify expect(promises_1.default.access).toHaveBeenCalledWith(resolvedConfigPath); expect(promises_1.default.access).toHaveBeenCalledWith(resolvedParentPath); expect(promises_1.default.readFile).toHaveBeenCalledWith(resolvedConfigPath, 'utf-8'); expect(promises_1.default.readFile).toHaveBeenCalledWith(resolvedParentPath, 'utf-8'); // The actual implementation might not set skipTransform if it's not in the config expect(result).toHaveProperty('extends', parentPath); expect(result.transformations).toBeDefined(); if (result.transformations) { expect(result.transformations.mappings).toContainEqual({ source: '$.spec.parentValue', target: '$.Test.parentValue' }); expect(result.transformations.mappings).toContainEqual({ source: '$.spec.childValue', target: '$.Test.childValue' }); expect(result.transformations.replacements).toContainEqual({ target: '$.Test.replacement', value: 'test', precedence: 10, operation: 'replace' }); } }); it('should handle config with custom transformer', async () => { // Setup const configPath = 'custom-config.json'; const resolvedPath = `${baseDir}/${configPath}`; const configContent = JSON.stringify({ skipTransform: false, custom: 'custom-transformer', transformations: { mappings: [{ source: '$.spec.value', target: '$.Test.value' }], replacements: [] } }); promises_1.default.readFile.mockResolvedValue(configContent); path_1.default.join.mockReturnValue(resolvedPath); // Execute const result = await configLoader.loadConfig(configPath); // Verify expect(result).toEqual({ skipTransform: false, custom: 'custom-transformer' // transformations should be removed when custom is specified }); }); it('should handle config with inheritance and custom transformer', async () => { // Setup const configPath = 'child-config.json'; const parentPath = 'parent-config.json'; const resolvedConfigPath = `${baseDir}/${configPath}`; const resolvedParentPath = `${baseDir}/${parentPath}`; const parentConfig = { skipTransform: false, transformations: { mappings: [{ source: '$.spec.parentValue', target: '$.Test.parentValue' }], replacements: [] } }; const childConfig = { extends: parentPath, custom: 'custom-transformer', transformations: { mappings: [{ source: '$.spec.childValue', target: '$.Test.childValue' }], replacements: [] } }; promises_1.default.readFile.mockImplementation((path) => { if (path === resolvedConfigPath) { return Promise.resolve(JSON.stringify(childConfig)); } else if (path === resolvedParentPath) { return Promise.resolve(JSON.stringify(parentConfig)); } return Promise.reject(new Error(`Unexpected path: ${path}`)); }); path_1.default.join.mockImplementation((base, file) => { if (file === configPath) return resolvedConfigPath; if (file === parentPath) return resolvedParentPath; return `${base}/${file}`; }); // Execute const result = await configLoader.loadConfig(configPath); // Verify // The actual implementation might not set skipTransform if it's not in the config expect(result).toHaveProperty('extends', parentPath); expect(result).toHaveProperty('custom', 'custom-transformer'); // transformations should be removed when custom is specified expect(result.transformations).toBeUndefined(); }); it('should cache loaded configs', async () => { // Setup const configPath = 'test-config.json'; const resolvedPath = `${baseDir}/${configPath}`; const configContent = JSON.stringify({ skipTransform: false, transformations: { mappings: [{ source: '$.spec.value', target: '$.Test.value' }], replacements: [] } }); promises_1.default.readFile.mockResolvedValue(configContent); path_1.default.join.mockReturnValue(resolvedPath); // Execute await configLoader.loadConfig(configPath); await configLoader.loadConfig(configPath); // Verify expect(promises_1.default.readFile).toHaveBeenCalledTimes(1); }); it('should handle errors when loading config', async () => { // Setup const configPath = 'nonexistent-config.json'; const resolvedPath = `${baseDir}/${configPath}`; promises_1.default.access.mockRejectedValue(new Error('File not found')); path_1.default.join.mockReturnValue(resolvedPath); // Execute & Verify await expect(configLoader.loadConfig(configPath)).rejects.toThrow(); }); }); describe('resolveConfigPath', () => { it('should resolve paths starting with src/', async () => { // Setup const configPath = 'src/configs/test-config.json'; const expectedPath = `${baseDir}/configs/test-config.json`; path_1.default.join.mockReturnValueOnce(expectedPath); // Execute const resolvedPath = await configLoader.resolveConfigPath(configPath); // Verify expect(resolvedPath).toBe(expectedPath); expect(path_1.default.join).toHaveBeenCalledWith(baseDir, 'configs/test-config.json'); }); it('should resolve paths starting with /src/', async () => { // Setup const configPath = '/src/configs/test-config.json'; const expectedPath = `${baseDir}/configs/test-config.json`; path_1.default.join.mockReturnValue(expectedPath); // Execute const resolvedPath = await configLoader.resolveConfigPath(configPath); // Verify expect(resolvedPath).toBe(expectedPath); expect(path_1.default.join).toHaveBeenCalledWith(baseDir, 'configs/test-config.json'); }); it('should resolve relative paths', async () => { // Setup const configPath = 'configs/test-config.json'; const expectedPath = `${baseDir}/configs/test-config.json`; path_1.default.join.mockReturnValue(expectedPath); path_1.default.isAbsolute.mockReturnValue(false); // Execute const resolvedPath = await configLoader.resolveConfigPath(configPath); // Verify expect(resolvedPath).toBe(expectedPath); expect(path_1.default.join).toHaveBeenCalledWith(baseDir, configPath); }); it('should resolve absolute paths containing /src/', async () => { // Setup const configPath = '/absolute/path/src/configs/test-config.json'; const expectedPath = `${baseDir}/configs/test-config.json`; path_1.default.isAbsolute.mockReturnValue(true); path_1.default.join.mockReturnValue(expectedPath); // Execute const resolvedPath = await configLoader.resolveConfigPath(configPath); // Verify expect(resolvedPath).toBe(expectedPath); }); it('should handle other absolute paths', async () => { // Setup const configPath = '/absolute/path/configs/test-config.json'; path_1.default.isAbsolute.mockReturnValue(true); // Execute const resolvedPath = await configLoader.resolveConfigPath(configPath); // Verify expect(resolvedPath).toBe(configPath); }); it('should throw error if file does not exist', async () => { // Setup const configPath = 'nonexistent-config.json'; const expectedPath = `${baseDir}/nonexistent-config.json`; path_1.default.join.mockReturnValue(expectedPath); promises_1.default.access.mockRejectedValue(new Error('File not found')); // Execute & Verify await expect(configLoader.resolveConfigPath(configPath)).rejects.toThrow(); }); }); });