UNPKG

@apistudio/apim-cli

Version:

CLI for API Management Products

518 lines (486 loc) 12.1 kB
/** * 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(); }); });