@apistudio/apim-cli
Version:
CLI for API Management Products
287 lines (246 loc) • 8.09 kB
text/typescript
/**
* Copyright IBM Corp. 2024, 2025
*/
/* eslint-disable no-unused-vars */
import {
createAssetReferenceMap,
convertNumberToString,
isValidAsset,
addErrorToResponse,
constructErrorResponse,
extractGatewayTypes,
createPathReferenceMap,
updatePathRefMap,
validateMinAssets,
updateRefs,
processRef,
checkFileExtension,
isRelativePath,
} from '../src/index.js';
import yaml from 'js-yaml';
import path from 'path';
import fs from 'fs';
jest.mock('@apic/studio-shared', () => ({
Logger: {
debug: jest.fn(),
info: jest.fn(),
warn: jest.fn(),
error: jest.fn(),
log: jest.fn(),
createChildLogger: jest.fn().mockReturnThis(),
},
LogComponent: () => (target: any) => target,
Component: {
Build: 'Build',
All: 'Studio',
},
ErrorResponse: jest.fn(),
Metadata_Ref: jest.fn(),
SpecObject: jest.fn(),
YamlContent: jest.fn(),
UpperCaseKinds: jest.fn(),
loadYaml: jest.fn((content) => require('js-yaml').load(content)),
SchemaHandler: jest.fn().mockImplementation(() => ({
getSchema: jest.fn().mockReturnValue(JSON.stringify({ type: 'object' })),
})),
}));
jest.mock('@apic/studio-client-model', () => ({
AssetModelKindConstants: {
API: 'API',
},
GatewayLabels: {
WMGW: 'webMethods',
LWGW: 'nano',
DPGW: 'datapower',
},
}));
// Mock JSZip for specific tests
const mockJSZip = {
loadAsync: jest.fn(),
file: jest.fn(),
files: {},
forEach: jest.fn(),
generateAsync: jest.fn(),
};
jest.mock('jszip', () => {
return jest.fn().mockImplementation(() => mockJSZip);
});
import {
createAssetReferenceMap,
convertNumberToString,
isValidAsset,
addErrorToResponse,
constructErrorResponse,
extractGatewayTypes,
createPathReferenceMap,
updatePathRefMap,
validateMinAssets,
updateRefs,
processRef,
checkFileExtension,
isRelativePath,
} from '../src/index.js';
import { YamlContent } from '@apic/studio-shared';
describe('validateYamlFiles', () => {
it('should create asset reference map successfully', async () => {
const zipFilePath = path.resolve(__dirname, './assets/gateway-asset.zip');
const Buffer = fs.readFileSync(zipFilePath);
const result = await createAssetReferenceMap(Buffer);
expect(result).not.toBe(undefined);
expect(result).not.toBe(false);
});
it('should log error for invalid YAML parsing', async () => {
const zipFilePath = path.resolve(__dirname, './assets/invalid-yaml-asset.zip');
const zipBuffer = fs.readFileSync(zipFilePath);
await createAssetReferenceMap(zipBuffer);
});
it('should convert number to string correctly', () => {
expect(convertNumberToString(1)).toBe('1.0');
expect(convertNumberToString(1.5)).toBe('1.5');
expect(convertNumberToString('1.5')).toBe('1.5');
});
it('should return true for valid YAML content', () => {
const filePath = path.resolve(__dirname, './assets/validate/api.yaml');
const buffer = fs.readFileSync(filePath);
const yamlContent = yaml.load(buffer.toString()) as YamlContent;
expect(isValidAsset(yamlContent)).toBe(true);
});
});
describe('checking error response', () => {
it('should construct an error response object', () => {
addErrorToResponse('ERR001', 'field1', 'Error description 1');
addErrorToResponse('ERR002', 'field2', 'Error description 2');
const response = constructErrorResponse();
expect(response).not.toEqual([]);
});
});
describe('extractGatewayTypes', () => {
beforeEach(() => {
jest.clearAllMocks();
});
it('should extract gateway types from gateways.json', async () => {
// Setup mock for JSZip
const mockFileContent = JSON.stringify({
gateways: [
{
gatewayURL: 'http://example.com',
gatewayUser: 'user',
gatewaySecret: 'secret',
gatewayTypes: ['webMethods', 'nano'],
},
],
});
mockJSZip.loadAsync.mockResolvedValue({
file: jest.fn().mockReturnValue({
async: jest.fn().mockResolvedValue(mockFileContent),
}),
});
const result = await extractGatewayTypes(Buffer.from('test'));
expect(result).toEqual(['webMethods', 'nano']);
expect(mockJSZip.loadAsync).toHaveBeenCalled();
});
it('should return empty array when gateways.json is not found', async () => {
mockJSZip.loadAsync.mockResolvedValue({
file: jest.fn().mockReturnValue(null),
});
const result = await extractGatewayTypes(Buffer.from('test'));
expect(result).toEqual([]);
});
it('should handle errors and return empty array', async () => {
mockJSZip.loadAsync.mockRejectedValue(new Error('Test error'));
const result = await extractGatewayTypes(Buffer.from('test'));
expect(result).toEqual([]);
});
});
describe('file utility functions', () => {
it('should check file extension correctly', () => {
expect(checkFileExtension('file.yml')).toBe(true);
expect(checkFileExtension('file.yaml')).toBe(true);
expect(checkFileExtension('file.txt')).toBe(false);
});
it('should detect relative paths correctly', () => {
expect(isRelativePath('./file.yml')).toBe(true);
expect(isRelativePath('../file.yml')).toBe(true);
expect(isRelativePath('file.yml')).toBe(false);
expect(isRelativePath('/absolute/path/file.yml')).toBe(false);
});
});
describe('path reference functions', () => {
beforeEach(() => {
jest.clearAllMocks();
});
it('should create path reference map', async () => {
// Setup mock for JSZip
mockJSZip.loadAsync.mockResolvedValue({
files: {
'file.yaml': {
dir: false,
async: jest.fn().mockResolvedValue('spec:\n $path: path/to/file.txt'),
},
},
});
const result = await createPathReferenceMap(Buffer.from('test'));
expect(result).toBeInstanceOf(Map);
});
it('should update path reference map', async () => {
const refMap = new Map<string, boolean>();
refMap.set('file.txt', false);
// Mock JSZip.loadAsync directly
const mockZipInstance = {
forEach: jest.fn().mockImplementation((callback: (path: string, obj: any) => void) => {
callback('resources/file.txt', { name: 'file.txt' });
}),
};
// Replace the original implementation for this test
const originalJSZip = require('jszip');
originalJSZip.loadAsync = jest.fn().mockResolvedValue(mockZipInstance);
await updatePathRefMap(Buffer.from('test'), refMap);
expect(refMap.get('file.txt')).toBe(true);
});
});
describe('validateMinAssets', () => {
beforeEach(() => {
jest.clearAllMocks();
});
it('should return true when yaml files exist', async () => {
mockJSZip.loadAsync.mockResolvedValue({
files: {
'file.yaml': { dir: false },
},
});
const result = await validateMinAssets(Buffer.from('test'));
expect(result).toBe(true);
});
it('should return false when no yaml files exist', async () => {
mockJSZip.loadAsync.mockResolvedValue({
files: {
'file.txt': { dir: false },
},
});
const result = await validateMinAssets(Buffer.from('test'));
expect(result).toBe(false);
});
it('should handle errors and return false', async () => {
mockJSZip.loadAsync.mockRejectedValue(new Error('Test error'));
const result = await validateMinAssets(Buffer.from('test'));
expect(result).toBe(false);
});
});
describe('reference processing functions', () => {
it('should process ref correctly', () => {
expect(processRef('namespace:name:1')).toBe('namespace:name:1.0');
expect(processRef('namespace:name:1.5')).toBe('namespace:name:1.5');
expect(processRef('namespace:name:string')).toBe('namespace:name:string');
});
it('should update refs in yaml content', () => {
const yamlContent = {
spec: {
$ref: 'namespace:name:1',
},
};
const versionMap = new Map<string, boolean>();
versionMap.set('namespace:name:1', false);
const result = updateRefs(yamlContent as any, versionMap);
expect(result.spec.$ref).toBe('namespace:name:1.0');
});
});