UNPKG

mathoid

Version:

Render TeX to SVG and MathML using MathJax. Based on svgtex.

291 lines (246 loc) 7.46 kB
'use strict'; const preq = require( 'preq' ); const assert = require( '../../utils/assert.js' ); const server = require( '../../utils/server.js' ); const URI = require( 'swagger-router' ).URI; const yaml = require( 'js-yaml' ); const fs = require( 'fs' ); if ( !server.stopHookAdded ) { server.stopHookAdded = true; // eslint-disable-next-line mocha/no-top-level-hooks after( () => server.stop() ); } function staticSpecLoad() { let spec; const myService = server.config.conf.services[ server.config.conf.services.length - 1 ].conf; const specPath = `${__dirname}/../../../${myService.spec ? myService.spec : 'spec.yaml'}`; try { spec = yaml.load( fs.readFileSync( specPath ) ); } catch ( e ) { // this error will be detected later, so ignore it spec = { paths: {}, 'x-default-params': {} }; } return spec; } function validateExamples( pathStr, defParams, mSpec ) { const uri = new URI( pathStr, {}, true ); if ( !mSpec ) { try { uri.expand( defParams ); return true; } catch ( e ) { throw new Error( `Missing parameter for route ${pathStr} : ${e.message}` ); } } if ( !Array.isArray( mSpec ) ) { throw new Error( `Route ${pathStr} : x-amples must be an array!` ); } mSpec.forEach( ( ex, idx ) => { if ( !ex.title ) { throw new Error( `Route ${pathStr}, example ${idx}: title missing!` ); } ex.request = ex.request || {}; try { uri.expand( Object.assign( {}, defParams, ex.request.params || {} ) ); } catch ( e ) { throw new Error( `Route ${pathStr}, example ${idx} (${ex.title}): missing parameter: ${e.message}` ); } } ); return true; } function constructTestCase( title, path, method, request, response ) { return { title, request: { uri: server.config.uri + ( path[ 0 ] === '/' ? path.substr( 1 ) : path ), method, headers: request.headers || {}, query: request.query, body: request.body, followRedirect: false }, response: { status: response.status || 200, headers: response.headers || {}, body: response.body } }; } function constructTests( paths, defParams ) { const ret = []; Object.keys( paths ).forEach( ( pathStr ) => { Object.keys( paths[ pathStr ] ).forEach( ( method ) => { const p = paths[ pathStr ][ method ]; if ( {}.hasOwnProperty.call( p, 'x-monitor' ) && !p[ 'x-monitor' ] ) { return; } const uri = new URI( pathStr, {}, true ); if ( !p[ 'x-amples' ] ) { ret.push( constructTestCase( pathStr, uri.toString( { params: defParams } ), method, {}, {} ) ); return; } p[ 'x-amples' ].forEach( ( ex ) => { ex.request = ex.request || {}; ret.push( constructTestCase( ex.title, uri.toString( { params: Object.assign( {}, defParams, ex.request.params || {} ) } ), method, ex.request, ex.response || {} ) ); } ); } ); } ); return ret; } function cmp( result, expected, errMsg ) { if ( expected === null || expected === undefined ) { // nothing to expect, so we can return return true; } if ( result === null || result === undefined ) { result = ''; } if ( expected.constructor === Object ) { Object.keys( expected ).forEach( ( key ) => { const val = expected[ key ]; assert.deepEqual( {}.hasOwnProperty.call( result, key ), true, `Body field ${key} not found in response!` ); cmp( result[ key ], val, `${key} body field mismatch!` ); } ); return true; } else if ( expected.constructor === Array ) { if ( result.constructor !== Array ) { assert.deepEqual( result, expected, errMsg ); return true; } // only one item in expected - compare them all if ( expected.length === 1 && result.length > 1 ) { result.forEach( ( item ) => { cmp( item, expected[ 0 ], errMsg ); } ); return true; } // more than one item expected, check them one by one if ( expected.length !== result.length ) { assert.deepEqual( result, expected, errMsg ); return true; } expected.forEach( ( item, idx ) => { cmp( result[ idx ], item, errMsg ); } ); return true; } if ( expected.length > 1 && expected[ 0 ] === '/' && expected[ expected.length - 1 ] === '/' ) { if ( ( new RegExp( expected.slice( 1, -1 ) ) ).test( result ) ) { return true; } } else if ( expected.length === 0 && result.length === 0 ) { return true; } else if ( result === expected || result.startsWith( expected ) ) { return true; } assert.deepEqual( result, expected, errMsg ); return true; } function validateTestResponse( testCase, res ) { const expRes = testCase.response; // check the status assert.status( res, expRes.status ); // check the headers Object.keys( expRes.headers ).forEach( ( key ) => { const val = expRes.headers[ key ]; assert.deepEqual( {}.hasOwnProperty.call( res.headers, key ), true, `Header ${key} not found in response!` ); cmp( res.headers[ key ], val, `${key} header mismatch!` ); } ); // check the body if ( !expRes.body ) { return true; } res.body = res.body || ''; if ( Buffer.isBuffer( res.body ) ) { res.body = res.body.toString(); } if ( expRes.body.constructor !== res.body.constructor ) { if ( expRes.body.constructor === String ) { res.body = JSON.stringify( res.body ); } else { res.body = JSON.parse( res.body ); } } // check that the body type is the same if ( expRes.body.constructor !== res.body.constructor ) { throw new Error( `Expected body type ${expRes.body.constructor} but got ${res.body.constructor}` ); } // compare the bodies cmp( res.body, expRes.body, 'Body mismatch!' ); return true; } describe( 'Swagger spec', function () { // the variable holding the spec let spec = staticSpecLoad(); // default params, if given let defParams = spec[ 'x-default-params' ] || {}; this.timeout( 20000 ); before( () => { return server.start(); } ); it( 'get the spec', () => { return preq.get( `${server.config.uri}?spec` ) .then( ( res ) => { assert.status( 200 ); assert.contentType( res, 'application/json' ); assert.notDeepEqual( res.body, undefined, 'No body received!' ); spec = res.body; } ); } ); it( 'spec validation', () => { if ( spec[ 'x-default-params' ] ) { defParams = spec[ 'x-default-params' ]; } // check the high-level attributes [ 'info', 'openapi', 'paths' ].forEach( ( prop ) => { assert.deepEqual( !!spec[ prop ], true, `No ${prop} field present!` ); } ); // no paths - no love assert.deepEqual( !!Object.keys( spec.paths ), true, 'No paths given in the spec!' ); // now check each path Object.keys( spec.paths ).forEach( ( pathStr ) => { assert.deepEqual( !!pathStr, true, 'A path cannot have a length of zero!' ); const path = spec.paths[ pathStr ]; assert.deepEqual( !!Object.keys( path ), true, `No methods defined for path: ${pathStr}` ); Object.keys( path ).forEach( ( method ) => { const mSpec = path[ method ]; if ( {}.hasOwnProperty.call( mSpec, 'x-monitor' ) && !mSpec[ 'x-monitor' ] ) { return; } validateExamples( pathStr, defParams, mSpec[ 'x-amples' ] ); } ); } ); } ); describe( 'routes', () => { constructTests( spec.paths, defParams ).forEach( ( testCase ) => { it( testCase.title, () => { return preq( testCase.request ) .then( ( res ) => { validateTestResponse( testCase, res ); }, ( err ) => { validateTestResponse( testCase, err ); } ); } ); } ); } ); } );