HistoryControllerTests.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362
  1. /* eslint-disable
  2. max-len,
  3. no-return-assign,
  4. */
  5. // TODO: This file was created by bulk-decaffeinate.
  6. // Fix any style issues and re-enable lint.
  7. /*
  8. * decaffeinate suggestions:
  9. * DS102: Remove unnecessary code created because of implicit returns
  10. * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
  11. */
  12. const sinon = require('sinon')
  13. const { expect } = require('chai')
  14. const Errors = require('../../../../app/src/Features/Errors/Errors')
  15. const modulePath = '../../../../app/src/Features/History/HistoryController'
  16. const SandboxedModule = require('sandboxed-module')
  17. describe('HistoryController', function () {
  18. beforeEach(function () {
  19. this.callback = sinon.stub()
  20. this.user_id = 'user-id-123'
  21. this.SessionManager = {
  22. getLoggedInUserId: sinon.stub().returns(this.user_id),
  23. }
  24. this.Stream = {
  25. pipeline: sinon.stub(),
  26. }
  27. this.HistoryController = SandboxedModule.require(modulePath, {
  28. requires: {
  29. request: (this.request = sinon.stub()),
  30. '@overleaf/settings': (this.settings = {}),
  31. stream: this.Stream,
  32. '../Authentication/SessionManager': this.SessionManager,
  33. './HistoryManager': (this.HistoryManager = {}),
  34. '../Project/ProjectDetailsHandler': (this.ProjectDetailsHandler = {}),
  35. '../Project/ProjectEntityUpdateHandler':
  36. (this.ProjectEntityUpdateHandler = {}),
  37. '../User/UserGetter': (this.UserGetter = {}),
  38. './RestoreManager': (this.RestoreManager = {}),
  39. '../../infrastructure/Features': (this.Features = sinon
  40. .stub()
  41. .withArgs('saas')
  42. .returns(true)),
  43. },
  44. })
  45. return (this.settings.apis = {
  46. trackchanges: {
  47. enabled: false,
  48. url: 'http://trackchanges.example.com',
  49. },
  50. project_history: {
  51. url: 'http://project_history.example.com',
  52. },
  53. })
  54. })
  55. describe('selectHistoryApi', function () {
  56. beforeEach(function () {
  57. this.req = { url: '/mock/url', method: 'POST', params: {} }
  58. this.res = 'mock-res'
  59. return (this.next = sinon.stub())
  60. })
  61. describe('for a project with project history', function () {
  62. beforeEach(function () {
  63. this.ProjectDetailsHandler.getDetails = sinon
  64. .stub()
  65. .callsArgWith(1, null, {
  66. overleaf: { history: { id: 42, display: true } },
  67. })
  68. return this.HistoryController.selectHistoryApi(
  69. this.req,
  70. this.res,
  71. this.next
  72. )
  73. })
  74. it('should set the flag for project history to true', function () {
  75. return this.req.useProjectHistory.should.equal(true)
  76. })
  77. })
  78. describe('for any other project ', function () {
  79. beforeEach(function () {
  80. this.ProjectDetailsHandler.getDetails = sinon
  81. .stub()
  82. .callsArgWith(1, null, {})
  83. return this.HistoryController.selectHistoryApi(
  84. this.req,
  85. this.res,
  86. this.next
  87. )
  88. })
  89. it('should not set the flag for project history to false', function () {
  90. return this.req.useProjectHistory.should.equal(false)
  91. })
  92. })
  93. })
  94. describe('proxyToHistoryApi', function () {
  95. beforeEach(function () {
  96. this.req = { url: '/mock/url', method: 'POST' }
  97. this.res = 'mock-res'
  98. this.next = sinon.stub()
  99. this.proxy = sinon.stub()
  100. this.request.returns(this.proxy)
  101. })
  102. describe('for a project with the project history flag', function () {
  103. beforeEach(function () {
  104. this.req.useProjectHistory = true
  105. return this.HistoryController.proxyToHistoryApi(
  106. this.req,
  107. this.res,
  108. this.next
  109. )
  110. })
  111. it('should get the user id', function () {
  112. return this.SessionManager.getLoggedInUserId
  113. .calledWith(this.req.session)
  114. .should.equal(true)
  115. })
  116. it('should call the project history api', function () {
  117. return this.request
  118. .calledWith({
  119. url: `${this.settings.apis.project_history.url}${this.req.url}`,
  120. method: this.req.method,
  121. headers: {
  122. 'X-User-Id': this.user_id,
  123. },
  124. })
  125. .should.equal(true)
  126. })
  127. it('should pipe the response to the client', function () {
  128. expect(this.Stream.pipeline).to.have.been.calledWith(
  129. this.proxy,
  130. this.res
  131. )
  132. })
  133. })
  134. describe('for a project without the project history flag', function () {
  135. beforeEach(function () {
  136. this.req.useProjectHistory = false
  137. return this.HistoryController.proxyToHistoryApi(
  138. this.req,
  139. this.res,
  140. this.next
  141. )
  142. })
  143. it('should get the user id', function () {
  144. return this.SessionManager.getLoggedInUserId
  145. .calledWith(this.req.session)
  146. .should.equal(true)
  147. })
  148. it('should call the track changes api', function () {
  149. return this.request
  150. .calledWith({
  151. url: `${this.settings.apis.trackchanges.url}${this.req.url}`,
  152. method: this.req.method,
  153. headers: {
  154. 'X-User-Id': this.user_id,
  155. },
  156. })
  157. .should.equal(true)
  158. })
  159. it('should pipe the response to the client', function () {
  160. expect(this.Stream.pipeline).to.have.been.calledWith(
  161. this.proxy,
  162. this.res
  163. )
  164. })
  165. })
  166. })
  167. describe('proxyToHistoryApiAndInjectUserDetails', function () {
  168. beforeEach(function () {
  169. this.req = { url: '/mock/url', method: 'POST' }
  170. this.res = { json: sinon.stub() }
  171. this.next = sinon.stub()
  172. this.request.yields(null, { statusCode: 200 }, (this.data = 'mock-data'))
  173. return (this.HistoryManager.injectUserDetails = sinon
  174. .stub()
  175. .yields(null, (this.data_with_users = 'mock-injected-data')))
  176. })
  177. describe('for a project with the project history flag', function () {
  178. beforeEach(function () {
  179. this.req.useProjectHistory = true
  180. return this.HistoryController.proxyToHistoryApiAndInjectUserDetails(
  181. this.req,
  182. this.res,
  183. this.next
  184. )
  185. })
  186. it('should get the user id', function () {
  187. return this.SessionManager.getLoggedInUserId
  188. .calledWith(this.req.session)
  189. .should.equal(true)
  190. })
  191. it('should call the project history api', function () {
  192. return this.request
  193. .calledWith({
  194. url: `${this.settings.apis.project_history.url}${this.req.url}`,
  195. method: this.req.method,
  196. json: true,
  197. headers: {
  198. 'X-User-Id': this.user_id,
  199. },
  200. })
  201. .should.equal(true)
  202. })
  203. it('should inject the user data', function () {
  204. return this.HistoryManager.injectUserDetails
  205. .calledWith(this.data)
  206. .should.equal(true)
  207. })
  208. it('should return the data with users to the client', function () {
  209. return this.res.json.calledWith(this.data_with_users).should.equal(true)
  210. })
  211. })
  212. describe('for a project without the project history flag', function () {
  213. beforeEach(function () {
  214. this.req.useProjectHistory = false
  215. return this.HistoryController.proxyToHistoryApiAndInjectUserDetails(
  216. this.req,
  217. this.res,
  218. this.next
  219. )
  220. })
  221. it('should get the user id', function () {
  222. return this.SessionManager.getLoggedInUserId
  223. .calledWith(this.req.session)
  224. .should.equal(true)
  225. })
  226. it('should call the track changes api', function () {
  227. return this.request
  228. .calledWith({
  229. url: `${this.settings.apis.trackchanges.url}${this.req.url}`,
  230. method: this.req.method,
  231. json: true,
  232. headers: {
  233. 'X-User-Id': this.user_id,
  234. },
  235. })
  236. .should.equal(true)
  237. })
  238. it('should inject the user data', function () {
  239. return this.HistoryManager.injectUserDetails
  240. .calledWith(this.data)
  241. .should.equal(true)
  242. })
  243. it('should return the data with users to the client', function () {
  244. return this.res.json.calledWith(this.data_with_users).should.equal(true)
  245. })
  246. })
  247. })
  248. describe('proxyToHistoryApiAndInjectUserDetails (with the history API failing)', function () {
  249. beforeEach(function () {
  250. this.req = { url: '/mock/url', method: 'POST', useProjectHistory: true }
  251. this.res = { json: sinon.stub() }
  252. this.next = sinon.stub()
  253. this.request.yields(null, { statusCode: 500 }, (this.data = 'mock-data'))
  254. this.HistoryManager.injectUserDetails = sinon
  255. .stub()
  256. .yields(null, (this.data_with_users = 'mock-injected-data'))
  257. return this.HistoryController.proxyToHistoryApiAndInjectUserDetails(
  258. this.req,
  259. this.res,
  260. this.next
  261. )
  262. })
  263. it('should not inject the user data', function () {
  264. return this.HistoryManager.injectUserDetails
  265. .calledWith(this.data)
  266. .should.equal(false)
  267. })
  268. it('should not return the data with users to the client', function () {
  269. return this.res.json.calledWith(this.data_with_users).should.equal(false)
  270. })
  271. })
  272. describe('resyncProjectHistory', function () {
  273. describe('for a project without project-history enabled', function () {
  274. beforeEach(function () {
  275. this.project_id = 'mock-project-id'
  276. this.req = { params: { Project_id: this.project_id } }
  277. this.res = { setTimeout: sinon.stub(), sendStatus: sinon.stub() }
  278. this.next = sinon.stub()
  279. this.error = new Errors.ProjectHistoryDisabledError()
  280. this.ProjectEntityUpdateHandler.resyncProjectHistory = sinon
  281. .stub()
  282. .yields(this.error)
  283. return this.HistoryController.resyncProjectHistory(
  284. this.req,
  285. this.res,
  286. this.next
  287. )
  288. })
  289. it('response with a 404', function () {
  290. return this.res.sendStatus.calledWith(404).should.equal(true)
  291. })
  292. })
  293. describe('for a project with project-history enabled', function () {
  294. beforeEach(function () {
  295. this.project_id = 'mock-project-id'
  296. this.req = { params: { Project_id: this.project_id } }
  297. this.res = { setTimeout: sinon.stub(), sendStatus: sinon.stub() }
  298. this.next = sinon.stub()
  299. this.ProjectEntityUpdateHandler.resyncProjectHistory = sinon
  300. .stub()
  301. .yields()
  302. return this.HistoryController.resyncProjectHistory(
  303. this.req,
  304. this.res,
  305. this.next
  306. )
  307. })
  308. it('sets an extended response timeout', function () {
  309. this.res.setTimeout.should.have.been.calledWith(6 * 60 * 1000)
  310. })
  311. it('resyncs the project', function () {
  312. return this.ProjectEntityUpdateHandler.resyncProjectHistory
  313. .calledWith(this.project_id)
  314. .should.equal(true)
  315. })
  316. it('responds with a 204', function () {
  317. return this.res.sendStatus.calledWith(204).should.equal(true)
  318. })
  319. })
  320. })
  321. })