Produced by Araxis Merge on 10/4/2017 8:04:41 AM Central Daylight Time. See www.araxis.com for information about Merge. This report uses XHTML and CSS2, and is best viewed with a modern standards-compliant browser. For optimum results when printing this report, use landscape orientation and enable printing of background images and colours in your browser.
| # | Location | File | Last Modified |
|---|---|---|---|
| 1 | rdk.zip\rdk\product\production\rdk\versioning-tests | versioning-tests.js | Mon Aug 21 12:51:01 2017 UTC |
| 2 | rdk.zip\rdk\product\production\rdk\versioning-tests | versioning-tests.js | Tue Oct 3 17:21:48 2017 UTC |
| Description | Between Files 1 and 2 |
|
|---|---|---|
| Text Blocks | Lines | |
| Unchanged | 2 | 884 |
| Changed | 1 | 2 |
| Inserted | 0 | 0 |
| Removed | 0 | 0 |
| Whitespace | |
|---|---|
| Character case | Differences in character case are significant |
| Line endings | Differences in line endings (CR and LF characters) are ignored |
| CR/LF characters | Not shown in the comparison detail |
No regular expressions were active.
| 1 | 'use stric t'; | |
| 2 | ||
| 3 | var fs = r equire('fs '); | |
| 4 | var _ = re quire('lod ash'); | |
| 5 | var async = require( 'async'); | |
| 6 | var httpMo cks = requ ire('node- mocks-http '); | |
| 7 | var ajv = require('a jv')(); | |
| 8 | var sinon = require( 'sinon'); | |
| 9 | var rdk = require('. ./src/core /rdk'); | |
| 10 | var loadRe sources = require('. ./src/util s/test-res ources-loa der-spec-h elper'); | |
| 11 | var buildD escription = loadRes ources.bui ldDescript ion; | |
| 12 | var mockHt tp = requi re('./http -mock').mo ckHttp; | |
| 13 | var mockVi staJS = re quire('./v istajs-moc k').mockVi staJS; | |
| 14 | var mockPa rameters = require(' ./mock-par ameters'); | |
| 15 | var verify UnchangedP arameters = require( './verify- unchanged- parameters '); | |
| 16 | var Includ eParams = mockParame ters.Inclu deParams; | |
| 17 | var readSc hema = req uire('./re cord-schem as-outerce ptor').rea dSchema; | |
| 18 | var apiBlu eprint = r equire('.. /src/core/ api-bluepr int/api-bl ueprint'); | |
| 19 | ||
| 20 | // Make th e below tr ue to enab le request logging | |
| 21 | var enable Logging = false; | |
| 22 | var printB odyForSche maErrors = false; | |
| 23 | var app; | |
| 24 | var user; | |
| 25 | var versio ningConfig ; | |
| 26 | ||
| 27 | describe(' For API ve rsion stab ility,', f unction() { | |
| 28 | ||
| 29 | mockHt tp(); | |
| 30 | mockVi staJS(); | |
| 31 | app = createApp( ); | |
| 32 | user = JSO N.parse(fs .readFileS ync('versi oning-test s/ REDACT .json', {e ncoding: ' utf8'})); | |
| 33 | versio ningConfig = loadVer sioningCon fig(); | |
| 34 | ||
| 35 | // FUT URE-TODO: get rid of this skip list | |
| 36 | var sk ip = [ | |
| 37 | '/ fhir/',// the servic es are cha nging to t he 1.0 spe c | |
| 38 | '/ asu-resour ce',// mis sing the a su config | |
| 39 | '/ tasks-reso urce',// m issing the JBPM conf ig | |
| 40 | '/ user-resou rce',// mi ssing user ResourceCo nfig | |
| 41 | 'o rder-detai l-resource ',// this always err ors for me , live | |
| 42 | '/ orderables /orderable s',// this always er rors for m e, live | |
| 43 | '/ cds-',// ( temporary? ) issues w ith the DB that CDS uses | |
| 44 | '/ user-defin ed-screens /',// TEMP | |
| 45 | ]; | |
| 46 | ||
| 47 | var re sources = loadResour ces(app); | |
| 48 | initRe sources(re sources); | |
| 49 | ||
| 50 | var fi lePaths = _.keys(res ources).so rt(); | |
| 51 | _.each (filePaths , function (filePath) { | |
| 52 | // FUTURE-TO DO: kill t hese skip tests | |
| 53 | if (_.contai ns(filePat h, '/write /') && !_. contains(f ilePath, ' /write/pic k-list/')) { | |
| 54 | return; | |
| 55 | } | |
| 56 | if (_.find(s kip, funct ion(entry) { | |
| 57 | return _ .contains( filePath, entry); | |
| 58 | }) ) { | |
| 59 | return; | |
| 60 | } | |
| 61 | ||
| 62 | va r resource Configs = resources[ filePath]; | |
| 63 | _. each(resou rceConfigs , function (resource) { | |
| 64 | var desc ription = buildDescr iption(res ource, res ourceConfi gs, filePa th); | |
| 65 | ||
| 66 | describe (descripti on, functi on(suite) { | |
| 67 | ||
| 68 | var method = g etMethod(r esource); | |
| 69 | var error; | |
| 70 | ||
| 71 | befo re(functio n(done) { | |
| 72 | this.timeo ut(120000) ; | |
| 73 | ||
| 74 | prepareReq uests(reso urce, meth od, functi on(err, re quests) { | |
| 75 | ||
| 76 | descri be(descrip tion, func tion() { | |
| 77 | _. each(reque sts, funct ion(reques t) { | |
| 78 | var shou ld = build ShouldPhra se(request ); | |
| 79 | ||
| 80 | it(shoul d, functio n(done) { | |
| 81 | make Request(re quest, res ource, don e); | |
| 82 | }); | |
| 83 | }) ; | |
| 84 | ||
| 85 | do ne(err); | |
| 86 | }); | |
| 87 | }); | |
| 88 | }); | |
| 89 | ||
| 90 | if ( !resource. undocument ed) { | |
| 91 | it('should have API Blueprint documentat ion', func tion() { | |
| 92 | error = error || 'Missing documentat ion for ' + method.t oUpperCase () + ' ' + resource. path; | |
| 93 | expect (resource. jsonDocume ntation, e rror).to.b e.an.objec t(); | |
| 94 | expect (mockParam eters.acti onDocument ationFor(r esource, m ethod.toUp perCase()) , error).t o.be.an.ob ject(); | |
| 95 | }); | |
| 96 | ||
| 97 | it('should not have warnings i n its API Blueprint documentat ion', func tion(done) { | |
| 98 | if (!_ .isEmpty(r esource.js onDocument ation.warn ings)) { | |
| 99 | ap iBlueprint .loadFullM arkdown(re source.fil ePath + '. md', resou rce.mountp oint, null , function (error, ma rkdown) { | |
| 100 | expect(r esource.js onDocument ation.warn ings, mark down).to.b e.empty(); | |
| 101 | done(); | |
| 102 | }) ; | |
| 103 | } else { | |
| 104 | do ne(); | |
| 105 | } | |
| 106 | }); | |
| 107 | ||
| 108 | it('should not chang e its para meters', f unction() { | |
| 109 | verify UnchangedP arameters( resource, method); | |
| 110 | }); | |
| 111 | } | |
| 112 | ||
| 113 | }); | |
| 114 | }) ; | |
| 115 | }); | |
| 116 | }); | |
| 117 | ||
| 118 | function b uildShould Phrase(req uest) { | |
| 119 | var ph rase = 'sh ould handl e a ' + re quest.meth od + ' req uest'; | |
| 120 | if (re quest.vers ioningSetu p.simulate ExternalEr ror) { | |
| 121 | ph rase += ' with a \'' + request .versionin gSetup.sim ulateExter nalError + '\' error from exte rnal resou rces'; | |
| 122 | } else { | |
| 123 | ph rase += ' with ' + r equest.ver sioningSet up.include Params.rep lace(/none /, 'no') + ' paramet ers provid ed'; | |
| 124 | } | |
| 125 | return phrase; | |
| 126 | } | |
| 127 | ||
| 128 | function c reateApp() { | |
| 129 | return rdk.appfa ctory().de faultConfi gFilename( '../../con fig/rdk-fe tch-server -config.js on').build (); | |
| 130 | } | |
| 131 | ||
| 132 | function l oadVersion ingConfig( ) { | |
| 133 | var ve rsioningCo nfig = req uire('./ve rsioning-t ests-confi g.json'); | |
| 134 | _.each (versionin gConfig.ig nore, func tion(ignor e) { | |
| 135 | de lete ignor e.justific ation; | |
| 136 | }); | |
| 137 | return versionin gConfig; | |
| 138 | } | |
| 139 | ||
| 140 | function i nitResourc es(resourc es) { | |
| 141 | _.each (resources , function (resourceC onfigs, fi lePath) { | |
| 142 | _. each(resou rceConfigs , function (resource) { | |
| 143 | resource .filePath = filePath ; | |
| 144 | resource .intercept ors = _.me rge((resou rce.interc eptors || {}), { | |
| 145 | metr ics: true, | |
| 146 | audi t: true, | |
| 147 | vali datePid: t rue, | |
| 148 | assi gnRequestS ite: true, | |
| 149 | conv ertPid: tr ue, | |
| 150 | vali dateReques tParameter s: true, | |
| 151 | auth entication : false, | |
| 152 | pep: false, | |
| 153 | oper ationalDat aCheck: fa lse, | |
| 154 | sync hronize: f alse | |
| 155 | }); | |
| 156 | if (!res ource.undo cumented) { | |
| 157 | var path = res ource.moun tpoint.len gth > 1 ? resource.m ountpoint : resource .path; | |
| 158 | var markdownPa th = resou rce.apiBlu eprintFile || filePa th + '.md' ; | |
| 159 | apiB lueprint.r egisterRes ource(path , markdown Path, fals e); | |
| 160 | } | |
| 161 | }) ; | |
| 162 | }); | |
| 163 | } | |
| 164 | ||
| 165 | function g etMethod(r esource) { | |
| 166 | return _.find([' get', 'pos t', 'put', 'delete'] , function (httpMetho d) { | |
| 167 | re turn _.has (resource, httpMetho d); | |
| 168 | }); | |
| 169 | } | |
| 170 | ||
| 171 | function p repareRequ ests(resou rce, metho d, callbac k) { | |
| 172 | apiBlu eprint.jso nDocumenta tionForPat h(resource .path, fun ction(erro r, jsonDoc umentation ) { | |
| 173 | if (error) { | |
| 174 | return c allback(er ror); | |
| 175 | } | |
| 176 | ||
| 177 | re source.jso nDocumenta tion = jso nDocumenta tion; | |
| 178 | ||
| 179 | va r requests = [ | |
| 180 | prepareR equest(res ource, met hod, Inclu deParams.r equired), | |
| 181 | prepareR equest(res ource, met hod, Inclu deParams.r equired, ' ESIMULATED '), | |
| 182 | prepareR equest(res ource, met hod, Inclu deParams.r equired, 4 04), | |
| 183 | prepareR equest(res ource, met hod, Inclu deParams.r equired, 5 00) | |
| 184 | ]; | |
| 185 | // FUTURE- TODO: use the first request to determine the numbe r of exter nal calls, then crea te request s | |
| 186 | // that si mulate fai lure for e ach one in turn. Thi s would al so avoid t he 404/500 requests for | |
| 187 | // VistaJS resources . | |
| 188 | ||
| 189 | // FUTURE- TODO: mayb e create s eparate re quests for each memb er of enum parameter s. | |
| 190 | ||
| 191 | // FUTURE- TODO: if a resource has multip le request types (li ke permiss ion-sets-r esource), test each one. | |
| 192 | if (mockPara meters.par ametersFor (resource, method).l ength > 0) { | |
| 193 | requests .push(prep areRequest (resource, method, I ncludePara ms.all)); | |
| 194 | requests .push(prep areRequest (resource, method, I ncludePara ms.none)); | |
| 195 | // FUTURE- TODO: supp ort invali d | |
| 196 | // reque sts.push(p repareRequ est(resour ce, method , IncludeP arams.inva lid)); | |
| 197 | } | |
| 198 | ||
| 199 | re turn callb ack(null, _.filter(r equests)); | |
| 200 | }); | |
| 201 | } | |
| 202 | ||
| 203 | function p repareRequ est(resour ce, method , includeP arams, sim ulateExter nalError) { | |
| 204 | if (sh ouldIgnore Request(re source, me thod, incl udeParams, simulateE xternalErr or)) { | |
| 205 | re turn undef ined; | |
| 206 | } | |
| 207 | ||
| 208 | var pa rameters = mockParam eters(reso urce, meth od, includ eParams); | |
| 209 | var ur l = replac ePathParam eters(reso urce.path, parameter s.path); | |
| 210 | ||
| 211 | var re quest = ht tpMocks.cr eateReques t({ | |
| 212 | me thod: meth od.toUpper Case(), | |
| 213 | ur l: url, | |
| 214 | he aders: par ameters.he aders, | |
| 215 | pa rams: para meters.pat h, | |
| 216 | qu ery: param eters.quer y, | |
| 217 | bo dy: parame ters.body | |
| 218 | }); | |
| 219 | ||
| 220 | reques t.route = { | |
| 221 | pa th: resour ce.path, | |
| 222 | st ack: [{ | |
| 223 | handle: resource[m ethod], | |
| 224 | name: re source.tit le, | |
| 225 | method: method | |
| 226 | }] | |
| 227 | }; | |
| 228 | ||
| 229 | reques t.session = { | |
| 230 | us er: user, | |
| 231 | co okie: { | |
| 232 | httpOnly: true, | |
| 233 | path: '/', | |
| 234 | expires: n ew Date(). toString() | |
| 235 | }, | |
| 236 | to uch: funct ion() {}, | |
| 237 | de stroy: fun ction(call back) { | |
| 238 | delete t his.user; | |
| 239 | callback (); | |
| 240 | } | |
| 241 | }; | |
| 242 | ||
| 243 | reques t.app = ap p; | |
| 244 | reques t._resourc eConfigIte m = resour ce; | |
| 245 | reques t.intercep torResults = {}; | |
| 246 | if (en ableLoggin g) { | |
| 247 | re quest.logg er = app.l ogger; | |
| 248 | } else { | |
| 249 | re quest.logg er = sinon .stub(requ ire('bunya n').create Logger({na me: 'versi oning-test s'})); | |
| 250 | } | |
| 251 | ||
| 252 | reques t.versioni ngSetup = { | |
| 253 | in cludeParam s: include Params, | |
| 254 | si mulateExte rnalError: simulateE xternalErr or | |
| 255 | }; | |
| 256 | ||
| 257 | return request; | |
| 258 | } | |
| 259 | ||
| 260 | function s houldIgnor eRequest(r esource, m ethod, inc ludeParams , simulate ExternalEr ror) { | |
| 261 | var ca ndidate = { | |
| 262 | pa th: resour ce.path, | |
| 263 | me thod: meth od.toUpper Case(), | |
| 264 | in cludeParam s: include Params, | |
| 265 | si mulateExte rnalError: simulateE xternalErr or | |
| 266 | }; | |
| 267 | if (si mulateExte rnalError) { | |
| 268 | ca ndidate.si mulateExte rnalError = simulate ExternalEr ror; | |
| 269 | } | |
| 270 | return _.filter( versioning Config.ign ore, funct ion(ignore ) { | |
| 271 | re turn _.isM atch(ignor e, candida te) || _.i sMatch(can didate, ig nore); | |
| 272 | }).len gth > 0; | |
| 273 | } | |
| 274 | ||
| 275 | function r eplacePath Parameters (url, path Params) { | |
| 276 | _.each (pathParam s, functio n(value, n ame) { | |
| 277 | ur l = url.re place(':' + name, va lue); | |
| 278 | }); | |
| 279 | return url; | |
| 280 | } | |
| 281 | ||
| 282 | function m akeRequest (request, resource, done) { | |
| 283 | var me thod = req uest.metho d.toLowerC ase(); | |
| 284 | var re sponse; | |
| 285 | var re sponses = []; | |
| 286 | var ha ndleRespon se = funct ion() { | |
| 287 | // some reso urces erro neously se nd multipl e response s | |
| 288 | re sponses.pu sh(respons e._getData ()); | |
| 289 | ex pect(respo nses.lengt h, 'Sent m ultiple re sponses: ' + JSON.st ringify(re sponses)). to.equal(1 ); | |
| 290 | ||
| 291 | ch eckRespons e(response , request, resource) ; | |
| 292 | ||
| 293 | // allows ch ecking for multiple responses | |
| 294 | se tImmediate (done); | |
| 295 | }; | |
| 296 | ||
| 297 | respon se = prepa reResponse (resource, handleRes ponse); | |
| 298 | callIn terceptors (resource, request, response, function() { | |
| 299 | if (request. versioning Setup.simu lateExtern alError) { | |
| 300 | // A hor rible hack : the only way of re liably com municating with the HTTP and V istaJS | |
| 301 | // subsy stems is t hrough the logger wh ich is alw ays passed to their config. | |
| 302 | request. logger = i nheritClon e(request. logger); | |
| 303 | request. logger.sim ulateExter nalError = request.v ersioningS etup.simul ateExterna lError; | |
| 304 | } | |
| 305 | ||
| 306 | re source[met hod](reque st, respon se, handle Response); | |
| 307 | }); | |
| 308 | } | |
| 309 | ||
| 310 | function i nheritClon e(item) { | |
| 311 | // thi s allows u s to keep prototype fields, wh ich in the case of l ogger are necessary | |
| 312 | var Co nstructor = function () {}; | |
| 313 | Constr uctor.prot otype = it em; | |
| 314 | return new Const ructor(); | |
| 315 | } | |
| 316 | ||
| 317 | function p repareResp onse(resou rce, done) { | |
| 318 | var re sponse = h ttpMocks.c reateRespo nse(); | |
| 319 | ||
| 320 | respon se.type = function() {return r esponse;}; | |
| 321 | ||
| 322 | var fa keapp = { | |
| 323 | us e: functio n(next) { | |
| 324 | next({}, response, function( ){}); | |
| 325 | } | |
| 326 | }; | |
| 327 | rdk.ap pfactory._ addRdkSend ToResponse (fakeapp); | |
| 328 | ||
| 329 | respon se._wrappe d_emit = r esponse.em it; | |
| 330 | respon se.emit = function(e vent) { | |
| 331 | if (event == = 'end') { | |
| 332 | done(); | |
| 333 | } | |
| 334 | th is._wrappe d_emit.app ly(this, a rguments); | |
| 335 | }; | |
| 336 | ||
| 337 | respon se.setTime out = func tion() {}; | |
| 338 | ||
| 339 | return response; | |
| 340 | } | |
| 341 | ||
| 342 | function c allInterce ptors(reso urce, requ est, respo nse, next) { | |
| 343 | _.each Right(app. intercepto rs, functi on(interce ptor) { | |
| 344 | va r intercep torName = _.first(_. keys(inter ceptor)); | |
| 345 | if (resource .intercept ors[interc eptorName] ) { | |
| 346 | next = i nterceptor [intercept orName].bi nd(null, r equest, re sponse, ne xt); | |
| 347 | } | |
| 348 | }); | |
| 349 | ||
| 350 | next() ; | |
| 351 | } | |
| 352 | ||
| 353 | function c heckRespon se(respons e, request , resource ) { | |
| 354 | var ex pectError = expectEx ternalErro r(request) || expect ParameterE rror(reque st); | |
| 355 | if (ex pectError) { | |
| 356 | if (_.isNumb er(expectE rror)) { | |
| 357 | expect(r esponse.st atusCode, 'Unexpecte d response status co de').to.eq ual(expect Error); | |
| 358 | } | |
| 359 | } else { | |
| 360 | va r message = 'Expecte d successf ul respons e status c ode'; | |
| 361 | if (response .statusCod e < 200 || response. statusCode >= 300) { | |
| 362 | message += ', ' + JSON.strin gify(respo nse._getDa ta()); | |
| 363 | } | |
| 364 | ex pect(respo nse.status Code, mess age).to.be .gte(200); | |
| 365 | ex pect(respo nse.status Code, mess age).to.be .lt(300); | |
| 366 | } | |
| 367 | ||
| 368 | var ch eckSchema = true; | |
| 369 | var ex pectedStat usCode = f alse; | |
| 370 | var ac tion = moc kParameter s.actionDo cumentatio nFor(resou rce, reque st.method) ; | |
| 371 | if (ac tion) { | |
| 372 | _. each(actio n.examples , function (example) { | |
| 373 | _.each(e xample.res ponses, fu nction(exa mpleRespon se) { | |
| 374 | var statusCode = parseIn t(exampleR esponse.na me, 10); | |
| 375 | if ( response.s tatusCode === status Code) { | |
| 376 | expectedSt atusCode = true; | |
| 377 | if (exampl eResponse. schema) { | |
| 378 | try { | |
| 379 | va r schema = JSON.pars e(exampleR esponse.sc hema); | |
| 380 | } catc h (e) { | |
| 381 | co nsole.log( 'failed to parse sch ema ' + ex ampleRespo nse.schema ); | |
| 382 | th row e; | |
| 383 | } | |
| 384 | valida teAgainstS chema(sche ma, respon se, resour ce); | |
| 385 | checkS chema = fa lse; | |
| 386 | } else if (statusCod e === 204) { | |
| 387 | checkS chema = fa lse; | |
| 388 | } else { | |
| 389 | var co ntentType = (_.find( exampleRes ponse.head ers, funct ion(header ) { | |
| 390 | re turn heade r.name === 'Content- Type'; | |
| 391 | }) || {}).value; | |
| 392 | if (co ntentType && !_.cont ains(conte ntType, 'j son')) { | |
| 393 | ch eckSchema = false; | |
| 394 | } | |
| 395 | } | |
| 396 | } | |
| 397 | }); | |
| 398 | }) ; | |
| 399 | ||
| 400 | va r error = 'Unexpecte d status c ode: ' + r esponse.st atusCode + ' not lis ted in API Blueprint documenta tion for ' + resourc e.filePath ; | |
| 401 | ex pect(expec tedStatusC ode, error ).to.be.tr ue(); | |
| 402 | ||
| 403 | // FUTURE- TODO: veri fy that al l document ed status codes were exercised ? | |
| 404 | } | |
| 405 | ||
| 406 | if (ch eckSchema) { | |
| 407 | va r schema; | |
| 408 | tr y { | |
| 409 | schema = readSchem a(request, response) ; | |
| 410 | } catch (e) {} | |
| 411 | ||
| 412 | va lidateAgai nstSchema( schema, re sponse, re source); | |
| 413 | } | |
| 414 | } | |
| 415 | ||
| 416 | function e xpectExter nalError(r equest) { | |
| 417 | if (re quest.logg er.didSimu lateExtern alError) { | |
| 418 | re turn reque st.logger. simulateEx ternalErro r; | |
| 419 | } | |
| 420 | return false; | |
| 421 | } | |
| 422 | ||
| 423 | function e xpectParam eterError( request) { | |
| 424 | var in cludeParam s = reques t.versioni ngSetup.in cludeParam s; | |
| 425 | return includePa rams === I ncludePara ms.none || includePa rams === I ncludePara ms.invalid ; | |
| 426 | } | |
| 427 | ||
| 428 | function v alidateAga instSchema (schema, r esponse, r esource) { | |
| 429 | if (!s chema) { | |
| 430 | th row new Er ror('No re sponse sch ema found for ' + re source.fil ePath + ' with statu s code ' + (response .statusCod e || 200)) ; | |
| 431 | } | |
| 432 | ||
| 433 | var bo dy = respo nse._getDa ta(); | |
| 434 | try { | |
| 435 | bo dy = JSON. parse(body ); | |
| 436 | } catc h (e) {} | |
| 437 | ||
| 438 | var va lid = ajv. validate(s chema, bod y); | |
| 439 | if (!v alid && pr intBodyFor SchemaErro rs) { | |
| 440 | co nsole.log( JSON.strin gify(body) ); | |
| 441 | } | |
| 442 | expect (valid, aj v.errorsTe xt(ajv.err ors, { dat aVar: 'res ponse' })) .to.be.tru e(); | |
| 443 | } |
Araxis Merge (but not the data content of this report) is Copyright © 1993-2016 Araxis Ltd (www.araxis.com). All rights reserved.