|
|
|
@ -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));
|
|
|
|
|
});
|
|
|
|
|