apiTest.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519
  1. var should = require('chai').should();
  2. var request = require('request');
  3. var Q = require('q');
  4. var config = {
  5. "authorizedKeys": {
  6. "1234567890": "contact@gaelmetais.com"
  7. }
  8. };
  9. var serverUrl = 'http://localhost:8387';
  10. var wwwUrl = 'http://localhost:8388';
  11. describe('api', function() {
  12. var syncRunResultUrl;
  13. var asyncRunId;
  14. it('should refuse a query with an invalid key', function(done) {
  15. this.timeout(5000);
  16. request({
  17. method: 'POST',
  18. url: serverUrl + '/api/runs',
  19. body: {
  20. url: wwwUrl + '/simple-page.html',
  21. waitForResponse: false
  22. },
  23. json: true,
  24. headers: {
  25. 'X-Api-Key': 'invalid'
  26. }
  27. }, function(error, response, body) {
  28. if (!error && response.statusCode === 401) {
  29. done();
  30. } else {
  31. done(error || response.statusCode);
  32. }
  33. });
  34. });
  35. it('should fail without an URL when asynchronous', function(done) {
  36. this.timeout(15000);
  37. request({
  38. method: 'POST',
  39. url: serverUrl + '/api/runs',
  40. body: {
  41. url: ''
  42. },
  43. json: true,
  44. headers: {
  45. 'X-Api-Key': Object.keys(config.authorizedKeys)[0]
  46. }
  47. }, function(error, response, body) {
  48. if (!error && response.statusCode === 400) {
  49. done();
  50. } else {
  51. done(error || response.statusCode);
  52. }
  53. });
  54. });
  55. it('should fail without an URL when synchronous', function(done) {
  56. this.timeout(15000);
  57. request({
  58. method: 'POST',
  59. url: serverUrl + '/api/runs',
  60. body: {
  61. url: '',
  62. waitForResponse: true
  63. },
  64. json: true,
  65. headers: {
  66. 'X-Api-Key': Object.keys(config.authorizedKeys)[0]
  67. }
  68. }, function(error, response, body) {
  69. if (!error && response.statusCode === 400) {
  70. done();
  71. } else {
  72. done(error || response.statusCode);
  73. }
  74. });
  75. });
  76. it('should launch a synchronous run', function(done) {
  77. this.timeout(15000);
  78. request({
  79. method: 'POST',
  80. url: serverUrl + '/api/runs',
  81. body: {
  82. url: wwwUrl + '/simple-page.html',
  83. waitForResponse: true
  84. },
  85. json: true,
  86. headers: {
  87. 'X-Api-Key': Object.keys(config.authorizedKeys)[0]
  88. }
  89. }, function(error, response, body) {
  90. if (!error && response.statusCode === 302) {
  91. response.headers.should.have.a.property('location').that.is.a('string');
  92. syncRunResultUrl = response.headers.location;
  93. done();
  94. } else {
  95. done(error || response.statusCode);
  96. }
  97. });
  98. });
  99. it('should return the rules only', function(done) {
  100. this.timeout(15000);
  101. request({
  102. method: 'POST',
  103. url: serverUrl + '/api/runs',
  104. body: {
  105. url: wwwUrl + '/simple-page.html',
  106. waitForResponse: true,
  107. partialResult: 'rules'
  108. },
  109. json: true,
  110. headers: {
  111. 'X-Api-Key': Object.keys(config.authorizedKeys)[0]
  112. }
  113. }, function(error, response, body) {
  114. if (!error && response.statusCode === 302) {
  115. console.log(response.headers.location);
  116. response.headers.should.have.a.property('location').that.is.a('string');
  117. response.headers.location.should.contain('/rules');
  118. done();
  119. } else {
  120. done(error || response.statusCode);
  121. }
  122. });
  123. });
  124. it('should retrieve the results for the synchronous run', function(done) {
  125. this.timeout(15000);
  126. request({
  127. method: 'GET',
  128. url: serverUrl + syncRunResultUrl,
  129. json: true,
  130. }, function(error, response, body) {
  131. if (!error && response.statusCode === 200) {
  132. body.should.have.a.property('runId').that.is.a('string');
  133. body.should.have.a.property('params').that.is.an('object');
  134. body.should.have.a.property('scoreProfiles').that.is.an('object');
  135. body.should.have.a.property('rules').that.is.an('object');
  136. body.should.have.a.property('toolsResults').that.is.an('object');
  137. body.should.have.a.property('javascriptExecutionTree').that.is.an('object');
  138. done();
  139. } else {
  140. done(error || response.statusCode);
  141. }
  142. });
  143. });
  144. it('should launch a run without waiting for the response', function(done) {
  145. this.timeout(5000);
  146. request({
  147. method: 'POST',
  148. url: serverUrl + '/api/runs',
  149. body: {
  150. url: wwwUrl + '/simple-page.html',
  151. waitForResponse: false
  152. },
  153. json: true,
  154. headers: {
  155. 'X-Api-Key': Object.keys(config.authorizedKeys)[0]
  156. }
  157. }, function(error, response, body) {
  158. if (!error && response.statusCode === 200) {
  159. asyncRunId = body.runId;
  160. asyncRunId.should.be.a('string');
  161. done();
  162. } else {
  163. done(error || response.statusCode);
  164. }
  165. });
  166. });
  167. it('should respond run status: running', function(done) {
  168. this.timeout(5000);
  169. request({
  170. method: 'GET',
  171. url: serverUrl + '/api/runs/' + asyncRunId,
  172. json: true,
  173. headers: {
  174. 'X-Api-Key': Object.keys(config.authorizedKeys)[0]
  175. }
  176. }, function(error, response, body) {
  177. if (!error && response.statusCode === 200) {
  178. body.runId.should.equal(asyncRunId);
  179. body.status.should.deep.equal({
  180. statusCode: 'running'
  181. });
  182. done();
  183. } else {
  184. done(error || response.statusCode);
  185. }
  186. });
  187. });
  188. it('should accept up to 10 anonymous runs to the API', function(done) {
  189. this.timeout(5000);
  190. function launchRun() {
  191. var deferred = Q.defer();
  192. request({
  193. method: 'POST',
  194. url: serverUrl + '/api/runs',
  195. body: {
  196. url: wwwUrl + '/simple-page.html',
  197. waitForResponse: false
  198. },
  199. json: true
  200. }, function(error, response, body) {
  201. lastRunId = body.runId;
  202. if (error) {
  203. deferred.reject(error);
  204. } else {
  205. deferred.resolve(response, body);
  206. }
  207. });
  208. return deferred.promise;
  209. }
  210. launchRun()
  211. .then(launchRun)
  212. .then(launchRun)
  213. .then(launchRun)
  214. .then(launchRun)
  215. .then(function(response, body) {
  216. // Here should still be ok
  217. response.statusCode.should.equal(200);
  218. launchRun()
  219. .then(launchRun)
  220. .then(launchRun)
  221. .then(launchRun)
  222. .then(launchRun)
  223. .then(launchRun)
  224. .then(function(response, body) {
  225. // It should fail now
  226. response.statusCode.should.equal(429);
  227. done();
  228. })
  229. .fail(function(error) {
  230. done(error);
  231. });
  232. }).fail(function(error) {
  233. done(error);
  234. });
  235. });
  236. it('should respond 404 to unknown runId', function(done) {
  237. this.timeout(5000);
  238. request({
  239. method: 'GET',
  240. url: serverUrl + '/api/runs/unknown',
  241. json: true
  242. }, function(error, response, body) {
  243. if (!error && response.statusCode === 404) {
  244. done();
  245. } else {
  246. done(error || response.statusCode);
  247. }
  248. });
  249. });
  250. it('should respond 404 to unknown result', function(done) {
  251. this.timeout(5000);
  252. request({
  253. method: 'GET',
  254. url: serverUrl + '/api/results/unknown',
  255. json: true
  256. }, function(error, response, body) {
  257. if (!error && response.statusCode === 404) {
  258. done();
  259. } else {
  260. done(error || response.statusCode);
  261. }
  262. });
  263. });
  264. it('should respond status complete to the first run', function(done) {
  265. this.timeout(12000);
  266. function checkStatus() {
  267. request({
  268. method: 'GET',
  269. url: serverUrl + '/api/runs/' + asyncRunId,
  270. json: true
  271. }, function(error, response, body) {
  272. if (!error && response.statusCode === 200) {
  273. body.runId.should.equal(asyncRunId);
  274. if (body.status.statusCode === 'running') {
  275. setTimeout(checkStatus, 250);
  276. } else if (body.status.statusCode === 'complete') {
  277. done();
  278. } else {
  279. done(body.status.statusCode);
  280. }
  281. } else {
  282. done(error || response.statusCode);
  283. }
  284. });
  285. }
  286. checkStatus();
  287. });
  288. it('should find the result of the async run', function(done) {
  289. this.timeout(5000);
  290. request({
  291. method: 'GET',
  292. url: serverUrl + '/api/results/' + asyncRunId,
  293. json: true,
  294. }, function(error, response, body) {
  295. if (!error && response.statusCode === 200) {
  296. body.should.have.a.property('runId').that.equals(asyncRunId);
  297. body.should.have.a.property('params').that.is.an('object');
  298. body.params.url.should.equal(wwwUrl + '/simple-page.html');
  299. body.should.have.a.property('scoreProfiles').that.is.an('object');
  300. body.scoreProfiles.should.have.a.property('generic').that.is.an('object');
  301. body.scoreProfiles.generic.should.have.a.property('globalScore').that.is.a('number');
  302. body.scoreProfiles.generic.should.have.a.property('categories').that.is.an('object');
  303. body.should.have.a.property('rules').that.is.an('object');
  304. body.should.have.a.property('toolsResults').that.is.an('object');
  305. body.toolsResults.should.have.a.property('phantomas').that.is.an('object');
  306. body.should.have.a.property('javascriptExecutionTree').that.is.an('object');
  307. body.javascriptExecutionTree.should.have.a.property('data').that.is.an('object');
  308. body.javascriptExecutionTree.data.should.have.a.property('type').that.equals('main');
  309. done();
  310. } else {
  311. done(error || response.statusCode);
  312. }
  313. });
  314. });
  315. it('should return the generic score object', function(done) {
  316. this.timeout(5000);
  317. request({
  318. method: 'GET',
  319. url: serverUrl + '/api/results/' + asyncRunId + '/generalScores',
  320. json: true,
  321. }, function(error, response, body) {
  322. if (!error && response.statusCode === 200) {
  323. body.should.have.a.property('globalScore').that.is.a('number');
  324. body.should.have.a.property('categories').that.is.an('object');
  325. done();
  326. } else {
  327. done(error || response.statusCode);
  328. }
  329. });
  330. });
  331. it('should return the generic score object also', function(done) {
  332. this.timeout(5000);
  333. request({
  334. method: 'GET',
  335. url: serverUrl + '/api/results/' + asyncRunId + '/generalScores/generic',
  336. json: true,
  337. }, function(error, response, body) {
  338. if (!error && response.statusCode === 200) {
  339. body.should.have.a.property('globalScore').that.is.a('number');
  340. body.should.have.a.property('categories').that.is.an('object');
  341. done();
  342. } else {
  343. done(error || response.statusCode);
  344. }
  345. });
  346. });
  347. it('should not find an unknown score object', function(done) {
  348. this.timeout(5000);
  349. request({
  350. method: 'GET',
  351. url: serverUrl + '/api/results/' + asyncRunId + '/generalScores/unknown',
  352. json: true,
  353. }, function(error, response, body) {
  354. if (!error && response.statusCode === 404) {
  355. done();
  356. } else {
  357. done(error || response.statusCode);
  358. }
  359. });
  360. });
  361. it('should return the rules', function(done) {
  362. this.timeout(5000);
  363. request({
  364. method: 'GET',
  365. url: serverUrl + '/api/results/' + asyncRunId + '/rules',
  366. json: true,
  367. }, function(error, response, body) {
  368. if (!error && response.statusCode === 200) {
  369. var firstRule = body[Object.keys(body)[0]];
  370. firstRule.should.have.a.property('policy').that.is.an('object');
  371. firstRule.should.have.a.property('value').that.is.a('number');
  372. firstRule.should.have.a.property('bad').that.is.a('boolean');
  373. firstRule.should.have.a.property('abnormal').that.is.a('boolean');
  374. firstRule.should.have.a.property('score').that.is.a('number');
  375. firstRule.should.have.a.property('abnormalityScore').that.is.a('number');
  376. done();
  377. } else {
  378. done(error || response.statusCode);
  379. }
  380. });
  381. });
  382. it('should return the javascript execution tree', function(done) {
  383. this.timeout(5000);
  384. request({
  385. method: 'GET',
  386. url: serverUrl + '/api/results/' + asyncRunId + '/javascriptExecutionTree',
  387. json: true,
  388. }, function(error, response, body) {
  389. if (!error && response.statusCode === 200) {
  390. body.should.have.a.property('data').that.is.an('object');
  391. body.data.should.have.a.property('type').that.equals('main');
  392. done();
  393. } else {
  394. done(error || response.statusCode);
  395. }
  396. });
  397. });
  398. it('should return the phantomas results', function(done) {
  399. this.timeout(5000);
  400. request({
  401. method: 'GET',
  402. url: serverUrl + '/api/results/' + asyncRunId + '/toolsResults/phantomas',
  403. json: true,
  404. }, function(error, response, body) {
  405. if (!error && response.statusCode === 200) {
  406. body.should.have.a.property('metrics').that.is.an('object');
  407. body.should.have.a.property('offenders').that.is.an('object');
  408. done();
  409. } else {
  410. done(error || response.statusCode);
  411. }
  412. });
  413. });
  414. });