@apistudio/apim-cli
Version:
CLI for API Management Products
257 lines (231 loc) • 8.14 kB
text/typescript
/**
* Copyright IBM Corp. 2024, 2025
*/
import axios from 'axios';
import FormData from 'form-data';
import {
sendToGateway,
validationManager,
ParseAndFindResponse,
} from '../../src/service/gateway-service.js';
jest.mock('axios');
const mockAppend = jest.fn();
const mockGetHeaders = jest.fn().mockReturnValue({});
jest.mock('form-data', () => {
return jest.fn().mockImplementation(() => ({
append: mockAppend,
getHeaders: mockGetHeaders,
}));
});
jest.mock('@apic/studio-shared', () => {
return {
Logger: {
debug: jest.fn(),
info: jest.fn(),
warn: jest.fn(),
error: jest.fn(),
log: jest.fn(),
},
IpcError: class IpcError extends Error {
status: number;
data: any;
constructor(message: string, status: number, data?: any) {
super(message);
this.name = 'IpcError';
this.status = status;
this.data = data;
}
},
GatewaysJson: {
gateways: [],
overwrite: true,
skip: false,
},
};
});
describe('gateway-service', () => {
const zipBuffer = Buffer.from('mock');
const gatewayJson = { overwrite: true } as any;
describe('sendToGateway', () => {
beforeEach(() => {
jest.clearAllMocks();
(FormData as any).mockImplementation(() => ({
append: jest.fn(),
getHeaders: jest.fn().mockReturnValue({}),
}));
});
it('should send successfully with basic auth', async () => {
(axios.post as jest.Mock).mockResolvedValue({ status: 200, data: {} });
await expect(
sendToGateway('http://example.com', 'user', 'pass', false, zipBuffer, gatewayJson)
).resolves.toMatchObject({
success: true,
statusCode: 200,
message: 'Api deployment Successful',
});
});
it('should send successfully with bearer token and failed APIs in response', async () => {
(axios.post as jest.Mock).mockResolvedValue({
status: 200,
data: {
StudioResult: [
{
API: { status: 'Failed', explanation: 'Something bad happened' },
},
],
},
});
await expect(
sendToGateway('http://example.com', '', 'token', true, zipBuffer, gatewayJson)
).resolves.toMatchObject({
success: false,
errors: expect.arrayContaining(['Something bad happened']),
message: 'Api deployment Failed',
});
});
it('should handle axios post failure without status (fallback to 404)', async () => {
(axios.post as jest.Mock).mockRejectedValue({ message: 'Post failed' });
await expect(
sendToGateway('http://example.com', 'user', 'pass', false, zipBuffer, gatewayJson)
).resolves.toMatchObject({ success: false, statusCode: 404 });
});
it('should handle 401 response and provide error message in sendToGateway', async () => {
(axios.post as jest.Mock).mockRejectedValue({
message: 'Unauthorized',
response: { status: 401 },
});
await expect(
sendToGateway('http://example.com', 'user', 'wrongpass', false, zipBuffer, gatewayJson)
).resolves.toMatchObject({
statusCode: 401,
errors: expect.arrayContaining([
'Unauthorized access: Please check your authentication details.',
]),
});
});
it('should handle error with errorDetails in response', async () => {
(axios.post as jest.Mock).mockRejectedValue({
message: 'Post failed',
response: {
status: 500,
data: {
errorDetails: 'Detailed error message from gateway',
},
},
});
await expect(
sendToGateway('http://example.com', 'user', 'pass', false, zipBuffer, gatewayJson)
).resolves.toMatchObject({
success: false,
statusCode: 500,
message: 'Detailed error message from gateway',
errors: ['Detailed error message from gateway'],
});
});
it('should fallback to error.message when errorDetails is not present', async () => {
(axios.post as jest.Mock).mockRejectedValue({
message: 'Generic failure',
response: {
status: 500,
data: {},
},
});
await expect(
sendToGateway('http://example.com', 'user', 'pass', false, zipBuffer, gatewayJson)
).resolves.toMatchObject({
success: false,
statusCode: 500,
message: expect.stringContaining('Response sending to http://example.com: Generic failure'),
});
});
});
describe('validationManager', () => {
const url = 'https://example.com';
const authorizationHeader = 'Bearer token';
it('should return { data: "OK", status: 200 } for a successful request', async () => {
(axios.get as jest.Mock).mockResolvedValue({
status: 200,
data: 'Success',
});
await expect(validationManager(url, authorizationHeader)).resolves.toEqual({
data: 'OK',
status: 200,
});
});
it('should throw an error for a 401 Unauthorized response', async () => {
(axios.get as jest.Mock).mockRejectedValue({
response: { status: 401, data: 'Unauthorized' },
});
await expect(validationManager(url, authorizationHeader)).rejects.toThrow(
'The user name or password did not match'
);
});
it('should throw an error for a 502 Bad Gateway response', async () => {
(axios.get as jest.Mock).mockRejectedValue({
response: { status: 502, data: 'Bad Gateway' },
});
await expect(validationManager(url, authorizationHeader)).rejects.toThrow('Bad Gateway');
});
it('should throw an error for a 404 Not Found response', async () => {
(axios.get as jest.Mock).mockRejectedValue({
response: { status: 404, data: 'Not Found' },
});
await expect(validationManager(url, authorizationHeader)).rejects.toThrow(
'Gateway not found'
);
});
it('should throw an error for a timeout', async () => {
(axios.get as jest.Mock).mockRejectedValue({ code: 'ECONNABORTED' });
await expect(validationManager(url, authorizationHeader)).rejects.toThrow(
'Request timed out'
);
});
it('should throw an error for an unknown error', async () => {
(axios.get as jest.Mock).mockRejectedValue(new Error('Unknown error'));
await expect(validationManager(url, authorizationHeader)).rejects.toThrow(
'Something went wrong while validating the gateway.'
);
});
it('should throw an error for an unexpected status code', async () => {
(axios.get as jest.Mock).mockResolvedValue({
status: 418,
data: 'I’m a teapot',
});
await expect(validationManager(url, authorizationHeader)).rejects.toThrow(
'Something went wrong while validating the gateway.'
);
});
it('should throw an error for unexpected status code', async () => {
(axios.get as jest.Mock).mockRejectedValue({
response: { status: 409, data: 'Conflict occurred' },
});
await expect(validationManager('http://example.com', 'token')).rejects.toThrow(
'Conflict occurred'
);
});
});
describe('ParseAndFindResponse', () => {
it('should return failed explanations', () => {
const result = ParseAndFindResponse({
data: {
StudioResult: [
{ API: { status: 'Failed', explanation: 'Failed 1' } },
{ API: { status: 'Success' } },
{ API: { status: 'Failed', explanation: 'Failed 2' } },
],
},
});
expect(result).toEqual(['Failed 1', 'Failed 2']);
});
it('should return empty array for no failures', () => {
const result = ParseAndFindResponse({
data: { StudioResult: [{ API: { status: 'Success' } }] },
});
expect(result).toEqual([]);
});
it('should return empty array for invalid input', () => {
expect(ParseAndFindResponse(null)).toEqual([]);
expect(ParseAndFindResponse({ data: {} })).toEqual([]);
});
});
});