apiTest.js 14 KB

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