@apistudio/apim-cli
Version:
CLI for API Management Products
560 lines (515 loc) • 17.3 kB
text/typescript
/**
* Copyright IBM Corp. 2024, 2025
*/
import { TestManager } from '../../src/index.js';
import path from 'path';
import fs from 'fs';
import { TestRunner } from '../../src/engine/execution/test-runner.js';
jest.mock('../../src/service/log-wrapper.js');
jest.mock('../../src/helpers/trace-helper.js', () => ({
fetchTraceAndCatalogData: jest.fn(),
fetchCaptureId: jest.fn()
}));
jest.mock('@apic/studio-logger', () => ({
LoggerConfig: {
isLoggerEnabled: jest.fn(),
},
}));
jest.mock('../../src/engine/execution/test-runner.js', () => ({
TestRunner: jest.fn().mockImplementation(() => ({
run: jest.fn(),
})),
}));
describe('test manager tests', () => {
const MockTestResponseValid = {
id: '6d619b61-1de3-473c-a508-948f872ce090',
name: 'PetPostTest Collection',
timestamp: 1750224392789,
totalPass: 8,
status: 'finished',
startedAt: 1750224389787,
totalFail: 0,
totalTime: 3002,
results: [
{
id: '',
name: 'POST /pet',
url: 'https://petstore.swagger.io/v2/pet',
method: 'POST',
header: {
'Content-Type': 'application/json',
Accept: 'application/json',
},
time: 1539,
responseCode: {
code: 200,
name: 'OK',
time: 1538,
size: 0,
},
response: {
id: 9223372036854739000,
category: {
id: 0,
name: 'string',
},
name: 'doggie',
photoUrls: ['string'],
tags: [
{
id: 0,
name: 'string',
},
],
status: 'available',
},
responseHeaders: [
{
key: 'date',
value: 'Wed, 18 Jun 2025 05:26:31 GMT',
},
{
key: 'content-type',
value: 'application/json',
},
{
key: 'transfer-encoding',
value: 'chunked',
},
{
key: 'connection',
value: 'close',
},
{
key: 'access-control-allow-origin',
value: '*',
},
{
key: 'access-control-allow-methods',
value: 'GET, POST, DELETE, PUT',
},
{
key: 'access-control-allow-headers',
value: 'Content-Type, api_key, Authorization',
},
{
key: 'server',
value: 'Jetty(9.2.9.v20150224)',
},
],
allTests: [
{
'Validate Response time': {
status: true,
actualValue: 1538,
expectedValue: 3,
action: 'notEquals',
},
},
],
},
],
};
const mockedResponseInvalidOutputvariable = {
id: '1fb34fdd-989d-4a4f-b821-b2c9b776694b',
name: 'PetPostTest Collection',
timestamp: 1750227902380,
totalPass: 0,
status: 'finished',
startedAt: 1750227902377,
totalFail: 0,
totalTime: 3,
results: [],
};
const mockedResponseValidOutputVariable = {
id: 'd4bf0632-86f2-42dc-b7f6-9485815f553a',
name: 'PetPostTest Collection',
timestamp: 1750233105313,
totalPass: 3,
status: 'finished',
startedAt: 1750233104110,
totalFail: 0,
totalTime: 1203,
results: [
{
id: '',
name: 'GET /pet/9223372036854740000',
url: 'https://petstore.swagger.io/v2/pet/9223372036854740000',
method: 'GET',
header: {
'Content-Type': 'application/json',
Accept: 'application/json',
},
time: 1202,
responseCode: {
code: 200,
name: 'OK',
time: 1201,
size: 0,
},
response: {
id: 9223372036854740000,
name: 'UPDATED DOGGIE',
photoUrls: ['https://example.com/dog.jpg'],
tags: [],
},
responseHeaders: [
{
key: 'date',
value: 'Wed, 18 Jun 2025 07:51:45 GMT',
},
{
key: 'content-type',
value: 'application/json',
},
{
key: 'transfer-encoding',
value: 'chunked',
},
{
key: 'connection',
value: 'close',
},
{
key: 'access-control-allow-origin',
value: '*',
},
{
key: 'access-control-allow-methods',
value: 'GET, POST, DELETE, PUT',
},
{
key: 'access-control-allow-headers',
value: 'Content-Type, api_key, Authorization',
},
{
key: 'server',
value: 'Jetty(9.2.9.v20150224)',
},
],
allTests: [
{
'Validate Response time': {
status: true,
actualValue: 1201,
expectedValue: 3,
action: 'notEquals',
},
},
{
'Validate data id': {
status: true,
actualValue: 9223372036854740000,
expectedValue: 1000,
action: 'greaterThan',
},
},
{
'Custom chai assertion': {
status: true,
actualValue: 9223372036854740000,
expectedValue: 2,
action: 'notEquals',
},
},
],
},
],
};
const mockedResponseInvalidOutputVariableAssertion = {
id: '7134489e-4481-4fc9-9401-51f03bbcf550',
name: 'PetPostTest Collection',
timestamp: 1750237726325,
totalPass: 2,
status: 'finished',
startedAt: 1750237725089,
totalFail: 1,
totalTime: 1236,
results: [
{
id: '',
name: 'GET /pet/9223372036854741000',
url: 'https://petstore.swagger.io/v2/pet/9223372036854741000',
method: 'GET',
header: {
'Content-Type': 'application/json',
Accept: 'application/json',
},
time: 1235,
responseCode: {
code: 200,
name: 'OK',
time: 1235,
size: 0,
},
response: {
id: 9223372036854741000,
category: {
id: 0,
name: 'string',
},
name: 'doggie',
photoUrls: ['string'],
tags: [
{
id: 0,
name: 'string',
},
],
status: 'string',
},
responseHeaders: [
{
key: 'date',
value: 'Wed, 18 Jun 2025 09:08:46 GMT',
},
{
key: 'content-type',
value: 'application/json',
},
{
key: 'transfer-encoding',
value: 'chunked',
},
{
key: 'connection',
value: 'close',
},
{
key: 'access-control-allow-origin',
value: '*',
},
{
key: 'access-control-allow-methods',
value: 'GET, POST, DELETE, PUT',
},
{
key: 'access-control-allow-headers',
value: 'Content-Type, api_key, Authorization',
},
{
key: 'server',
value: 'Jetty(9.2.9.v20150224)',
},
],
allTests: [
{
'Validate Response time': {
status: true,
actualValue: 1235,
expectedValue: 2,
action: 'notEquals',
},
},
{
'Validate data id': {
status: false,
error: {
name: 'Error',
test: 'Validate data id',
message:
"Cannot resolve path 'payload.data.ids' – 'ids' not found.",
stack:
"Error: Cannot resolve path 'payload.data.ids' – 'ids' not found.\n at ContextManager.resolvePath (file:///Users/anulal/Developer/api-studio/packages/studio-test/dist/engine/variable-context-manager/context-manager.js:137:23)\n at ContextManager.resolveValue (file:///Users/anulal/Developer/api-studio/packages/studio-test/dist/engine/variable-context-manager/context-manager.js:110:25)\n at ContextManager.resolve (file:///Users/anulal/Developer/api-studio/packages/studio-test/dist/engine/variable-context-manager/context-manager.js:86:21)\n at AssertionEngine.resolveValue (file:///Users/anulal/Developer/api-studio/packages/studio-test/dist/engine/assertion/assertion.engine.js:75:20)\n at AssertionEngine.assert (file:///Users/anulal/Developer/api-studio/packages/studio-test/dist/engine/assertion/assertion.engine.js:22:42)\n at TestRunner.run (file:///Users/anulal/Developer/api-studio/packages/studio-test/dist/engine/execution/test-runner.js:35:68)\n at process.processTicksAndRejections (node:internal/process/task_queues:95:5)\n at async Promise.allSettled (index 0)\n at async TestManager.processFile (file:///Users/anulal/Developer/api-studio/packages/studio-test/dist/managers/test.manager.js:210:35)\n at async file:///Users/anulal/Developer/api-studio/packages/studio-server/dist/controllers/test.controller.js:36:25",
},
action: 'equals',
},
},
{
'Custom chai assertion': {
status: true,
actualValue: 9223372036854741000,
expectedValue: 2,
action: 'notEquals',
},
},
],
},
],
};
it('should return response for valid asset', async () => {
(TestRunner as jest.Mock).mockImplementation(() => {
return {
run: () => MockTestResponseValid,
};
});
const obj = new TestManager();
const zipFilePath = path.resolve(__dirname, '../assets/valid-asset.zip');
const zipBuffer = fs.readFileSync(zipFilePath);
const result = await obj.processFile(zipBuffer);
expect(Array.isArray(result)).toBe(true);
expect(result[0]).toEqual(MockTestResponseValid);
});
it('should return empty result if output variable is not defined in test file', async () => {
(TestRunner as jest.Mock).mockImplementation(() => {
return {
run: () => mockedResponseInvalidOutputvariable,
};
});
const obj = new TestManager();
const zipFilePath = path.resolve(
__dirname,
'../assets/invalidOutputVariable.zip',
);
const zipBuffer = fs.readFileSync(zipFilePath);
const result = await obj.processFile(zipBuffer);
expect(Array.isArray(result)).toBe(true);
expect(result).toEqual([mockedResponseInvalidOutputvariable]);
});
it('should return response if output variable is defined in test file', async () => {
(TestRunner as jest.Mock).mockImplementation(() => {
return {
run: () => mockedResponseValidOutputVariable,
};
});
const obj = new TestManager();
const zipFilePath = path.resolve(__dirname, '../assets/valid-asset.zip');
const zipBuffer = fs.readFileSync(zipFilePath);
const result = await obj.processFile(zipBuffer);
expect(result).toEqual([mockedResponseValidOutputVariable]);
});
it('should return null if output variable is not defined in Assertion file', async () => {
(TestRunner as jest.Mock).mockImplementation(() => {
return {
run: () => mockedResponseInvalidOutputVariableAssertion,
};
});
const obj = new TestManager();
const zipFilePath = path.resolve(
__dirname,
'../assets/InvalidOutputVariableInAssertion.zip',
);
const zipBuffer = fs.readFileSync(zipFilePath);
const result = await obj.processFile(zipBuffer);
expect(Array.isArray(result)).toBe(true);
expect(result).toEqual([mockedResponseInvalidOutputVariableAssertion]);
});
describe('processTrace', () => {
beforeEach(() => {
jest.clearAllMocks();
});
it('should call fetchTraceAndCatalogData with the correct parameters', async () => {
// Mock the fetchTraceAndCatalogData function
const mockTraceData = {
traceData: { id: 'trace-123', details: 'Sample trace data' },
catalogData: { id: 'catalog-456', name: 'Test API' }
};
const traceHelperModule = require('../../src/helpers/trace-helper.js');
traceHelperModule.fetchTraceAndCatalogData.mockResolvedValue(mockTraceData);
// Create test data
const reqBody = JSON.stringify({
url: 'https://api.example.com',
apiName: 'test-api'
});
const reqHeader = {
'x-access-token': 'test-token'
};
// Call the method
const testManager = new TestManager();
const result = await testManager.processTrace(reqBody, reqHeader);
// Assertions
expect(traceHelperModule.fetchTraceAndCatalogData).toHaveBeenCalledWith(reqBody, reqHeader);
expect(result).toEqual(mockTraceData);
});
it('should handle errors from fetchTraceAndCatalogData', async () => {
// Mock the fetchTraceAndCatalogData function to throw an error
const mockError = new Error('Failed to fetch trace data');
const traceHelperModule = require('../../src/helpers/trace-helper.js');
traceHelperModule.fetchTraceAndCatalogData.mockRejectedValue(mockError);
// Create test data
const reqBody = JSON.stringify({
url: 'https://api.example.com',
apiName: 'test-api'
});
const reqHeader = {
'x-access-token': 'test-token'
};
// Call the method
const testManager = new TestManager();
// Assert that the error is propagated
await expect(testManager.processTrace(reqBody, reqHeader)).rejects.toThrow('Failed to fetch trace data');
});
it('should return null when fetchTraceAndCatalogData returns null', async () => {
// Mock the fetchTraceAndCatalogData function to return null
const traceHelperModule = require('../../src/helpers/trace-helper.js');
traceHelperModule.fetchTraceAndCatalogData.mockResolvedValue(null);
// Create test data
const reqBody = JSON.stringify({
url: 'https://api.example.com',
apiName: 'test-api'
});
const reqHeader = {
'x-access-token': 'test-token'
};
// Call the method
const testManager = new TestManager();
const result = await testManager.processTrace(reqBody, reqHeader);
// Assertions
expect(result).toBeNull();
});
});
describe('getCaptureId', () => {
beforeEach(() => {
jest.clearAllMocks();
});
it('should call fetchCaptureId with the correct parameters', async () => {
// Mock the fetchCaptureId function
const mockCaptureData = {
captureId: 'capture-123',
status: 'success'
};
const traceHelperModule = require('../../src/helpers/trace-helper.js');
traceHelperModule.fetchCaptureId.mockResolvedValue(mockCaptureData);
// Create test data
const reqBody = JSON.stringify({
url: 'https://api.example.com',
apiName: 'test-api'
});
const reqHeader = {
'x-access-token': 'test-token'
};
// Call the method
const testManager = new TestManager();
const result = await testManager.getCaptureId(reqBody, reqHeader);
// Assertions
expect(traceHelperModule.fetchCaptureId).toHaveBeenCalledWith(reqBody, reqHeader);
expect(result).toEqual(mockCaptureData);
});
it('should handle errors from fetchCaptureId', async () => {
// Mock the fetchCaptureId function to throw an error
const mockError = new Error('Failed to fetch capture ID');
const traceHelperModule = require('../../src/helpers/trace-helper.js');
traceHelperModule.fetchCaptureId.mockRejectedValue(mockError);
// Create test data
const reqBody = JSON.stringify({
url: 'https://api.example.com',
apiName: 'test-api'
});
const reqHeader = {
'x-access-token': 'test-token'
};
// Call the method
const testManager = new TestManager();
// Assert that the error is propagated
await expect(testManager.getCaptureId(reqBody, reqHeader)).rejects.toThrow('Failed to fetch capture ID');
});
it('should return null when fetchCaptureId returns null', async () => {
// Mock the fetchCaptureId function to return null
const traceHelperModule = require('../../src/helpers/trace-helper.js');
traceHelperModule.fetchCaptureId.mockResolvedValue(null);
// Create test data
const reqBody = JSON.stringify({
url: 'https://api.example.com',
apiName: 'test-api'
});
const reqHeader = {
'x-access-token': 'test-token'
};
// Call the method
const testManager = new TestManager();
const result = await testManager.getCaptureId(reqBody, reqHeader);
// Assertions
expect(result).toBeNull();
});
});
});