DocumentControllerTests.js 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  1. /* eslint-disable
  2. max-len,
  3. no-return-assign,
  4. no-unused-vars,
  5. */
  6. // TODO: This file was created by bulk-decaffeinate.
  7. // Fix any style issues and re-enable lint.
  8. /*
  9. * decaffeinate suggestions:
  10. * DS102: Remove unnecessary code created because of implicit returns
  11. * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
  12. */
  13. const sinon = require('sinon')
  14. const { expect } = require('chai')
  15. const modulePath =
  16. '../../../../app/src/Features/Documents/DocumentController.js'
  17. const SandboxedModule = require('sandboxed-module')
  18. const events = require('events')
  19. const MockRequest = require('../helpers/MockRequest')
  20. const MockResponse = require('../helpers/MockResponse')
  21. const Errors = require('../../../../app/src/Features/Errors/Errors')
  22. describe('DocumentController', function () {
  23. beforeEach(function () {
  24. this.DocumentController = SandboxedModule.require(modulePath, {
  25. requires: {
  26. '../Project/ProjectGetter': (this.ProjectGetter = {}),
  27. '../Project/ProjectLocator': (this.ProjectLocator = {}),
  28. '../Project/ProjectEntityHandler': (this.ProjectEntityHandler = {}),
  29. '../Project/ProjectEntityUpdateHandler': (this.ProjectEntityUpdateHandler = {}),
  30. },
  31. })
  32. this.res = new MockResponse()
  33. this.req = new MockRequest()
  34. this.next = sinon.stub()
  35. this.project_id = 'project-id-123'
  36. this.doc_id = 'doc-id-123'
  37. this.doc_lines = ['one', 'two', 'three']
  38. this.version = 42
  39. this.ranges = { mock: 'ranges' }
  40. this.pathname = '/a/b/c/file.tex'
  41. this.lastUpdatedAt = new Date().getTime()
  42. this.lastUpdatedBy = 'fake-last-updater-id'
  43. return (this.rev = 5)
  44. })
  45. describe('getDocument', function () {
  46. beforeEach(function () {
  47. return (this.req.params = {
  48. Project_id: this.project_id,
  49. doc_id: this.doc_id,
  50. })
  51. })
  52. describe('when the project exists without project history enabled', function () {
  53. beforeEach(function () {
  54. this.project = { _id: this.project_id }
  55. return (this.ProjectGetter.getProject = sinon
  56. .stub()
  57. .callsArgWith(2, null, this.project))
  58. })
  59. describe('when the document exists', function () {
  60. beforeEach(function () {
  61. this.doc = { _id: this.doc_id }
  62. this.ProjectLocator.findElement = sinon
  63. .stub()
  64. .callsArgWith(1, null, this.doc, { fileSystem: this.pathname })
  65. this.ProjectEntityHandler.getDoc = sinon
  66. .stub()
  67. .callsArgWith(
  68. 2,
  69. null,
  70. this.doc_lines,
  71. this.rev,
  72. this.version,
  73. this.ranges
  74. )
  75. return this.DocumentController.getDocument(
  76. this.req,
  77. this.res,
  78. this.next
  79. )
  80. })
  81. it('should get the project', function () {
  82. return this.ProjectGetter.getProject
  83. .calledWith(this.project_id, { rootFolder: true, overleaf: true })
  84. .should.equal(true)
  85. })
  86. it('should get the pathname of the document', function () {
  87. return this.ProjectLocator.findElement
  88. .calledWith({
  89. project: this.project,
  90. element_id: this.doc_id,
  91. type: 'doc',
  92. })
  93. .should.equal(true)
  94. })
  95. it('should get the document content', function () {
  96. return this.ProjectEntityHandler.getDoc
  97. .calledWith(this.project_id, this.doc_id)
  98. .should.equal(true)
  99. })
  100. it('should return the document data to the client as JSON', function () {
  101. this.res.type.should.equal('application/json')
  102. return this.res.body.should.equal(
  103. JSON.stringify({
  104. lines: this.doc_lines,
  105. version: this.version,
  106. ranges: this.ranges,
  107. pathname: this.pathname,
  108. })
  109. )
  110. })
  111. })
  112. describe("when the document doesn't exist", function () {
  113. beforeEach(function () {
  114. this.ProjectLocator.findElement = sinon
  115. .stub()
  116. .callsArgWith(1, new Errors.NotFoundError('not found'))
  117. return this.DocumentController.getDocument(
  118. this.req,
  119. this.res,
  120. this.next
  121. )
  122. })
  123. it('should call next with the NotFoundError', function () {
  124. return this.next
  125. .calledWith(sinon.match.instanceOf(Errors.NotFoundError))
  126. .should.equal(true)
  127. })
  128. })
  129. })
  130. describe('when project exists with project history enabled', function () {
  131. beforeEach(function () {
  132. this.doc = { _id: this.doc_id }
  133. this.projectHistoryId = 1234
  134. this.projectHistoryDisplay = true
  135. this.projectHistoryType = 'project-history'
  136. this.project = {
  137. _id: this.project_id,
  138. overleaf: {
  139. history: {
  140. id: this.projectHistoryId,
  141. display: this.projectHistoryDisplay,
  142. },
  143. },
  144. }
  145. this.ProjectGetter.getProject = sinon
  146. .stub()
  147. .callsArgWith(2, null, this.project)
  148. this.ProjectLocator.findElement = sinon
  149. .stub()
  150. .callsArgWith(1, null, this.doc, { fileSystem: this.pathname })
  151. this.ProjectEntityHandler.getDoc = sinon
  152. .stub()
  153. .callsArgWith(
  154. 2,
  155. null,
  156. this.doc_lines,
  157. this.rev,
  158. this.version,
  159. this.ranges
  160. )
  161. return this.DocumentController.getDocument(
  162. this.req,
  163. this.res,
  164. this.next
  165. )
  166. })
  167. it('should return the history id and display setting to the client as JSON', function () {
  168. this.res.type.should.equal('application/json')
  169. return this.res.body.should.equal(
  170. JSON.stringify({
  171. lines: this.doc_lines,
  172. version: this.version,
  173. ranges: this.ranges,
  174. pathname: this.pathname,
  175. projectHistoryId: this.projectHistoryId,
  176. projectHistoryType: this.projectHistoryType,
  177. })
  178. )
  179. })
  180. })
  181. describe('when project exists that was migrated with downgrades allowed', function () {
  182. beforeEach(function () {
  183. this.doc = { _id: this.doc_id }
  184. this.projectHistoryId = 1234
  185. this.projectHistoryDisplay = true
  186. this.projectHistoryType = undefined
  187. this.project = {
  188. _id: this.project_id,
  189. overleaf: {
  190. history: {
  191. id: this.projectHistoryId,
  192. display: this.projectHistoryDisplay,
  193. allowDowngrade: true,
  194. },
  195. },
  196. }
  197. this.ProjectGetter.getProject = sinon
  198. .stub()
  199. .callsArgWith(2, null, this.project)
  200. this.ProjectLocator.findElement = sinon
  201. .stub()
  202. .callsArgWith(1, null, this.doc, { fileSystem: this.pathname })
  203. this.ProjectEntityHandler.getDoc = sinon
  204. .stub()
  205. .callsArgWith(
  206. 2,
  207. null,
  208. this.doc_lines,
  209. this.rev,
  210. this.version,
  211. this.ranges
  212. )
  213. return this.DocumentController.getDocument(
  214. this.req,
  215. this.res,
  216. this.next
  217. )
  218. })
  219. it('should return the history id in the JSON but not history type, sending history to both services', function () {
  220. this.res.type.should.equal('application/json')
  221. return this.res.body.should.equal(
  222. JSON.stringify({
  223. lines: this.doc_lines,
  224. version: this.version,
  225. ranges: this.ranges,
  226. pathname: this.pathname,
  227. projectHistoryId: this.projectHistoryId,
  228. projectHistoryType: this.projectHistoryType,
  229. })
  230. )
  231. })
  232. })
  233. describe('when the project does not exist', function () {
  234. beforeEach(function () {
  235. this.ProjectGetter.getProject = sinon.stub().callsArgWith(2, null, null)
  236. return this.DocumentController.getDocument(
  237. this.req,
  238. this.res,
  239. this.next
  240. )
  241. })
  242. it('returns a 404', function () {
  243. return this.res.statusCode.should.equal(404)
  244. })
  245. })
  246. })
  247. describe('setDocument', function () {
  248. beforeEach(function () {
  249. return (this.req.params = {
  250. Project_id: this.project_id,
  251. doc_id: this.doc_id,
  252. })
  253. })
  254. describe('when the document exists', function () {
  255. beforeEach(function () {
  256. this.ProjectEntityUpdateHandler.updateDocLines = sinon.stub().yields()
  257. this.req.body = {
  258. lines: this.doc_lines,
  259. version: this.version,
  260. ranges: this.ranges,
  261. lastUpdatedAt: this.lastUpdatedAt,
  262. lastUpdatedBy: this.lastUpdatedBy,
  263. }
  264. return this.DocumentController.setDocument(
  265. this.req,
  266. this.res,
  267. this.next
  268. )
  269. })
  270. it('should update the document in Mongo', function () {
  271. return sinon.assert.calledWith(
  272. this.ProjectEntityUpdateHandler.updateDocLines,
  273. this.project_id,
  274. this.doc_id,
  275. this.doc_lines,
  276. this.version,
  277. this.ranges,
  278. this.lastUpdatedAt,
  279. this.lastUpdatedBy
  280. )
  281. })
  282. it('should return a successful response', function () {
  283. return this.res.success.should.equal(true)
  284. })
  285. })
  286. describe("when the document doesn't exist", function () {
  287. beforeEach(function () {
  288. this.ProjectEntityUpdateHandler.updateDocLines = sinon
  289. .stub()
  290. .yields(new Errors.NotFoundError('document does not exist'))
  291. this.req.body = { lines: this.doc_lines }
  292. return this.DocumentController.setDocument(
  293. this.req,
  294. this.res,
  295. this.next
  296. )
  297. })
  298. it('should call next with the NotFoundError', function () {
  299. return this.next
  300. .calledWith(sinon.match.instanceOf(Errors.NotFoundError))
  301. .should.equal(true)
  302. })
  303. })
  304. })
  305. })