@apistudio/apim-cli
Version:
CLI for API Management Products
518 lines (486 loc) • 12.1 kB
text/typescript
/**
* Copyright IBM Corp. 2024, 2025
*/
import { ZodError } from 'zod';
import {
validTest,
validTestWithEndpoint,
validTestWithEnvironmentsAndAssertions,
} from '../__mocks__/test-data/test.data.js';
import { TestSchema } from '../../src/schemas/test.schema.js';
jest.mock('@apic/api-model/common/Metadata.js', () => ({
Metadata_TypeEnum: {
REST: 'REST',
SWAGGER: 'SWAGGER',
SOAP: 'SOAP',
GRAPHQL: 'GRAPHQL',
ODATA: 'ODATA',
},
}));
const validBase = {
kind: 'test',
metadata: {
name: 'TestPayments',
version: '1.0.0',
tags: ['functional'],
namespace: 'default',
},
spec: {
api: {
$ref: 'PaymentAPI:1.0.1',
},
environment: {
$ref: 'default:TestPaymentsEnvironment:1.0.0',
},
request: [
{
method: 'POST',
resource: 'v2/pet',
headers: [
{
key: 'Content-Type',
value: '${content-type}',
},
],
auth: {
noauth: true,
},
payload: {
raw: {
json: '{\n "name":"Jose"\n}\n',
},
},
settings: {
sslVerification: false,
encodeURL: true,
},
assertions: {
$ref: 'default:TestPaymentAssertion:1.0.0',
},
},
{
endpoint: 'new-url.com/api',
method: '${method}',
resource: 'v2/pet',
var: [
{
record1: 'response[0]',
record2: 'response[1]',
},
],
headers: [
{
key: 'Content-Type',
value: '${content-type}',
},
],
auth: {
noauth: true,
},
payload: {
raw: {
json: '{\n "name":"Adam"\n}\n',
},
},
settings: {
sslVerification: false,
encodeURL: true,
},
assertions: {
$ref: 'default:TestPaymentAssertion:1.0.0',
},
},
],
},
};
describe('TestSchema', () => {
it('should pass with a minimal valid test', () => {
expect(() => TestSchema.parse(validTest)).not.toThrow();
});
it('should pass with a minimal valid test', () => {
expect(() => TestSchema.parse(validTestWithEndpoint)).not.toThrow();
});
it('should pass with a minimal valid test with multiple environments and assertions', () => {
expect(() =>
TestSchema.parse(validTestWithEnvironmentsAndAssertions),
).not.toThrow();
});
it("should fail if kind is not 'test'", () => {
const input = { ...validTest, kind: 'something-else' };
expect(() => TestSchema.parse(input)).toThrow();
expect(() => TestSchema.parse(validBase)).not.toThrow();
});
it("should fail if kind is not 'test'", () => {
const input = { ...validBase, kind: 'something-else' };
expect(() => TestSchema.parse(input)).toThrow();
});
it('should fail if api is missing $ref and $endpoint', () => {
const input = {
...validTest,
spec: {
...validTest.spec,
api: {},
},
};
expect(() => TestSchema.parse(input)).toThrow(
/Either \$ref or \$endpoint must be provided/,
);
});
it('should pass with api using $ref', () => {
const input = {
...validTest,
spec: {
...validTest.spec,
api: { $ref: '#/some/path' },
},
};
expect(() => TestSchema.parse(input)).not.toThrow();
});
it('should pass with api using $ref as array', () => {
const input = {
...validBase,
spec: {
...validBase.spec,
api: { $ref: ['#/some/path', '#/some/path2'] },
},
};
expect(() => TestSchema.parse(input)).not.toThrow();
});
it('should pass with api using $endpoint', () => {
const input = {
...validTest,
spec: {
...validTest.spec,
api: { $endpoint: '/some/path' },
},
};
expect(() => TestSchema.parse(input)).not.toThrow();
});
it('should fail with api using $endpoint as array', () => {
const input = {
...validTest,
spec: {
...validTest.spec,
api: { $endpoint: ['/some/path'] },
},
};
expect(() => TestSchema.parse(input)).toThrow();
});
it('should fail with api using $ref and $endpoint', () => {
const input = {
...validTest,
spec: {
...validTest.spec,
api: { $ref: '#/some/path', $endpoint: 'www.test.com' },
},
};
try {
TestSchema.parse(input);
} catch (e: any) {
expect(e).toBeInstanceOf(ZodError);
expect(e.issues[0].message).toMatch(
'Either $ref or $endpoint must be provided, but not both',
);
}
});
it('should pass with environment using $ref', () => {
const input = {
...validTest,
spec: {
...validTest.spec,
environment: {
$ref: '#/env/ref',
},
},
};
expect(() => TestSchema.parse(input)).not.toThrow();
});
it('should pass with environment using $ref array', () => {
const input = {
...validBase,
spec: {
...validBase.spec,
environment: [{ $ref: '#/env/ref' }, { $ref: '#/env/ref2' }],
},
};
expect(() => TestSchema.parse(input)).not.toThrow();
});
it('should pass with environment.variables', () => {
const input = {
...validTest,
spec: {
...validTest.spec,
environment: {
variables: [
{
kind: 'environment',
metadata: {
name: 'DefaultEnv',
version: '1.0.0',
namespace: 'default',
},
spec: {
variables: [{ key: 'TOKEN', value: 'abc123', isSecret: true }],
},
},
],
},
},
};
expect(() => TestSchema.parse(input)).not.toThrow();
});
it('should pass with noauth=true in auth', () => {
const input = {
...validTest,
spec: {
...validTest.spec,
request: [
{
method: 'GET',
resource: '/secure',
auth: { noauth: true },
},
],
},
};
expect(() => TestSchema.parse(input)).not.toThrow();
});
it('should pass with bearerToken in auth', () => {
const input = {
...validTest,
spec: {
...validTest.spec,
request: [
{
method: 'GET',
resource: '/secure',
auth: { bearerToken: 'my-token' },
},
],
},
};
expect(() => TestSchema.parse(input)).not.toThrow();
});
it('should pass with basicAuth in auth', () => {
const input = {
...validTest,
spec: {
...validTest.spec,
request: [
{
method: 'POST',
resource: '/secure',
auth: {
basicAuth: {
username: 'user',
password: 'pass',
},
},
},
],
},
};
expect(() => TestSchema.parse(input)).not.toThrow();
});
it('should pass with custom headers', () => {
const input = {
...validTest,
spec: {
...validTest.spec,
request: [
{
method: 'GET',
resource: '/info',
headers: [
{
key: 'Authorization',
value: 'Bearer xyz',
description: 'Auth header',
},
{
key: 'X-Custom-Header',
value: 'value',
},
],
},
],
},
};
expect(() => TestSchema.parse(input)).not.toThrow();
});
it('should fail if multiple auth fields are present', () => {
const input = {
...validTest,
spec: {
...validTest.spec,
request: [
{
method: 'GET',
resource: '/secure',
auth: {
noauth: true,
bearerToken: 'token123',
},
},
],
},
};
expect(() => TestSchema.parse(input)).toThrow(
'Only one of noauth, bearerToken, or basicAuth must be provided',
);
});
it('should fail if environment has neither $ref nor variables', () => {
const input = {
...validTest,
spec: {
...validTest.spec,
environment: {},
},
};
expect(() => TestSchema.parse(input)).toThrow(
/Either \$ref or variables.*must be provided/,
);
});
it('should fail if a header is missing key or value', () => {
const input = {
...validTest,
spec: {
...validTest.spec,
request: [
{
method: 'GET',
resource: '/bad',
headers: [
{
key: 'X-Broken',
} as any,
],
},
],
},
};
expect(() => TestSchema.parse(input)).toThrow();
});
it('should fail if environment has empty variables array', () => {
const input = {
...validTest,
spec: {
...validTest.spec,
environment: {
variables: [],
},
},
};
expect(() => TestSchema.parse(input)).toThrow(
/Either \$ref or variables.*must be provided/,
);
});
it('should pass with assertions using $ref', () => {
const input = {
...validTest,
spec: {
...validTest.spec,
request: [
{
method: 'GET',
resource: '/status',
assertions: {
$ref: '#/assert/ref',
},
},
],
},
};
expect(() => TestSchema.parse(input)).not.toThrow();
});
it('should pass with assertions using array of $ref objects', () => {
const input = {
...validBase,
spec: {
...validBase.spec,
request: [
{
method: 'GET',
resource: '/status',
assertions: [{ $ref: '#/assert/ref' }, { $ref: '#/assert/ref2' }],
},
],
},
};
expect(() => TestSchema.parse(input)).not.toThrow();
});
it('should pass with assertions using expressions', () => {
const input = {
...validTest,
spec: {
...validTest.spec,
request: [
{
method: 'GET',
resource: '/status',
assertions: {
assertions: [
{
kind: 'assertion',
metadata: {
name: 'CheckStatus',
version: '1.0.0',
namespace: 'default',
},
spec: [
{
name: 'Check status value',
key: 'status',
value: 200,
action: 'equals',
},
],
},
],
},
},
],
},
};
expect(() => TestSchema.parse(input)).not.toThrow();
});
it('should fail if payload has multiple formats', () => {
const input = {
...validTest,
spec: {
...validTest.spec,
request: [
{
method: 'POST',
resource: '/submit',
payload: {
raw: { json: '{}' },
formData: [{ key: 'name', value: 'John' }],
},
},
],
},
};
expect(() => TestSchema.parse(input)).toThrow(
/Exactly one of raw, urlEncodedFormData, or formData must be provided/,
);
});
it('should pass with a valid raw payload', () => {
const input = {
...validTest,
spec: {
...validTest.spec,
request: [
{
method: 'POST',
resource: '/submit',
payload: {
raw: { json: '{}' },
},
},
],
},
};
expect(() => TestSchema.parse(input)).not.toThrow();
});
});