@apistudio/apim-cli
Version:
CLI for API Management Products
242 lines (210 loc) • 9.05 kB
text/typescript
/**
* Copyright Super iPaaS Integration LLC, an IBM Company 2024
*/
import { prepareGatewayJson, prepareArchiveBuffer, executeDeployment } from './projects-deployer.js';
import { processDeployment } from '@apic/studio-deploy';
import { showError, showInfo, showSuccess } from '../../helpers/common/message-helper.js';
import { readFileAsBuffer } from '../../helpers/common/fs-helper.js';
import {
APIENDPOINTS,
DEPLOY_STARTED,
DEPLOYMENT_FAILURE,
LINE,
} from '../../constants/message-constants.js';
let mockExit: jest.SpyInstance;
jest.mock('@apic/studio-deploy', () => ({
processDeployment: jest.fn() as jest.MockedFunction<typeof processDeployment>,
}));
jest.mock('../../helpers/common/message-helper.js', () => ({
showError: jest.fn(),
showInfo: jest.fn(),
showSuccess: jest.fn()
}));
jest.mock('@apic/studio-build', () => ({
processProjectBuild: jest.fn(),
}));
beforeEach(() => {
// @ts-ignore
mockExit = jest.spyOn(process, 'exit').mockImplementation(() => {
});
});
jest.mock('env-paths', () => {
return jest.fn((name: string) => ({
data: `/mock/path/${name}-data`,
config: `/mock/path/${name}-config`,
cache: `/mock/path/${name}-cache`,
log: `/mock/path/${name}-log`,
temp: `/mock/path/${name}-temp`,
}));
});
jest.mock('../../helpers/common/fs-helper.js', () => ({
readFileAsBuffer: jest.fn() as jest.MockedFunction<typeof readFileAsBuffer>,
}));
jest.mock('cli-table3', () => {
return jest.fn().mockImplementation(() => ({
push: jest.fn(),
toString: jest.fn().mockReturnValue(
'┌──────────┬─────────────────────┐\n' +
'│ APIs │ Gateway Endpoints │\n' +
'├──────────┼─────────────────────┤\n' +
'│ Test API │ http://example.com │\n' +
'│ │ http://example2.com │\n' +
'└──────────┴─────────────────────┘'
)
}));
});
describe('Projects deployer, Test suite', () => {
beforeEach(() => {
jest.clearAllMocks();
});
describe('prepareGatewayJson', () => {
it('should return correct GatewaysJson', () => {
const result = prepareGatewayJson('http://example.com', 'user', '', true,false);
expect(result).toEqual({
gateways: [{
gatewayURL: 'http://example.com',
gatewayUser: 'user',
gatewaySecret: '',
is_mcsp_enabled: false
}],
overwrite: 'all',
skip: 'none',
});
});
it('should handle skip and overwrite correctly when false', () => {
const result = prepareGatewayJson('http://example.com', 'user', '', false,false);
expect(result).toEqual({
gateways: [{
gatewayURL: 'http://example.com',
gatewayUser: 'user',
gatewaySecret: '',
is_mcsp_enabled: false
}],
overwrite: 'none',
skip: 'all',
});
});
it('should handle non-URL strings for target', () => {
const result = prepareGatewayJson('not-a-url', 'user', 'xyz', true,false);
expect(result).toEqual({
gateways: [
{
gatewayURL: 'not-a-url',
gatewayUser: 'user',
gatewaySecret: 'xyz',
is_mcsp_enabled: false
},
],
overwrite: 'all',
skip: 'none',
});
});
it('should handle special characters in username and password', () => {
const result = prepareGatewayJson('http://example.com', 'user!@#$', 'xyz', true,false);
expect(result).toEqual({
gateways: [
{
gatewayURL: 'http://example.com',
gatewayUser: 'user!@#$',
gatewaySecret: 'xyz',
is_mcsp_enabled: false
},
],
overwrite: 'all',
skip: 'none',
});
});
});
describe('prepareArchiveBuffer', () => {
it('should call readFileAsBuffer with the correct path', () => {
const mockBuffer = Buffer.from('test');
(readFileAsBuffer as jest.Mock).mockReturnValue(mockBuffer);
const result = prepareArchiveBuffer('path/to/archive.zip');
expect(readFileAsBuffer).toHaveBeenCalledWith('path/to/archive.zip');
expect(result).toBe(mockBuffer);
});
it('should handle large file buffer', () => {
const largeBuffer = Buffer.alloc(1024 * 1024 * 100); // 100MB
(readFileAsBuffer as jest.Mock).mockReturnValue(largeBuffer);
const result = prepareArchiveBuffer('/path/to/large-file');
expect(result.byteLength).toBe(1024 * 1024 * 100);
});
it('should handle files with special characters in path', () => {
const mockBuffer = Buffer.from('special content');
(readFileAsBuffer as jest.Mock).mockReturnValue(mockBuffer);
const result = prepareArchiveBuffer('/path/to/file-with-特殊字符');
expect(result).toBe(mockBuffer);
});
it('should handle non-existent file gracefully', () => {
(readFileAsBuffer as jest.Mock).mockImplementation(() => {
throw new Error('File not found');
});
expect(() => prepareArchiveBuffer('/path/to/non-existent-file')).toThrow('File not found');
});
});
describe('executeDeployment', () => {
const mockConsoleLog = jest.spyOn(console, 'log').mockImplementation(() => {});
beforeEach(() => {
jest.clearAllMocks();
});
it('should handle successful deployments without errors', async () => {
const gatewayJson = { gateways: [], overwrite: 'ALL', skip: 'NONE' };
const fileBuffer = Buffer.from('mock data');
const mockResponses = [
{ error: false, data: { StudioResult: [{ API: { name: 'Test API', gatewayEndpoints: ['http://example.com', 'http://example2.com'] } }] } }
];
(processDeployment as jest.Mock).mockResolvedValue(mockResponses);
await executeDeployment(gatewayJson, fileBuffer);
expect(showInfo).toHaveBeenCalledWith(LINE);
expect(showInfo).toHaveBeenCalledWith(DEPLOY_STARTED);
expect(showSuccess).toHaveBeenCalledWith(APIENDPOINTS);
expect(mockConsoleLog).toHaveBeenCalledWith(
'┌──────────┬─────────────────────┐\n' +
'│ APIs │ Gateway Endpoints │\n' +
'├──────────┼─────────────────────┤\n' +
'│ Test API │ http://example.com │\n' +
'│ │ http://example2.com │\n' +
'└──────────┴─────────────────────┘'
);
});
it('should handle deployment errors and log them correctly', async () => {
const gatewayJson = { gateways: [], overwrite: 'ALL', skip: 'NONE' };
const fileBuffer = Buffer.from('mock data');
const mockResponses = [{ error: true, message: 'Deployment failed' }];
(processDeployment as jest.Mock).mockResolvedValue(mockResponses);
await executeDeployment(gatewayJson, fileBuffer);
expect(showError).toHaveBeenCalledWith(DEPLOYMENT_FAILURE);
expect(showError).toHaveBeenCalledWith('Deployment failed');
});
it('should handle responses with successful deployments and log API endpoints', async () => {
const gatewayJson = { gateways: [], overwrite: 'ALL', skip: 'NONE' };
const fileBuffer = Buffer.from('mock data');
const mockResponses = [
{ error: false, data: { StudioResult: [{ API: { name: 'Test API', gatewayEndpoints: ['http://example.com', 'http://example2.com'], kind: 'API', namespace: 'test', version: '1.0', assetName: 'testAPI' } }] } }
];
(processDeployment as jest.Mock).mockResolvedValue(mockResponses);
await executeDeployment(gatewayJson, fileBuffer);
expect(showInfo).toHaveBeenCalledWith(LINE);
expect(showInfo).toHaveBeenCalledWith(DEPLOY_STARTED);
expect(showSuccess).toHaveBeenCalledWith(APIENDPOINTS);
expect(mockConsoleLog).toHaveBeenCalledWith(
'┌──────────┬─────────────────────┐\n' +
'│ APIs │ Gateway Endpoints │\n' +
'├──────────┼─────────────────────┤\n' +
'│ Test API │ http://example.com │\n' +
'│ │ http://example2.com │\n' +
'└──────────┴─────────────────────┘'
);
});
it('should handle partial success in deployment', async () => {
const mockGatewayJson = { gateways: [], overwrite: '', skip: '' };
const mockBuffer = Buffer.from('test buffer');
const partialSuccessResponse = [{
data: { StudioResult: [{ API: { status: 'Success', name: 'API1', namespace: 'ns', version: 'v1', assetName: 'asset1', gatewayEndpoints: ['endpoint1'] } }, { API: { status: 'Failure', name: 'API2', namespace: 'ns2', version: 'v2', assetName: 'asset2', gatewayEndpoints: ['endpoint2'] } }] }
}];
(processDeployment as jest.Mock).mockResolvedValue(partialSuccessResponse);
await executeDeployment(mockGatewayJson, mockBuffer);
expect(showSuccess).toHaveBeenCalledWith('\nGateway endpoints of the APIs in the project');
});
});
});