@apistudio/apim-cli
Version:
CLI for API Management Products
274 lines (273 loc) • 13.2 kB
JavaScript
;
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();
});
});
});