@apistudio/apim-cli
Version:
CLI for API Management Products
270 lines (251 loc) • 10.9 kB
text/typescript
/**
* Copyright IBM Corp. 2024, 2025
*/
import path from 'path';
import fs from 'fs';
import JSZip from 'jszip';
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(),
}));
import { BuildProjectAssets } from '../../src/build-project-assets.js';
import { ProjectAssetValidator } from '../../src/validator/asset-validator.js';
import { normalizeZipPaths, resolveRelativePaths } from '../../src/index.js';
jest.mock('@apic/studio-client-model', () => ({
AssetModelKindConstants: {
API: 'API',
},
}));
describe('Build Asset Project Modules', () => {
it('should validate that given entry and return true if it is yaml ', async () => {
const zipFilePath = path.resolve(__dirname, '../assets/gateway-multi-project-asset.zip');
const Buffer = fs.readFileSync(zipFilePath);
const obj = new BuildProjectAssets();
const zip2 = await JSZip.loadAsync(Buffer);
const normalizedBuffer = await normalizeZipPaths(zip2);
const buffer2 = await normalizedBuffer.generateAsync({
type: 'nodebuffer',
});
const result = await obj['loadZipFromBuffer'](buffer2);
const entry = result.files[path.normalize('demo/api.yml')];
const obj2 = new ProjectAssetValidator();
const bool = obj2['isYamlFileForFolder'](entry, 'demo');
expect(bool).toBe(true);
});
it('should load zip from buffer ', async () => {
const zipFilePath = path.resolve(__dirname, '../assets/gateway-multi-project-asset.zip');
const Buffer = fs.readFileSync(zipFilePath);
const obj = new ProjectAssetValidator();
const result = await obj['loadZipFromBuffer'](Buffer);
expect(result).not.toBe(undefined);
expect(result).not.toBe(null);
expect(result).not.toBe(false);
});
it('should create asset reference map for the project', async () => {
const zipFilePath = path.resolve(__dirname, '../assets/gateway-multi-project-asset.zip');
const Buffer = fs.readFileSync(zipFilePath);
const obj = new ProjectAssetValidator();
const allFolderNames = new Set<string>();
allFolderNames.add('project1');
allFolderNames.add('project2');
const result = await obj['createProjectAssetReferenceMap'](Buffer, 'project1', allFolderNames);
for (const value of result.values()) {
expect(value).toBe(true);
}
});
it('should execute catch if createProjectAssetReferenceMap throws an error', async () => {
const spy = jest
.spyOn(BuildProjectAssets.prototype, 'createVersionProcessingMap')
.mockImplementation(() => {
throw new Error('Error processing zip');
});
const buffer = fs.readFileSync(
path.resolve(__dirname, '../assets/gateway-multi-project-asset.zip')
);
const obj = new ProjectAssetValidator();
const allFolderNames = new Set(['project1', 'project2']);
const result = await obj['createProjectAssetReferenceMap'](buffer, 'project1', allFolderNames);
expect(result).toBeInstanceOf(Map);
spy.mockRestore();
});
it('should process other folders if unresolved refs exist', async () => {
const buffer = fs.readFileSync(
path.resolve(__dirname, '../assets/gateway-multi-project-asset.zip')
);
const obj = new ProjectAssetValidator();
const allFolderNames = new Set(['project1', 'project2']);
const processedFolders: string[] = [];
const spy = jest
.spyOn(ProjectAssetValidator.prototype as any, 'processYamlFiles')
.mockImplementation(async function (
zipContent: unknown,
folder: unknown,
refMap: unknown
// versionMap: unknown,
) {
const typedRefMap = refMap as Map<string, boolean>;
processedFolders.push(folder as string);
if (folder === 'project1') {
typedRefMap.set('ref1', false);
}
});
const result = await obj['createProjectAssetReferenceMap'](buffer, 'project1', allFolderNames);
expect(result).toBeInstanceOf(Map);
expect(result.get('ref1')).toBe(false);
expect(processedFolders).toContain('project2');
spy.mockRestore();
});
it('should create asset path reference map for the project', async () => {
const zipFilePath = path.resolve(__dirname, '../assets/gateway-multi-project-asset.zip');
const Buffer = fs.readFileSync(zipFilePath);
const obj = new ProjectAssetValidator();
const result = await obj['createProjectPathReferenceMap'](Buffer, 'project1');
for (const value of result.values()) {
expect(value).toBe(false);
}
});
it('should execute catch if createProjectPathReferenceMap throws an error', async () => {
const spy = jest
.spyOn(BuildProjectAssets.prototype, 'createVersionProcessingMap')
.mockImplementation(() => {
throw new Error('Error processing zip');
});
const buffer = fs.readFileSync(
path.resolve(__dirname, '../assets/gateway-multi-project-asset.zip')
);
const obj = new ProjectAssetValidator();
const result = await obj['createProjectPathReferenceMap'](buffer, 'project1');
expect(result).toBeInstanceOf(Map);
spy.mockRestore();
});
it('should create asset path reference map for the project and update the map with filepath ', async () => {
const zipFilePath = path.resolve(__dirname, '../assets/gateway-multi-project-asset.zip');
const Buffer = fs.readFileSync(zipFilePath);
const obj = new ProjectAssetValidator();
const zip2 = await JSZip.loadAsync(Buffer);
const normalizedBuffer = await normalizeZipPaths(zip2);
const zip3 = await resolveRelativePaths(
await normalizedBuffer.generateAsync({ type: 'nodebuffer' })
);
const buffer2 = await zip3.generateAsync({ type: 'nodebuffer' });
const obj2 = new BuildProjectAssets();
const folderNames = new Set<string>();
const filePathsInFolder = new Set<string>();
await obj2['extractFolderNamesAndPaths'](buffer2, folderNames, filePathsInFolder);
const result = await obj['validateProjectPathReference'](
buffer2,
'project1',
filePathsInFolder
);
expect(result).toBe(true);
});
it('should throw an error if ValidateProjectPathReference throws an error', async () => {
const obj = new ProjectAssetValidator();
const spy = jest
.spyOn(ProjectAssetValidator.prototype as any, 'createProjectPathReferenceMap')
.mockImplementation(() => {
throw new Error('Error validating asset');
});
// const refMap = new Map<string, boolean>();
const buffer = fs.readFileSync(
path.resolve(__dirname, '../assets/gateway-multi-project-asset.zip')
);
const allFolderNames = new Set(['project1', 'project2']);
const result = await obj['validateProjectPathReference'](buffer, 'project1', allFolderNames);
expect(result).toBe(false);
spy.mockRestore();
});
it('should throw an error if validateProjectPathReference encounters invalid references', async () => {
const obj = new ProjectAssetValidator();
// const spy = jest.spyOn(ProjectAssetValidator.prototype as any, 'createProjectPathReferenceMap').mockResolvedValue(new Map([
// ['ref1', false],
// ['ref2', true],
// ]))
const Buffer = fs.readFileSync(
path.resolve(__dirname, '../assets/gateway-multi-project-asset.zip')
);
const filePathsInFolder = new Set<string>();
const result = await obj['validateProjectPathReference'](Buffer, 'demo', filePathsInFolder);
expect(result).toBe(false);
});
it('should throw an error if ValidateProjectAssetReference encounters invalid references', async () => {
const obj = new ProjectAssetValidator();
const spy = jest
.spyOn(ProjectAssetValidator.prototype as any, 'createProjectAssetReferenceMap')
.mockImplementation(() => {
throw new Error('Error validating asset');
});
// const refMap = new Map<string, boolean>();
const buffer = fs.readFileSync(
path.resolve(__dirname, '../assets/gateway-multi-project-asset.zip')
);
const allFolderNames = new Set(['project1', 'project2']);
const result = await obj['validateProjectAssetReference'](buffer, 'project1', allFolderNames);
expect(result.isValid).toBe(false);
expect(result.refMap).toBeInstanceOf(Map);
spy.mockRestore();
});
it('should create asset reference map for the project and update the map with filepath ', async () => {
const zipFilePath = path.resolve(__dirname, '../assets/gateway-multi-project-asset.zip');
const Buffer = fs.readFileSync(zipFilePath);
const obj = new ProjectAssetValidator();
const zip2 = await JSZip.loadAsync(Buffer);
const normalizedBuffer = await normalizeZipPaths(zip2);
const zip3 = await resolveRelativePaths(
await normalizedBuffer.generateAsync({ type: 'nodebuffer' })
);
const buffer2 = await zip3.generateAsync({ type: 'nodebuffer' });
const obj2 = new BuildProjectAssets();
const folderNames = new Set<string>();
const filePathsInFolder = new Set<string>();
await obj2['extractFolderNamesAndPaths'](buffer2, folderNames, filePathsInFolder);
const result = await obj['validateProjectAssetReference'](
buffer2,
'project1',
filePathsInFolder
);
expect(result.isValid).toBe(true);
expect(result.refMap).not.toBe(null);
expect(result.refMap).not.toBe(undefined);
});
it('should validate the minimum assets required for deployment ', async () => {
const zipFilePath = path.resolve(__dirname, '../assets/gateway-multi-project-asset.zip');
const Buffer = fs.readFileSync(zipFilePath);
const obj = new ProjectAssetValidator();
const result = await obj['validateProjectHasMinimumAssets'](Buffer);
expect(result).toBe(true);
});
it('should return false the minimum assets required for deployment is not found ', async () => {
const zipFilePath = path.resolve(
__dirname,
'../assets/gateway-project-with-no-valid-assets.zip'
);
const Buffer = fs.readFileSync(zipFilePath);
const obj = new ProjectAssetValidator();
const result = await obj['validateProjectHasMinimumAssets'](Buffer);
expect(result).toBe(false);
});
it('should validate the api spec for kind api files ', async () => {
const zipFilePath = path.resolve(__dirname, '../assets/gateway-multi-project-asset.zip');
const Buffer = fs.readFileSync(zipFilePath);
const obj = new ProjectAssetValidator();
const result = await obj['validateProjectApiSpecVariable'](Buffer, 'project1');
expect(result).toBe(true);
});
});