apiTest.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446
  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 launch a synchronous run', function(done) {
  36. this.timeout(15000);
  37. request({
  38. method: 'POST',
  39. url: serverUrl + '/api/runs',
  40. body: {
  41. url: wwwUrl + '/simple-page.html',
  42. waitForResponse: true
  43. },
  44. json: true,
  45. headers: {
  46. 'X-Api-Key': Object.keys(config.authorizedKeys)[0]
  47. }
  48. }, function(error, response, body) {
  49. if (!error && response.statusCode === 302) {
  50. response.headers.should.have.a.property('location').that.is.a('string');
  51. syncRunResultUrl = response.headers.location;
  52. done();
  53. } else {
  54. done(error || response.statusCode);
  55. }
  56. });
  57. });
  58. it('should retrieve the results for the synchronous run', function(done) {
  59. this.timeout(15000);
  60. request({
  61. method: 'GET',
  62. url: serverUrl + syncRunResultUrl,
  63. json: true,
  64. }, function(error, response, body) {
  65. if (!error && response.statusCode === 200) {
  66. body.should.have.a.property('runId').that.is.a('string');
  67. body.should.have.a.property('params').that.is.an('object');
  68. body.should.have.a.property('scoreProfiles').that.is.an('object');
  69. body.should.have.a.property('rules').that.is.an('object');
  70. body.should.have.a.property('toolsResults').that.is.an('object');
  71. body.should.have.a.property('javascriptExecutionTree').that.is.an('object');
  72. done();
  73. } else {
  74. done(error || response.statusCode);
  75. }
  76. });
  77. });
  78. it('should launch a run without waiting for the response', function(done) {
  79. this.timeout(5000);
  80. request({
  81. method: 'POST',
  82. url: serverUrl + '/api/runs',
  83. body: {
  84. url: wwwUrl + '/simple-page.html',
  85. waitForResponse: false
  86. },
  87. json: true,
  88. headers: {
  89. 'X-Api-Key': Object.keys(config.authorizedKeys)[0]
  90. }
  91. }, function(error, response, body) {
  92. if (!error && response.statusCode === 200) {
  93. asyncRunId = body.runId;
  94. asyncRunId.should.be.a('string');
  95. done();
  96. } else {
  97. done(error || response.statusCode);
  98. }
  99. });
  100. });
  101. it('should respond run status: running', function(done) {
  102. this.timeout(5000);
  103. request({
  104. method: 'GET',
  105. url: serverUrl + '/api/runs/' + asyncRunId,
  106. json: true,
  107. headers: {
  108. 'X-Api-Key': Object.keys(config.authorizedKeys)[0]
  109. }
  110. }, function(error, response, body) {
  111. if (!error && response.statusCode === 200) {
  112. body.runId.should.equal(asyncRunId);
  113. body.status.should.deep.equal({
  114. statusCode: 'running'
  115. });
  116. done();
  117. } else {
  118. done(error || response.statusCode);
  119. }
  120. });
  121. });
  122. it('should accept up to 10 anonymous runs to the API', function(done) {
  123. this.timeout(5000);
  124. function launchRun() {
  125. var deferred = Q.defer();
  126. request({
  127. method: 'POST',
  128. url: serverUrl + '/api/runs',
  129. body: {
  130. url: wwwUrl + '/simple-page.html',
  131. waitForResponse: false
  132. },
  133. json: true
  134. }, function(error, response, body) {
  135. lastRunId = body.runId;
  136. if (error) {
  137. deferred.reject(error);
  138. } else {
  139. deferred.resolve(response, body);
  140. }
  141. });
  142. return deferred.promise;
  143. }
  144. launchRun()
  145. .then(launchRun)
  146. .then(launchRun)
  147. .then(launchRun)
  148. .then(launchRun)
  149. .then(function(response, body) {
  150. // Here should still be ok
  151. response.statusCode.should.equal(200);
  152. launchRun()
  153. .then(launchRun)
  154. .then(launchRun)
  155. .then(launchRun)
  156. .then(launchRun)
  157. .then(launchRun)
  158. .then(function(response, body) {
  159. // It should fail now
  160. response.statusCode.should.equal(429);
  161. done();
  162. })
  163. .fail(function(error) {
  164. done(error);
  165. });
  166. }).fail(function(error) {
  167. done(error);
  168. });
  169. });
  170. it('should respond 404 to unknown runId', function(done) {
  171. this.timeout(5000);
  172. request({
  173. method: 'GET',
  174. url: serverUrl + '/api/runs/unknown',
  175. json: true
  176. }, function(error, response, body) {
  177. if (!error && response.statusCode === 404) {
  178. done();
  179. } else {
  180. done(error || response.statusCode);
  181. }
  182. });
  183. });
  184. it('should respond 404 to unknown result', function(done) {
  185. this.timeout(5000);
  186. request({
  187. method: 'GET',
  188. url: serverUrl + '/api/results/unknown',
  189. json: true
  190. }, function(error, response, body) {
  191. if (!error && response.statusCode === 404) {
  192. done();
  193. } else {
  194. done(error || response.statusCode);
  195. }
  196. });
  197. });
  198. it('should respond status complete to the first run', function(done) {
  199. this.timeout(12000);
  200. function checkStatus() {
  201. request({
  202. method: 'GET',
  203. url: serverUrl + '/api/runs/' + asyncRunId,
  204. json: true
  205. }, function(error, response, body) {
  206. if (!error && response.statusCode === 200) {
  207. body.runId.should.equal(asyncRunId);
  208. if (body.status.statusCode === 'running') {
  209. setTimeout(checkStatus, 250);
  210. } else if (body.status.statusCode === 'complete') {
  211. done();
  212. } else {
  213. done(body.status.statusCode);
  214. }
  215. } else {
  216. done(error || response.statusCode);
  217. }
  218. });
  219. }
  220. checkStatus();
  221. });
  222. it('should find the result of the async run', function(done) {
  223. this.timeout(5000);
  224. request({
  225. method: 'GET',
  226. url: serverUrl + '/api/results/' + asyncRunId,
  227. json: true,
  228. }, function(error, response, body) {
  229. if (!error && response.statusCode === 200) {
  230. body.should.have.a.property('runId').that.equals(asyncRunId);
  231. body.should.have.a.property('params').that.is.an('object');
  232. body.params.url.should.equal(wwwUrl + '/simple-page.html');
  233. body.should.have.a.property('scoreProfiles').that.is.an('object');
  234. body.scoreProfiles.should.have.a.property('generic').that.is.an('object');
  235. body.scoreProfiles.generic.should.have.a.property('globalScore').that.is.a('number');
  236. body.scoreProfiles.generic.should.have.a.property('categories').that.is.an('object');
  237. body.should.have.a.property('rules').that.is.an('object');
  238. body.should.have.a.property('toolsResults').that.is.an('object');
  239. body.toolsResults.should.have.a.property('phantomas').that.is.an('object');
  240. body.should.have.a.property('javascriptExecutionTree').that.is.an('object');
  241. body.javascriptExecutionTree.should.have.a.property('data').that.is.an('object');
  242. body.javascriptExecutionTree.data.should.have.a.property('type').that.equals('main');
  243. done();
  244. } else {
  245. done(error || response.statusCode);
  246. }
  247. });
  248. });
  249. it('should return the generic score object', function(done) {
  250. this.timeout(5000);
  251. request({
  252. method: 'GET',
  253. url: serverUrl + '/api/results/' + asyncRunId + '/generalScores',
  254. json: true,
  255. }, function(error, response, body) {
  256. if (!error && response.statusCode === 200) {
  257. body.should.have.a.property('globalScore').that.is.a('number');
  258. body.should.have.a.property('categories').that.is.an('object');
  259. done();
  260. } else {
  261. done(error || response.statusCode);
  262. }
  263. });
  264. });
  265. it('should return the generic score object also', function(done) {
  266. this.timeout(5000);
  267. request({
  268. method: 'GET',
  269. url: serverUrl + '/api/results/' + asyncRunId + '/generalScores/generic',
  270. json: true,
  271. }, function(error, response, body) {
  272. if (!error && response.statusCode === 200) {
  273. body.should.have.a.property('globalScore').that.is.a('number');
  274. body.should.have.a.property('categories').that.is.an('object');
  275. done();
  276. } else {
  277. done(error || response.statusCode);
  278. }
  279. });
  280. });
  281. it('should not find an unknown score object', function(done) {
  282. this.timeout(5000);
  283. request({
  284. method: 'GET',
  285. url: serverUrl + '/api/results/' + asyncRunId + '/generalScores/unknown',
  286. json: true,
  287. }, function(error, response, body) {
  288. if (!error && response.statusCode === 404) {
  289. done();
  290. } else {
  291. done(error || response.statusCode);
  292. }
  293. });
  294. });
  295. it('should return the rules', function(done) {
  296. this.timeout(5000);
  297. request({
  298. method: 'GET',
  299. url: serverUrl + '/api/results/' + asyncRunId + '/rules',
  300. json: true,
  301. }, function(error, response, body) {
  302. if (!error && response.statusCode === 200) {
  303. var firstRule = body[Object.keys(body)[0]];
  304. firstRule.should.have.a.property('policy').that.is.an('object');
  305. firstRule.should.have.a.property('value').that.is.a('number');
  306. firstRule.should.have.a.property('bad').that.is.a('boolean');
  307. firstRule.should.have.a.property('abnormal').that.is.a('boolean');
  308. firstRule.should.have.a.property('score').that.is.a('number');
  309. firstRule.should.have.a.property('abnormalityScore').that.is.a('number');
  310. done();
  311. } else {
  312. done(error || response.statusCode);
  313. }
  314. });
  315. });
  316. it('should return the javascript execution tree', function(done) {
  317. this.timeout(5000);
  318. request({
  319. method: 'GET',
  320. url: serverUrl + '/api/results/' + asyncRunId + '/javascriptExecutionTree',
  321. json: true,
  322. }, function(error, response, body) {
  323. if (!error && response.statusCode === 200) {
  324. body.should.have.a.property('data').that.is.an('object');
  325. body.data.should.have.a.property('type').that.equals('main');
  326. done();
  327. } else {
  328. done(error || response.statusCode);
  329. }
  330. });
  331. });
  332. it('should return the phantomas results', function(done) {
  333. this.timeout(5000);
  334. request({
  335. method: 'GET',
  336. url: serverUrl + '/api/results/' + asyncRunId + '/toolsResults/phantomas',
  337. json: true,
  338. }, function(error, response, body) {
  339. if (!error && response.statusCode === 200) {
  340. body.should.have.a.property('metrics').that.is.an('object');
  341. body.should.have.a.property('offenders').that.is.an('object');
  342. done();
  343. } else {
  344. done(error || response.statusCode);
  345. }
  346. });
  347. });
  348. });