UNPKG

@apistudio/apim-cli

Version:

CLI for API Management Products

270 lines (251 loc) 10.9 kB
/** * 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); }); });