feat: wip, write api tests framework

re-using read api tests if possible
v1.18.x
Julian Lam
parent b3ca7de016
commit b156b8b573

@ -34,43 +34,6 @@ function authenticatedRoutes() {
setupApiRoute(router, 'post', '/:uid/tokens', [...middlewares, middleware.assert.user, middleware.exposePrivilegeSet], controllers.write.users.generateToken);
setupApiRoute(router, 'delete', '/:uid/tokens/:token', [...middlewares, middleware.assert.user, middleware.exposePrivilegeSet], controllers.write.users.deleteToken);
/**
* Implement this later...
*/
// app.route('/:uid/tokens')
// .get(apiMiddleware.requireUser, function(req, res) {
// if (parseInt(req.params.uid, 10) !== parseInt(req.user.uid, 10)) {
// return errorHandler.respond(401, res);
// }
// auth.getTokens(req.params.uid, function(err, tokens) {
// return errorHandler.handle(err, res, {
// tokens: tokens
// });
// });
// })
// .post(apiMiddleware.requireUser, function(req, res) {
// if (parseInt(req.params.uid, 10) !== parseInt(req.user.uid)) {
// return errorHandler.respond(401, res);
// }
// auth.generateToken(req.params.uid, function(err, token) {
// return errorHandler.handle(err, res, {
// token: token
// });
// });
// });
// app.delete('/:uid/tokens/:token', apiMiddleware.requireUser, function(req, res) {
// if (parseInt(req.params.uid, 10) !== req.user.uid) {
// return errorHandler.respond(401, res);
// }
// auth.revokeToken(req.params.token, 'user', function(err) {
// errorHandler.handle(err, res);
// });
// });
}
module.exports = function () {

@ -21,7 +21,9 @@ const messaging = require('../src/messaging');
describe('Read API', async () => {
let readApi = false;
const apiPath = path.resolve(__dirname, '../public/openapi/read.yaml');
let writeApi = false;
const readApiPath = path.resolve(__dirname, '../public/openapi/read.yaml');
const writeApiPath = path.resolve(__dirname, '../public/openapi/write.yaml');
let jar;
let setup = false;
const unauthenticatedRoutes = ['/api/login', '/api/register']; // Everything else will be called with the admin user
@ -67,7 +69,7 @@ describe('Read API', async () => {
await socketUser.exportPosts({ uid: adminUid }, { uid: adminUid });
await socketUser.exportUploads({ uid: adminUid }, { uid: adminUid });
// wait for export child process to complete
await wait(20000);
// await wait(20000);
// Attach a search hook so /api/search is enabled
plugins.registerHook('core', {
@ -81,24 +83,89 @@ describe('Read API', async () => {
it('should pass OpenAPI v3 validation', async () => {
try {
await SwaggerParser.validate(apiPath);
await SwaggerParser.validate(readApiPath);
await SwaggerParser.validate(writeApiPath);
} catch (e) {
assert.ifError(e);
}
});
readApi = await SwaggerParser.dereference(apiPath);
readApi = await SwaggerParser.dereference(readApiPath);
writeApi = await SwaggerParser.dereference(writeApiPath);
// Iterate through all documented paths, make a call to it, and compare the result body with what is defined in the spec
const paths = Object.keys(readApi.paths);
const paths = Object.keys(writeApi.paths);
// const paths = Object.keys(readApi.paths);
paths.forEach((path) => {
const context = writeApi.paths[path];
// const context = readApi.paths[path];
let schema;
let response;
let url;
const urls = [];
const methods = [];
const headers = {};
const qs = {};
it('should have examples when parameters are present', () => {
Object.keys(context).forEach((method) => {
const parameters = context[method].parameters;
let testPath = path;
if (parameters) {
parameters.forEach((param) => {
assert(param.example !== null && param.example !== undefined, path + ' has parameters without examples');
switch (param.in) {
case 'path':
testPath = testPath.replace('{' + param.name + '}', param.example);
break;
case 'header':
headers[param.name] = param.example;
break;
case 'query':
qs[param.name] = param.example;
break;
}
});
}
urls.push = nconf.get('url') + testPath;
methods.push(method);
});
});
it('should resolve with a 200 when called', async () => {
await setupData();
try {
response = await request(url, {
jar: !unauthenticatedRoutes.includes(path) ? jar : undefined,
json: true,
headers: headers,
qs: qs,
});
} catch (e) {
assert(!e, path + ' resolved with ' + e.message);
}
});
// Recursively iterate through schema properties, comparing type
it('response should match schema definition', () => {
const has200 = context.get.responses['200'];
if (!has200) {
return;
}
const hasJSON = has200.content && has200.content['application/json'];
if (hasJSON) {
schema = context.get.responses['200'].content['application/json'].schema;
compare(schema, response, 'root');
}
// TODO someday: text/csv, binary file type checking?
});
});
function compare(schema, response, context) {
let required = [];
const additionalProperties = schema.hasOwnProperty('additionalProperties');
@ -173,79 +240,4 @@ describe('Read API', async () => {
assert(schema[prop], '"' + prop + '" was found in response, but is not defined in schema (path: ' + path + ', context: ' + context + ')');
});
}
// TOXO: fix -- premature exit for POST-only routes
if (!readApi.paths[path].get) {
return;
}
it('should have examples when parameters are present', () => {
const parameters = readApi.paths[path].get.parameters;
let testPath = path;
if (parameters) {
parameters.forEach((param) => {
assert(param.example !== null && param.example !== undefined, path + ' has parameters without examples');
switch (param.in) {
case 'path':
testPath = testPath.replace('{' + param.name + '}', param.example);
break;
case 'header':
headers[param.name] = param.example;
break;
case 'query':
qs[param.name] = param.example;
break;
}
});
}
url = nconf.get('url') + testPath;
});
it('should resolve with a 200 when called', async () => {
await setupData();
try {
response = await request(url, {
jar: !unauthenticatedRoutes.includes(path) ? jar : undefined,
json: true,
headers: headers,
qs: qs,
});
} catch (e) {
assert(!e, path + ' resolved with ' + e.message);
}
});
// Recursively iterate through schema properties, comparing type
it('response should match schema definition', () => {
const has200 = readApi.paths[path].get.responses['200'];
if (!has200) {
return;
}
const hasJSON = has200.content && has200.content['application/json'];
if (hasJSON) {
schema = readApi.paths[path].get.responses['200'].content['application/json'].schema;
compare(schema, response, 'root');
}
// TODO someday: text/csv, binary file type checking?
});
});
});
describe('Write API', async () => {
const apiPath = path.resolve(__dirname, '../public/openapi/write.yaml');
it('should pass OpenAPI v3 validation', async () => {
try {
await SwaggerParser.validate(apiPath);
} catch (e) {
assert.ifError(e);
}
});
console.log(await SwaggerParser.dereference(apiPath));
});

Loading…
Cancel
Save