apiTest.js 13 KB

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