RequestParserTests.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380
  1. /*
  2. * decaffeinate suggestions:
  3. * DS102: Remove unnecessary code created because of implicit returns
  4. * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
  5. */
  6. const SandboxedModule = require('sandboxed-module');
  7. const sinon = require('sinon');
  8. require('chai').should();
  9. const { expect } = require('chai');
  10. const modulePath = require('path').join(__dirname, '../../../app/js/RequestParser');
  11. const tk = require("timekeeper");
  12. describe("RequestParser", function() {
  13. beforeEach(function() {
  14. tk.freeze();
  15. this.callback = sinon.stub();
  16. this.validResource = {
  17. path: "main.tex",
  18. date: "12:00 01/02/03",
  19. content: "Hello world"
  20. };
  21. this.validRequest = {
  22. compile: {
  23. token: "token-123",
  24. options: {
  25. imageName: "basicImageName/here:2017-1",
  26. compiler: "pdflatex",
  27. timeout: 42
  28. },
  29. resources: []
  30. }
  31. };
  32. return this.RequestParser = SandboxedModule.require(modulePath, { requires: {
  33. "settings-sharelatex": (this.settings = {})
  34. }
  35. });});
  36. afterEach(() => tk.reset());
  37. describe("without a top level object", function() {
  38. beforeEach(function() {
  39. return this.RequestParser.parse([], this.callback);
  40. });
  41. return it("should return an error", function() {
  42. return this.callback.calledWith("top level object should have a compile attribute")
  43. .should.equal(true);
  44. });
  45. });
  46. describe("without a compile attribute", function() {
  47. beforeEach(function() {
  48. return this.RequestParser.parse({}, this.callback);
  49. });
  50. return it("should return an error", function() {
  51. return this.callback.calledWith("top level object should have a compile attribute")
  52. .should.equal(true);
  53. });
  54. });
  55. describe("without a valid compiler", function() {
  56. beforeEach(function() {
  57. this.validRequest.compile.options.compiler = "not-a-compiler";
  58. return this.RequestParser.parse(this.validRequest, this.callback);
  59. });
  60. return it("should return an error", function() {
  61. return this.callback.calledWith("compiler attribute should be one of: pdflatex, latex, xelatex, lualatex")
  62. .should.equal(true);
  63. });
  64. });
  65. describe("without a compiler specified", function() {
  66. beforeEach(function() {
  67. delete this.validRequest.compile.options.compiler;
  68. return this.RequestParser.parse(this.validRequest, (error, data) => {
  69. this.data = data;
  70. });
  71. });
  72. return it("should set the compiler to pdflatex by default", function() {
  73. return this.data.compiler.should.equal("pdflatex");
  74. });
  75. });
  76. describe("with imageName set", function() {
  77. beforeEach(function() {
  78. return this.RequestParser.parse(this.validRequest, (error, data) => {
  79. this.data = data;
  80. });
  81. });
  82. return it("should set the imageName", function() {
  83. return this.data.imageName.should.equal("basicImageName/here:2017-1");
  84. });
  85. });
  86. describe("with flags set", function() {
  87. beforeEach(function() {
  88. this.validRequest.compile.options.flags = ["-file-line-error"];
  89. return this.RequestParser.parse(this.validRequest, (error, data) => {
  90. this.data = data;
  91. });
  92. });
  93. return it("should set the flags attribute", function() {
  94. return expect(this.data.flags).to.deep.equal(["-file-line-error"]);
  95. });
  96. });
  97. describe("with flags not specified", function() {
  98. beforeEach(function() {
  99. return this.RequestParser.parse(this.validRequest, (error, data) => {
  100. this.data = data;
  101. });
  102. });
  103. return it("it should have an empty flags list", function() {
  104. return expect(this.data.flags).to.deep.equal([]);
  105. });
  106. });
  107. describe("without a timeout specified", function() {
  108. beforeEach(function() {
  109. delete this.validRequest.compile.options.timeout;
  110. return this.RequestParser.parse(this.validRequest, (error, data) => {
  111. this.data = data;
  112. });
  113. });
  114. return it("should set the timeout to MAX_TIMEOUT", function() {
  115. return this.data.timeout.should.equal(this.RequestParser.MAX_TIMEOUT * 1000);
  116. });
  117. });
  118. describe("with a timeout larger than the maximum", function() {
  119. beforeEach(function() {
  120. this.validRequest.compile.options.timeout = this.RequestParser.MAX_TIMEOUT + 1;
  121. return this.RequestParser.parse(this.validRequest, (error, data) => {
  122. this.data = data;
  123. });
  124. });
  125. return it("should set the timeout to MAX_TIMEOUT", function() {
  126. return this.data.timeout.should.equal(this.RequestParser.MAX_TIMEOUT * 1000);
  127. });
  128. });
  129. describe("with a timeout", function() {
  130. beforeEach(function() {
  131. return this.RequestParser.parse(this.validRequest, (error, data) => {
  132. this.data = data;
  133. });
  134. });
  135. return it("should set the timeout (in milliseconds)", function() {
  136. return this.data.timeout.should.equal(this.validRequest.compile.options.timeout * 1000);
  137. });
  138. });
  139. describe("with a resource without a path", function() {
  140. beforeEach(function() {
  141. delete this.validResource.path;
  142. this.validRequest.compile.resources.push(this.validResource);
  143. return this.RequestParser.parse(this.validRequest, this.callback);
  144. });
  145. return it("should return an error", function() {
  146. return this.callback.calledWith("all resources should have a path attribute")
  147. .should.equal(true);
  148. });
  149. });
  150. describe("with a resource with a path", function() {
  151. beforeEach(function() {
  152. this.validResource.path = (this.path = "test.tex");
  153. this.validRequest.compile.resources.push(this.validResource);
  154. this.RequestParser.parse(this.validRequest, this.callback);
  155. return this.data = this.callback.args[0][1];});
  156. return it("should return the path in the parsed response", function() {
  157. return this.data.resources[0].path.should.equal(this.path);
  158. });
  159. });
  160. describe("with a resource with a malformed modified date", function() {
  161. beforeEach(function() {
  162. this.validResource.modified = "not-a-date";
  163. this.validRequest.compile.resources.push(this.validResource);
  164. return this.RequestParser.parse(this.validRequest, this.callback);
  165. });
  166. return it("should return an error", function() {
  167. return this.callback
  168. .calledWith(
  169. "resource modified date could not be understood: "+
  170. this.validResource.modified
  171. )
  172. .should.equal(true);
  173. });
  174. });
  175. describe("with a resource with a valid date", function() {
  176. beforeEach(function() {
  177. this.date = "12:00 01/02/03";
  178. this.validResource.modified = this.date;
  179. this.validRequest.compile.resources.push(this.validResource);
  180. this.RequestParser.parse(this.validRequest, this.callback);
  181. return this.data = this.callback.args[0][1];});
  182. return it("should return the date as a Javascript Date object", function() {
  183. (this.data.resources[0].modified instanceof Date).should.equal(true);
  184. return this.data.resources[0].modified.getTime().should.equal(Date.parse(this.date));
  185. });
  186. });
  187. describe("with a resource without either a content or URL attribute", function() {
  188. beforeEach(function() {
  189. delete this.validResource.url;
  190. delete this.validResource.content;
  191. this.validRequest.compile.resources.push(this.validResource);
  192. return this.RequestParser.parse(this.validRequest, this.callback);
  193. });
  194. return it("should return an error", function() {
  195. return this.callback.calledWith("all resources should have either a url or content attribute")
  196. .should.equal(true);
  197. });
  198. });
  199. describe("with a resource where the content is not a string", function() {
  200. beforeEach(function() {
  201. this.validResource.content = [];
  202. this.validRequest.compile.resources.push(this.validResource);
  203. return this.RequestParser.parse((this.validRequest), this.callback);
  204. });
  205. return it("should return an error", function() {
  206. return this.callback.calledWith("content attribute should be a string")
  207. .should.equal(true);
  208. });
  209. });
  210. describe("with a resource where the url is not a string", function() {
  211. beforeEach(function() {
  212. this.validResource.url = [];
  213. this.validRequest.compile.resources.push(this.validResource);
  214. return this.RequestParser.parse((this.validRequest), this.callback);
  215. });
  216. return it("should return an error", function() {
  217. return this.callback.calledWith("url attribute should be a string")
  218. .should.equal(true);
  219. });
  220. });
  221. describe("with a resource with a url", function() {
  222. beforeEach(function() {
  223. this.validResource.url = (this.url = "www.example.com");
  224. this.validRequest.compile.resources.push(this.validResource);
  225. this.RequestParser.parse((this.validRequest), this.callback);
  226. return this.data = this.callback.args[0][1];});
  227. return it("should return the url in the parsed response", function() {
  228. return this.data.resources[0].url.should.equal(this.url);
  229. });
  230. });
  231. describe("with a resource with a content attribute", function() {
  232. beforeEach(function() {
  233. this.validResource.content = (this.content = "Hello world");
  234. this.validRequest.compile.resources.push(this.validResource);
  235. this.RequestParser.parse((this.validRequest), this.callback);
  236. return this.data = this.callback.args[0][1];});
  237. return it("should return the content in the parsed response", function() {
  238. return this.data.resources[0].content.should.equal(this.content);
  239. });
  240. });
  241. describe("without a root resource path", function() {
  242. beforeEach(function() {
  243. delete this.validRequest.compile.rootResourcePath;
  244. this.RequestParser.parse((this.validRequest), this.callback);
  245. return this.data = this.callback.args[0][1];});
  246. return it("should set the root resource path to 'main.tex' by default", function() {
  247. return this.data.rootResourcePath.should.equal("main.tex");
  248. });
  249. });
  250. describe("with a root resource path", function() {
  251. beforeEach(function() {
  252. this.validRequest.compile.rootResourcePath = (this.path = "test.tex");
  253. this.RequestParser.parse((this.validRequest), this.callback);
  254. return this.data = this.callback.args[0][1];});
  255. return it("should return the root resource path in the parsed response", function() {
  256. return this.data.rootResourcePath.should.equal(this.path);
  257. });
  258. });
  259. describe("with a root resource path that is not a string", function() {
  260. beforeEach(function() {
  261. this.validRequest.compile.rootResourcePath = [];
  262. return this.RequestParser.parse((this.validRequest), this.callback);
  263. });
  264. return it("should return an error", function() {
  265. return this.callback.calledWith("rootResourcePath attribute should be a string")
  266. .should.equal(true);
  267. });
  268. });
  269. describe("with a root resource path that needs escaping", function() {
  270. beforeEach(function() {
  271. this.badPath = "`rm -rf foo`.tex";
  272. this.goodPath = "rm -rf foo.tex";
  273. this.validRequest.compile.rootResourcePath = this.badPath;
  274. this.validRequest.compile.resources.push({
  275. path: this.badPath,
  276. date: "12:00 01/02/03",
  277. content: "Hello world"
  278. });
  279. this.RequestParser.parse(this.validRequest, this.callback);
  280. return this.data = this.callback.args[0][1];});
  281. it("should return the escaped resource", function() {
  282. return this.data.rootResourcePath.should.equal(this.goodPath);
  283. });
  284. return it("should also escape the resource path", function() {
  285. return this.data.resources[0].path.should.equal(this.goodPath);
  286. });
  287. });
  288. describe("with a root resource path that has a relative path", function() {
  289. beforeEach(function() {
  290. this.validRequest.compile.rootResourcePath = "foo/../../bar.tex";
  291. this.RequestParser.parse(this.validRequest, this.callback);
  292. return this.data = this.callback.args[0][1];});
  293. return it("should return an error", function() {
  294. return this.callback.calledWith("relative path in root resource")
  295. .should.equal(true);
  296. });
  297. });
  298. describe("with a root resource path that has unescaped + relative path", function() {
  299. beforeEach(function() {
  300. this.validRequest.compile.rootResourcePath = "foo/#../bar.tex";
  301. this.RequestParser.parse(this.validRequest, this.callback);
  302. return this.data = this.callback.args[0][1];});
  303. return it("should return an error", function() {
  304. return this.callback.calledWith("relative path in root resource")
  305. .should.equal(true);
  306. });
  307. });
  308. return describe("with an unknown syncType", function() {
  309. beforeEach(function() {
  310. this.validRequest.compile.options.syncType = "unexpected";
  311. this.RequestParser.parse(this.validRequest, this.callback);
  312. return this.data = this.callback.args[0][1];});
  313. return it("should return an error", function() {
  314. return this.callback.calledWith("syncType attribute should be one of: full, incremental")
  315. .should.equal(true);
  316. });
  317. });
  318. });