@ibm-cloud/openapi-ruleset
Version:
Spectral ruleset for validating IBM Cloud services
236 lines (219 loc) • 7.15 kB
JavaScript
/**
* Copyright 2017 - 2024 IBM Corporation.
* SPDX-License-Identifier: Apache2.0
*/
const {
makeCopy,
rootDocument,
testRule,
severityCodes,
} = require('../test-utils');
let rule = require('../../src/rules').noOperationRequestBody;
const ruleId = 'ibm-no-operation-requestbody';
const expectedSeverity = severityCodes.warning;
const expectedMsg = 'operations should not define a requestBody';
describe(`Spectral rule: ${ruleId}`, () => {
describe('Should not yield errors', () => {
afterEach(() => {
jest.resetModules();
rule = require('../../src/rules').noOperationRequestBody;
});
it('Clean spec', async () => {
const results = await testRule(ruleId, rule, rootDocument);
expect(results).toHaveLength(0);
});
it('Delete operation w/requestBody - delete not in config', async () => {
const testDocument = makeCopy(rootDocument);
// omit 'delete' from the list of methods.
rule.then.functionOptions.httpMethods = ['get', 'head', 'options'];
testDocument.paths['/v1/drinks/{drink_id}'].delete = {
operationId: 'delete_drink',
summary: 'Pour a drink',
description: 'Pour a drink down the drain',
tags: ['TestTag'],
requestBody: {
content: {
'text/html': {
schema: {
type: 'string',
},
},
},
},
responses: {
204: {
description: 'Drink successfully poured!',
},
400: {
$ref: '#/components/responses/BarIsClosed',
},
},
};
const results = await testRule(ruleId, rule, testDocument);
expect(results).toHaveLength(0);
});
});
describe('Should yield errors', () => {
it('DELETE operation w/requestBody', async () => {
const testDocument = makeCopy(rootDocument);
testDocument.paths['/v1/drinks/{drink_id}'].delete = {
operationId: 'delete_drink',
summary: 'Pour a drink down the drain',
description: 'Pour a drink down the drain',
tags: ['TestTag'],
requestBody: {
content: {
'text/html': {
schema: {
type: 'string',
},
},
},
},
responses: {
204: {
description: 'Drink successfully poured!',
},
400: {
$ref: '#/components/responses/BarIsClosed',
},
},
};
const results = await testRule(ruleId, rule, testDocument);
expect(results).toHaveLength(1);
expect(results[0].code).toBe(ruleId);
expect(results[0].message).toBe(`DELETE ${expectedMsg}`);
expect(results[0].severity).toBe(expectedSeverity);
expect(results[0].path.join('.')).toBe(
'paths./v1/drinks/{drink_id}.delete.requestBody'
);
});
it('GET operation w/requestBody', async () => {
const testDocument = makeCopy(rootDocument);
testDocument.paths['/v1/drinks/{drink_id}'].get = {
operationId: 'imagine_drink',
summary: 'Imagine the drink',
description: 'Imagine consuming the drink',
tags: ['TestTag'],
requestBody: {
content: {
'text/html': {
schema: {
type: 'string',
},
},
},
},
responses: {
204: {
description: 'Drink successfully imagined!',
},
400: {
$ref: '#/components/responses/BarIsClosed',
},
},
};
const results = await testRule(ruleId, rule, testDocument);
expect(results).toHaveLength(1);
expect(results[0].code).toBe(ruleId);
expect(results[0].message).toBe(`GET ${expectedMsg}`);
expect(results[0].severity).toBe(expectedSeverity);
expect(results[0].path.join('.')).toBe(
'paths./v1/drinks/{drink_id}.get.requestBody'
);
});
it('HEAD operation w/requestBody', async () => {
const testDocument = makeCopy(rootDocument);
testDocument.paths['/v1/drinks/{drink_id}'].head = {
operationId: 'evaporate_drink',
summary: 'Evaporate the drink',
description: 'Evaporate the drink',
tags: ['TestTag'],
requestBody: {
content: {
'text/html': {
schema: {
type: 'string',
},
},
},
},
responses: {
204: {
description: 'Drink successfully evaporated!',
},
400: {
$ref: '#/components/responses/BarIsClosed',
},
},
};
const results = await testRule(ruleId, rule, testDocument);
expect(results).toHaveLength(1);
expect(results[0].code).toBe(ruleId);
expect(results[0].message).toBe(`HEAD ${expectedMsg}`);
expect(results[0].severity).toBe(expectedSeverity);
expect(results[0].path.join('.')).toBe(
'paths./v1/drinks/{drink_id}.head.requestBody'
);
});
it('OPTIONS operation w/requestBody', async () => {
const testDocument = makeCopy(rootDocument);
testDocument.paths['/v1/drinks/{drink_id}'].options = {
operationId: 'needa_drink',
summary: 'Need a drink',
description: 'Need a drink',
tags: ['TestTag'],
requestBody: {
content: {
'text/html': {
schema: {
type: 'string',
},
},
},
},
responses: {
204: {
description: 'Drink successfully needed!',
},
400: {
$ref: '#/components/responses/BarIsClosed',
},
},
};
const results = await testRule(ruleId, rule, testDocument);
expect(results).toHaveLength(1);
expect(results[0].code).toBe(ruleId);
expect(results[0].message).toBe(`OPTIONS ${expectedMsg}`);
expect(results[0].severity).toBe(expectedSeverity);
expect(results[0].path.join('.')).toBe(
'paths./v1/drinks/{drink_id}.options.requestBody'
);
});
it('POST operation w/requestBody - post in config', async () => {
const testDocument = makeCopy(rootDocument);
rule.then.functionOptions.httpMethods = [
'delete',
'get',
'head',
'options',
'POST',
];
// No need to change testDocument because there are plenty of post
// operations with a requestBody :)
const results = await testRule(ruleId, rule, testDocument);
expect(results).toHaveLength(3);
const expectedPaths = [
'paths./v1/drinks.post.requestBody',
'paths./v1/movies.post.requestBody',
'paths./v1/cars.post.requestBody',
];
for (const i in results) {
expect(results[i].code).toBe(ruleId);
expect(results[i].message).toBe(`POST ${expectedMsg}`);
expect(results[i].severity).toBe(expectedSeverity);
expect(results[i].path.join('.')).toBe(expectedPaths[i]);
}
});
});
});