ReferencesHandlerTests.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570
  1. /* eslint-disable
  2. node/handle-callback-err,
  3. max-len,
  4. mocha/no-identical-title,
  5. no-return-assign,
  6. no-unused-vars,
  7. */
  8. // TODO: This file was created by bulk-decaffeinate.
  9. // Fix any style issues and re-enable lint.
  10. /*
  11. * decaffeinate suggestions:
  12. * DS102: Remove unnecessary code created because of implicit returns
  13. * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
  14. */
  15. const SandboxedModule = require('sandboxed-module')
  16. const should = require('chai').should()
  17. const { expect } = require('chai')
  18. const sinon = require('sinon')
  19. const { assert } = require('chai')
  20. const modulePath = '../../../../app/src/Features/References/ReferencesHandler'
  21. describe('ReferencesHandler', function() {
  22. beforeEach(function() {
  23. this.projectId = '222'
  24. this.fakeProject = {
  25. _id: this.projectId,
  26. owner_ref: (this.fakeOwner = {
  27. _id: 'some_owner',
  28. features: {
  29. references: false
  30. }
  31. }),
  32. rootFolder: [
  33. {
  34. docs: [
  35. { name: 'one.bib', _id: 'aaa' },
  36. { name: 'two.txt', _id: 'bbb' }
  37. ],
  38. folders: [
  39. {
  40. docs: [{ name: 'three.bib', _id: 'ccc' }],
  41. fileRefs: [{ name: 'four.bib', _id: 'ghg' }],
  42. folders: []
  43. }
  44. ]
  45. }
  46. ]
  47. }
  48. this.docIds = ['aaa', 'ccc']
  49. this.handler = SandboxedModule.require(modulePath, {
  50. globals: {
  51. console: console
  52. },
  53. requires: {
  54. 'logger-sharelatex': {
  55. log() {},
  56. warn() {},
  57. err() {}
  58. },
  59. 'settings-sharelatex': (this.settings = {
  60. apis: {
  61. references: { url: 'http://some.url/references' },
  62. docstore: { url: 'http://some.url/docstore' },
  63. filestore: { url: 'http://some.url/filestore' }
  64. }
  65. }),
  66. request: (this.request = {
  67. get: sinon.stub(),
  68. post: sinon.stub()
  69. }),
  70. '../Project/ProjectGetter': (this.ProjectGetter = {
  71. getProject: sinon.stub().callsArgWith(2, null, this.fakeProject)
  72. }),
  73. '../User/UserGetter': (this.UserGetter = {
  74. getUser: sinon.stub()
  75. }),
  76. '../DocumentUpdater/DocumentUpdaterHandler': (this.DocumentUpdaterHandler = {
  77. flushDocToMongo: sinon.stub().callsArgWith(2, null)
  78. }),
  79. '../../infrastructure/Features': (this.Features = {
  80. hasFeature: sinon.stub().returns(true)
  81. })
  82. }
  83. })
  84. return (this.fakeResponseData = {
  85. projectId: this.projectId,
  86. keys: ['k1', 'k2']
  87. })
  88. })
  89. describe('index', function() {
  90. beforeEach(function() {
  91. sinon.stub(this.handler, '_findBibDocIds')
  92. sinon.stub(this.handler, '_findBibFileIds')
  93. sinon.stub(this.handler, '_isFullIndex').callsArgWith(1, null, true)
  94. this.request.post.callsArgWith(
  95. 1,
  96. null,
  97. { statusCode: 200 },
  98. this.fakeResponseData
  99. )
  100. return (this.call = callback => {
  101. return this.handler.index(this.projectId, this.docIds, callback)
  102. })
  103. })
  104. describe('when references feature is disabled', function() {
  105. beforeEach(function() {
  106. this.Features.hasFeature.withArgs('references').returns(false)
  107. })
  108. it('should not try to retrieve any user information', function(done) {
  109. this.call(() => {
  110. this.UserGetter.getUser.callCount.should.equal(0)
  111. done()
  112. })
  113. })
  114. it('should not produce an error', function(done) {
  115. return this.call(err => {
  116. expect(err).to.equal(undefined)
  117. return done()
  118. })
  119. })
  120. })
  121. describe('with docIds as an array', function() {
  122. beforeEach(function() {
  123. return (this.docIds = ['aaa', 'ccc'])
  124. })
  125. it('should not call _findBibDocIds', function(done) {
  126. return this.call((err, data) => {
  127. this.handler._findBibDocIds.callCount.should.equal(0)
  128. return done()
  129. })
  130. })
  131. it('should call ProjectGetter.getProject', function(done) {
  132. return this.call((err, data) => {
  133. this.ProjectGetter.getProject.callCount.should.equal(1)
  134. this.ProjectGetter.getProject
  135. .calledWith(this.projectId)
  136. .should.equal(true)
  137. return done()
  138. })
  139. })
  140. it('should not call _findBibDocIds', function(done) {
  141. return this.call((err, data) => {
  142. this.handler._findBibDocIds.callCount.should.equal(0)
  143. return done()
  144. })
  145. })
  146. it('should call DocumentUpdaterHandler.flushDocToMongo', function(done) {
  147. return this.call((err, data) => {
  148. this.DocumentUpdaterHandler.flushDocToMongo.callCount.should.equal(2)
  149. this.docIds.forEach(docId => {
  150. return this.DocumentUpdaterHandler.flushDocToMongo
  151. .calledWith(this.projectId, docId)
  152. .should.equal(true)
  153. })
  154. return done()
  155. })
  156. })
  157. it('should make a request to references service', function(done) {
  158. return this.call((err, data) => {
  159. this.request.post.callCount.should.equal(1)
  160. const arg = this.request.post.firstCall.args[0]
  161. expect(arg.json).to.have.all.keys('docUrls', 'fullIndex')
  162. expect(arg.json.docUrls.length).to.equal(2)
  163. expect(arg.json.fullIndex).to.equal(true)
  164. return done()
  165. })
  166. })
  167. it('should not produce an error', function(done) {
  168. return this.call((err, data) => {
  169. expect(err).to.equal(null)
  170. return done()
  171. })
  172. })
  173. it('should return data', function(done) {
  174. return this.call((err, data) => {
  175. expect(data).to.not.equal(null)
  176. expect(data).to.not.equal(undefined)
  177. expect(data).to.equal(this.fakeResponseData)
  178. return done()
  179. })
  180. })
  181. })
  182. describe('when ProjectGetter.getProject produces an error', function() {
  183. beforeEach(function() {
  184. return this.ProjectGetter.getProject.callsArgWith(2, new Error('woops'))
  185. })
  186. it('should produce an error', function(done) {
  187. return this.call((err, data) => {
  188. expect(err).to.not.equal(null)
  189. expect(err).to.be.instanceof(Error)
  190. expect(data).to.equal(undefined)
  191. return done()
  192. })
  193. })
  194. it('should not send request', function(done) {
  195. return this.call((err, data) => {
  196. this.request.post.callCount.should.equal(0)
  197. return done()
  198. })
  199. })
  200. })
  201. describe('when _isFullIndex produces an error', function() {
  202. beforeEach(function() {
  203. this.ProjectGetter.getProject.callsArgWith(2, null, this.fakeProject)
  204. return this.handler._isFullIndex.callsArgWith(1, new Error('woops'))
  205. })
  206. it('should produce an error', function(done) {
  207. return this.call((err, data) => {
  208. expect(err).to.not.equal(null)
  209. expect(err).to.be.instanceof(Error)
  210. expect(data).to.equal(undefined)
  211. return done()
  212. })
  213. })
  214. it('should not send request', function(done) {
  215. return this.call((err, data) => {
  216. this.request.post.callCount.should.equal(0)
  217. return done()
  218. })
  219. })
  220. })
  221. describe('when flushDocToMongo produces an error', function() {
  222. beforeEach(function() {
  223. this.ProjectGetter.getProject.callsArgWith(2, null, this.fakeProject)
  224. this.handler._isFullIndex.callsArgWith(1, false)
  225. return this.DocumentUpdaterHandler.flushDocToMongo.callsArgWith(
  226. 2,
  227. new Error('woops')
  228. )
  229. })
  230. it('should produce an error', function(done) {
  231. return this.call((err, data) => {
  232. expect(err).to.not.equal(null)
  233. expect(err).to.be.instanceof(Error)
  234. expect(data).to.equal(undefined)
  235. return done()
  236. })
  237. })
  238. it('should not send request', function(done) {
  239. return this.call((err, data) => {
  240. this.request.post.callCount.should.equal(0)
  241. return done()
  242. })
  243. })
  244. })
  245. describe('when request produces an error', function() {
  246. beforeEach(function() {
  247. this.ProjectGetter.getProject.callsArgWith(2, null, this.fakeProject)
  248. this.handler._isFullIndex.callsArgWith(1, null, false)
  249. this.DocumentUpdaterHandler.flushDocToMongo.callsArgWith(2, null)
  250. return this.request.post.callsArgWith(1, new Error('woops'))
  251. })
  252. it('should produce an error', function(done) {
  253. return this.call((err, data) => {
  254. expect(err).to.not.equal(null)
  255. expect(err).to.be.instanceof(Error)
  256. expect(data).to.equal(undefined)
  257. return done()
  258. })
  259. })
  260. })
  261. describe('when request responds with error status', function() {
  262. beforeEach(function() {
  263. this.ProjectGetter.getProject.callsArgWith(2, null, this.fakeProject)
  264. this.handler._isFullIndex.callsArgWith(1, null, false)
  265. return this.request.post.callsArgWith(
  266. 1,
  267. null,
  268. { statusCode: 500 },
  269. null
  270. )
  271. })
  272. it('should produce an error', function(done) {
  273. return this.call((err, data) => {
  274. expect(err).to.not.equal(null)
  275. expect(err).to.be.instanceof(Error)
  276. expect(data).to.equal(undefined)
  277. return done()
  278. })
  279. })
  280. })
  281. })
  282. describe('indexAll', function() {
  283. beforeEach(function() {
  284. sinon.stub(this.handler, '_findBibDocIds').returns(['aaa', 'ccc'])
  285. sinon.stub(this.handler, '_findBibFileIds').returns(['fff', 'ggg'])
  286. sinon.stub(this.handler, '_isFullIndex').callsArgWith(1, null, true)
  287. this.request.post.callsArgWith(
  288. 1,
  289. null,
  290. { statusCode: 200 },
  291. this.fakeResponseData
  292. )
  293. return (this.call = callback => {
  294. return this.handler.indexAll(this.projectId, callback)
  295. })
  296. })
  297. it('should call _findBibDocIds', function(done) {
  298. return this.call((err, data) => {
  299. this.handler._findBibDocIds.callCount.should.equal(1)
  300. this.handler._findBibDocIds
  301. .calledWith(this.fakeProject)
  302. .should.equal(true)
  303. return done()
  304. })
  305. })
  306. it('should call _findBibFileIds', function(done) {
  307. return this.call((err, data) => {
  308. this.handler._findBibDocIds.callCount.should.equal(1)
  309. this.handler._findBibDocIds
  310. .calledWith(this.fakeProject)
  311. .should.equal(true)
  312. return done()
  313. })
  314. })
  315. it('should call DocumentUpdaterHandler.flushDocToMongo', function(done) {
  316. return this.call((err, data) => {
  317. this.DocumentUpdaterHandler.flushDocToMongo.callCount.should.equal(2)
  318. return done()
  319. })
  320. })
  321. it('should make a request to references service', function(done) {
  322. return this.call((err, data) => {
  323. this.request.post.callCount.should.equal(1)
  324. const arg = this.request.post.firstCall.args[0]
  325. expect(arg.json).to.have.all.keys('docUrls', 'fullIndex')
  326. expect(arg.json.docUrls.length).to.equal(4)
  327. expect(arg.json.fullIndex).to.equal(true)
  328. return done()
  329. })
  330. })
  331. it('should not produce an error', function(done) {
  332. return this.call((err, data) => {
  333. expect(err).to.equal(null)
  334. return done()
  335. })
  336. })
  337. it('should return data', function(done) {
  338. return this.call((err, data) => {
  339. expect(data).to.not.equal(null)
  340. expect(data).to.not.equal(undefined)
  341. expect(data).to.equal(this.fakeResponseData)
  342. return done()
  343. })
  344. })
  345. describe('when ProjectGetter.getProject produces an error', function() {
  346. beforeEach(function() {
  347. return this.ProjectGetter.getProject.callsArgWith(2, new Error('woops'))
  348. })
  349. it('should produce an error', function(done) {
  350. return this.call((err, data) => {
  351. expect(err).to.not.equal(null)
  352. expect(err).to.be.instanceof(Error)
  353. expect(data).to.equal(undefined)
  354. return done()
  355. })
  356. })
  357. it('should not send request', function(done) {
  358. return this.call((err, data) => {
  359. this.request.post.callCount.should.equal(0)
  360. return done()
  361. })
  362. })
  363. })
  364. describe('when _isFullIndex produces an error', function() {
  365. beforeEach(function() {
  366. this.ProjectGetter.getProject.callsArgWith(2, null, this.fakeProject)
  367. return this.handler._isFullIndex.callsArgWith(1, new Error('woops'))
  368. })
  369. it('should produce an error', function(done) {
  370. return this.call((err, data) => {
  371. expect(err).to.not.equal(null)
  372. expect(err).to.be.instanceof(Error)
  373. expect(data).to.equal(undefined)
  374. return done()
  375. })
  376. })
  377. it('should not send request', function(done) {
  378. return this.call((err, data) => {
  379. this.request.post.callCount.should.equal(0)
  380. return done()
  381. })
  382. })
  383. })
  384. describe('when flushDocToMongo produces an error', function() {
  385. beforeEach(function() {
  386. this.ProjectGetter.getProject.callsArgWith(2, null, this.fakeProject)
  387. this.handler._isFullIndex.callsArgWith(1, false)
  388. return this.DocumentUpdaterHandler.flushDocToMongo.callsArgWith(
  389. 2,
  390. new Error('woops')
  391. )
  392. })
  393. it('should produce an error', function(done) {
  394. return this.call((err, data) => {
  395. expect(err).to.not.equal(null)
  396. expect(err).to.be.instanceof(Error)
  397. expect(data).to.equal(undefined)
  398. return done()
  399. })
  400. })
  401. it('should not send request', function(done) {
  402. return this.call((err, data) => {
  403. this.request.post.callCount.should.equal(0)
  404. return done()
  405. })
  406. })
  407. })
  408. })
  409. describe('_findBibDocIds', function() {
  410. beforeEach(function() {
  411. this.fakeProject = {
  412. rootFolder: [
  413. {
  414. docs: [
  415. { name: 'one.bib', _id: 'aaa' },
  416. { name: 'two.txt', _id: 'bbb' }
  417. ],
  418. folders: [
  419. { docs: [{ name: 'three.bib', _id: 'ccc' }], folders: [] }
  420. ]
  421. }
  422. ]
  423. }
  424. return (this.expectedIds = ['aaa', 'ccc'])
  425. })
  426. it('should select the correct docIds', function() {
  427. const result = this.handler._findBibDocIds(this.fakeProject)
  428. return expect(result).to.deep.equal(this.expectedIds)
  429. })
  430. it('should not error with a non array of folders from dirty data', function() {
  431. this.fakeProject.rootFolder[0].folders[0].folders = {}
  432. const result = this.handler._findBibDocIds(this.fakeProject)
  433. return expect(result).to.deep.equal(this.expectedIds)
  434. })
  435. })
  436. describe('_findBibFileIds', function() {
  437. beforeEach(function() {
  438. this.fakeProject = {
  439. rootFolder: [
  440. {
  441. docs: [
  442. { name: 'one.bib', _id: 'aaa' },
  443. { name: 'two.txt', _id: 'bbb' }
  444. ],
  445. fileRefs: [{ name: 'other.bib', _id: 'ddd' }],
  446. folders: [
  447. {
  448. docs: [{ name: 'three.bib', _id: 'ccc' }],
  449. fileRefs: [{ name: 'four.bib', _id: 'ghg' }],
  450. folders: []
  451. }
  452. ]
  453. }
  454. ]
  455. }
  456. return (this.expectedIds = ['ddd', 'ghg'])
  457. })
  458. it('should select the correct docIds', function() {
  459. const result = this.handler._findBibFileIds(this.fakeProject)
  460. return expect(result).to.deep.equal(this.expectedIds)
  461. })
  462. })
  463. describe('_isFullIndex', function() {
  464. beforeEach(function() {
  465. this.fakeProject = { owner_ref: (this.owner_ref = 'owner-ref-123') }
  466. this.owner = {
  467. features: {
  468. references: false
  469. }
  470. }
  471. this.UserGetter.getUser = sinon.stub()
  472. this.UserGetter.getUser
  473. .withArgs(this.owner_ref, { features: true })
  474. .yields(null, this.owner)
  475. return (this.call = callback => {
  476. return this.handler._isFullIndex(this.fakeProject, callback)
  477. })
  478. })
  479. describe('with references feature on', function() {
  480. beforeEach(function() {
  481. return (this.owner.features.references = true)
  482. })
  483. it('should return true', function() {
  484. return this.call((err, isFullIndex) => {
  485. expect(err).to.equal(null)
  486. return expect(isFullIndex).to.equal(true)
  487. })
  488. })
  489. })
  490. describe('with references feature off', function() {
  491. beforeEach(function() {
  492. return (this.owner.features.references = false)
  493. })
  494. it('should return false', function() {
  495. return this.call((err, isFullIndex) => {
  496. expect(err).to.equal(null)
  497. return expect(isFullIndex).to.equal(false)
  498. })
  499. })
  500. })
  501. describe('with referencesSearch', function() {
  502. beforeEach(function() {
  503. return (this.owner.features = {
  504. referencesSearch: true,
  505. references: false
  506. })
  507. })
  508. it('should return true', function() {
  509. return this.call((err, isFullIndex) => {
  510. expect(err).to.equal(null)
  511. return expect(isFullIndex).to.equal(true)
  512. })
  513. })
  514. })
  515. })
  516. })