CompileControllerTests.js 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  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 modulePath = require('path').join(__dirname, '../../../app/js/CompileController');
  10. const tk = require("timekeeper");
  11. describe("CompileController", function() {
  12. beforeEach(function() {
  13. this.CompileController = SandboxedModule.require(modulePath, { requires: {
  14. "./CompileManager": (this.CompileManager = {}),
  15. "./RequestParser": (this.RequestParser = {}),
  16. "settings-sharelatex": (this.Settings = {
  17. apis: {
  18. clsi: {
  19. url: "http://clsi.example.com"
  20. }
  21. }
  22. }),
  23. "./ProjectPersistenceManager": (this.ProjectPersistenceManager = {}),
  24. "logger-sharelatex": (this.logger = { log: sinon.stub(), error: sinon.stub(), err:sinon.stub(), warn: sinon.stub()})
  25. }
  26. });
  27. this.Settings.externalUrl = "http://www.example.com";
  28. this.req = {};
  29. this.res = {};
  30. return this.next = sinon.stub();
  31. });
  32. describe("compile", function() {
  33. beforeEach(function() {
  34. this.req.body = {
  35. compile: "mock-body"
  36. };
  37. this.req.params =
  38. {project_id: (this.project_id = "project-id-123")};
  39. this.request = {
  40. compile: "mock-parsed-request"
  41. };
  42. this.request_with_project_id = {
  43. compile: this.request.compile,
  44. project_id: this.project_id
  45. };
  46. this.output_files = [{
  47. path: "output.pdf",
  48. type: "pdf",
  49. build: 1234
  50. }, {
  51. path: "output.log",
  52. type: "log",
  53. build: 1234
  54. }];
  55. this.RequestParser.parse = sinon.stub().callsArgWith(1, null, this.request);
  56. this.ProjectPersistenceManager.markProjectAsJustAccessed = sinon.stub().callsArg(1);
  57. this.res.status = sinon.stub().returnsThis();
  58. return this.res.send = sinon.stub();
  59. });
  60. describe("successfully", function() {
  61. beforeEach(function() {
  62. this.CompileManager.doCompileWithLock = sinon.stub().callsArgWith(1, null, this.output_files);
  63. return this.CompileController.compile(this.req, this.res);
  64. });
  65. it("should parse the request", function() {
  66. return this.RequestParser.parse
  67. .calledWith(this.req.body)
  68. .should.equal(true);
  69. });
  70. it("should run the compile for the specified project", function() {
  71. return this.CompileManager.doCompileWithLock
  72. .calledWith(this.request_with_project_id)
  73. .should.equal(true);
  74. });
  75. it("should mark the project as accessed", function() {
  76. return this.ProjectPersistenceManager.markProjectAsJustAccessed
  77. .calledWith(this.project_id)
  78. .should.equal(true);
  79. });
  80. return it("should return the JSON response", function() {
  81. this.res.status.calledWith(200).should.equal(true);
  82. return this.res.send
  83. .calledWith({
  84. compile: {
  85. status: "success",
  86. error: null,
  87. outputFiles: this.output_files.map(file => {
  88. return {
  89. url: `${this.Settings.apis.clsi.url}/project/${this.project_id}/build/${file.build}/output/${file.path}`,
  90. path: file.path,
  91. type: file.type,
  92. build: file.build
  93. };
  94. })
  95. }
  96. })
  97. .should.equal(true);
  98. });
  99. });
  100. describe("with an error", function() {
  101. beforeEach(function() {
  102. this.CompileManager.doCompileWithLock = sinon.stub().callsArgWith(1, new Error(this.message = "error message"), null);
  103. return this.CompileController.compile(this.req, this.res);
  104. });
  105. return it("should return the JSON response with the error", function() {
  106. this.res.status.calledWith(500).should.equal(true);
  107. return this.res.send
  108. .calledWith({
  109. compile: {
  110. status: "error",
  111. error: this.message,
  112. outputFiles: []
  113. }
  114. })
  115. .should.equal(true);
  116. });
  117. });
  118. describe("when the request times out", function() {
  119. beforeEach(function() {
  120. this.error = new Error(this.message = "container timed out");
  121. this.error.timedout = true;
  122. this.CompileManager.doCompileWithLock = sinon.stub().callsArgWith(1, this.error, null);
  123. return this.CompileController.compile(this.req, this.res);
  124. });
  125. return it("should return the JSON response with the timeout status", function() {
  126. this.res.status.calledWith(200).should.equal(true);
  127. return this.res.send
  128. .calledWith({
  129. compile: {
  130. status: "timedout",
  131. error: this.message,
  132. outputFiles: []
  133. }
  134. })
  135. .should.equal(true);
  136. });
  137. });
  138. return describe("when the request returns no output files", function() {
  139. beforeEach(function() {
  140. this.CompileManager.doCompileWithLock = sinon.stub().callsArgWith(1, null, []);
  141. return this.CompileController.compile(this.req, this.res);
  142. });
  143. return it("should return the JSON response with the failure status", function() {
  144. this.res.status.calledWith(200).should.equal(true);
  145. return this.res.send
  146. .calledWith({
  147. compile: {
  148. error: null,
  149. status: "failure",
  150. outputFiles: []
  151. }
  152. })
  153. .should.equal(true);
  154. });
  155. });
  156. });
  157. describe("syncFromCode", function() {
  158. beforeEach(function() {
  159. this.file = "main.tex";
  160. this.line = 42;
  161. this.column = 5;
  162. this.project_id = "mock-project-id";
  163. this.req.params =
  164. {project_id: this.project_id};
  165. this.req.query = {
  166. file: this.file,
  167. line: this.line.toString(),
  168. column: this.column.toString()
  169. };
  170. this.res.json = sinon.stub();
  171. this.CompileManager.syncFromCode = sinon.stub().callsArgWith(5, null, (this.pdfPositions = ["mock-positions"]));
  172. return this.CompileController.syncFromCode(this.req, this.res, this.next);
  173. });
  174. it("should find the corresponding location in the PDF", function() {
  175. return this.CompileManager.syncFromCode
  176. .calledWith(this.project_id, undefined, this.file, this.line, this.column)
  177. .should.equal(true);
  178. });
  179. return it("should return the positions", function() {
  180. return this.res.json
  181. .calledWith({
  182. pdf: this.pdfPositions
  183. })
  184. .should.equal(true);
  185. });
  186. });
  187. describe("syncFromPdf", function() {
  188. beforeEach(function() {
  189. this.page = 5;
  190. this.h = 100.23;
  191. this.v = 45.67;
  192. this.project_id = "mock-project-id";
  193. this.req.params =
  194. {project_id: this.project_id};
  195. this.req.query = {
  196. page: this.page.toString(),
  197. h: this.h.toString(),
  198. v: this.v.toString()
  199. };
  200. this.res.json = sinon.stub();
  201. this.CompileManager.syncFromPdf = sinon.stub().callsArgWith(5, null, (this.codePositions = ["mock-positions"]));
  202. return this.CompileController.syncFromPdf(this.req, this.res, this.next);
  203. });
  204. it("should find the corresponding location in the code", function() {
  205. return this.CompileManager.syncFromPdf
  206. .calledWith(this.project_id, undefined, this.page, this.h, this.v)
  207. .should.equal(true);
  208. });
  209. return it("should return the positions", function() {
  210. return this.res.json
  211. .calledWith({
  212. code: this.codePositions
  213. })
  214. .should.equal(true);
  215. });
  216. });
  217. return describe("wordcount", function() {
  218. beforeEach(function() {
  219. this.file = "main.tex";
  220. this.project_id = "mock-project-id";
  221. this.req.params =
  222. {project_id: this.project_id};
  223. this.req.query = {
  224. file: this.file,
  225. image: (this.image = "example.com/image")
  226. };
  227. this.res.json = sinon.stub();
  228. this.CompileManager.wordcount = sinon.stub().callsArgWith(4, null, (this.texcount = ["mock-texcount"]));
  229. return this.CompileController.wordcount(this.req, this.res, this.next);
  230. });
  231. it("should return the word count of a file", function() {
  232. return this.CompileManager.wordcount
  233. .calledWith(this.project_id, undefined, this.file, this.image)
  234. .should.equal(true);
  235. });
  236. return it("should return the texcount info", function() {
  237. return this.res.json
  238. .calledWith({
  239. texcount: this.texcount
  240. })
  241. .should.equal(true);
  242. });
  243. });
  244. });