DocumentControllerTests.js 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. const sinon = require('sinon')
  2. const SandboxedModule = require('sandboxed-module')
  3. const MockRequest = require('../helpers/MockRequest')
  4. const MockResponse = require('../helpers/MockResponse')
  5. const Errors = require('../../../../app/src/Features/Errors/Errors')
  6. const MODULE_PATH =
  7. '../../../../app/src/Features/Documents/DocumentController.js'
  8. describe('DocumentController', function () {
  9. beforeEach(function () {
  10. this.res = new MockResponse()
  11. this.req = new MockRequest()
  12. this.next = sinon.stub()
  13. this.doc = { _id: 'doc-id-123' }
  14. this.doc_lines = ['one', 'two', 'three']
  15. this.version = 42
  16. this.ranges = {
  17. comments: [
  18. {
  19. id: 'comment1',
  20. op: {
  21. c: 'foo',
  22. p: 123,
  23. t: 'comment1',
  24. },
  25. },
  26. {
  27. id: 'comment2',
  28. op: {
  29. c: 'bar',
  30. p: 456,
  31. t: 'comment2',
  32. },
  33. },
  34. ],
  35. }
  36. this.pathname = '/a/b/c/file.tex'
  37. this.lastUpdatedAt = new Date().getTime()
  38. this.lastUpdatedBy = 'fake-last-updater-id'
  39. this.rev = 5
  40. this.project = {
  41. _id: 'project-id-123',
  42. overleaf: {
  43. history: {
  44. id: 1234,
  45. display: true,
  46. },
  47. },
  48. }
  49. this.resolvedThreadIds = [
  50. 'comment2',
  51. 'comment4', // Comment in project but not in doc
  52. ]
  53. this.ProjectGetter = {
  54. promises: {
  55. getProject: sinon.stub().resolves(this.project),
  56. },
  57. }
  58. this.ProjectLocator = {
  59. promises: {
  60. findElement: sinon
  61. .stub()
  62. .resolves({ element: this.doc, path: { fileSystem: this.pathname } }),
  63. },
  64. }
  65. this.ProjectEntityHandler = {
  66. promises: {
  67. getDoc: sinon.stub().resolves({
  68. lines: this.doc_lines,
  69. rev: this.rev,
  70. version: this.version,
  71. ranges: this.ranges,
  72. }),
  73. },
  74. }
  75. this.ProjectEntityUpdateHandler = {
  76. promises: {
  77. updateDocLines: sinon.stub().resolves(),
  78. },
  79. }
  80. this.ChatApiHandler = {
  81. promises: {
  82. getResolvedThreadIds: sinon.stub().resolves(this.resolvedThreadIds),
  83. },
  84. }
  85. this.DocumentController = SandboxedModule.require(MODULE_PATH, {
  86. requires: {
  87. '../Project/ProjectGetter': this.ProjectGetter,
  88. '../Project/ProjectLocator': this.ProjectLocator,
  89. '../Project/ProjectEntityHandler': this.ProjectEntityHandler,
  90. '../Project/ProjectEntityUpdateHandler':
  91. this.ProjectEntityUpdateHandler,
  92. '../Chat/ChatApiHandler': this.ChatApiHandler,
  93. },
  94. })
  95. })
  96. describe('getDocument', function () {
  97. beforeEach(function () {
  98. this.req.params = {
  99. Project_id: this.project._id,
  100. doc_id: this.doc._id,
  101. }
  102. })
  103. describe('when project exists with project history enabled', function () {
  104. beforeEach(function (done) {
  105. this.res.callback = err => {
  106. done(err)
  107. }
  108. this.DocumentController.getDocument(this.req, this.res, this.next)
  109. })
  110. it('should return the history id and display setting to the client as JSON', function () {
  111. this.res.type.should.equal('application/json')
  112. JSON.parse(this.res.body).should.deep.equal({
  113. lines: this.doc_lines,
  114. version: this.version,
  115. ranges: this.ranges,
  116. pathname: this.pathname,
  117. projectHistoryId: this.project.overleaf.history.id,
  118. projectHistoryType: 'project-history',
  119. resolvedCommentIds: ['comment2'],
  120. historyRangesSupport: false,
  121. })
  122. })
  123. })
  124. describe('when the project does not exist', function () {
  125. beforeEach(function (done) {
  126. this.ProjectGetter.promises.getProject.resolves(null)
  127. this.res.callback = err => {
  128. done(err)
  129. }
  130. this.DocumentController.getDocument(this.req, this.res, this.next)
  131. })
  132. it('returns a 404', function () {
  133. this.res.statusCode.should.equal(404)
  134. })
  135. })
  136. })
  137. describe('setDocument', function () {
  138. beforeEach(function () {
  139. this.req.params = {
  140. Project_id: this.project._id,
  141. doc_id: this.doc._id,
  142. }
  143. })
  144. describe('when the document exists', function () {
  145. beforeEach(function (done) {
  146. this.req.body = {
  147. lines: this.doc_lines,
  148. version: this.version,
  149. ranges: this.ranges,
  150. lastUpdatedAt: this.lastUpdatedAt,
  151. lastUpdatedBy: this.lastUpdatedBy,
  152. }
  153. this.res.callback = err => {
  154. done(err)
  155. }
  156. this.DocumentController.setDocument(this.req, this.res, this.next)
  157. })
  158. it('should update the document in Mongo', function () {
  159. sinon.assert.calledWith(
  160. this.ProjectEntityUpdateHandler.promises.updateDocLines,
  161. this.project._id,
  162. this.doc._id,
  163. this.doc_lines,
  164. this.version,
  165. this.ranges,
  166. this.lastUpdatedAt,
  167. this.lastUpdatedBy
  168. )
  169. })
  170. it('should return a successful response', function () {
  171. this.res.success.should.equal(true)
  172. })
  173. })
  174. describe("when the document doesn't exist", function () {
  175. beforeEach(function (done) {
  176. this.ProjectEntityUpdateHandler.promises.updateDocLines.rejects(
  177. new Errors.NotFoundError('document does not exist')
  178. )
  179. this.req.body = { lines: this.doc_lines }
  180. this.next.callsFake(() => {
  181. done()
  182. })
  183. this.DocumentController.setDocument(this.req, this.res, this.next)
  184. })
  185. it('should call next with the NotFoundError', function () {
  186. this.next
  187. .calledWith(sinon.match.instanceOf(Errors.NotFoundError))
  188. .should.equal(true)
  189. })
  190. })
  191. })
  192. })