DocArchiveManagerTests.coffee 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  1. assert = require("chai").assert
  2. sinon = require('sinon')
  3. chai = require('chai')
  4. should = chai.should()
  5. expect = chai.expect
  6. modulePath = "../../../app/js/DocArchiveManager.js"
  7. SandboxedModule = require('sandboxed-module')
  8. ObjectId = require("mongojs").ObjectId
  9. Errors = require "../../../app/js/Errors"
  10. crypto = require("crypto")
  11. describe "DocArchiveManager", ->
  12. beforeEach ->
  13. @settings =
  14. docstore:
  15. s3:
  16. secret: "secret"
  17. key: "this_key"
  18. bucket:"doc-archive-unit-test"
  19. @request =
  20. put: {}
  21. get: {}
  22. del: {}
  23. @archivedDocs = [{
  24. _id: ObjectId()
  25. inS3:true
  26. rev: 2
  27. }, {
  28. _id: ObjectId()
  29. inS3:true
  30. rev: 4
  31. }, {
  32. _id: ObjectId()
  33. inS3:true
  34. rev: 6
  35. }]
  36. @mongoDocs = [{
  37. _id: ObjectId()
  38. lines: ["one", "two", "three"]
  39. rev: 2
  40. }, {
  41. _id: ObjectId()
  42. lines: ["aaa", "bbb", "ccc"]
  43. rev: 4
  44. }, {
  45. _id: ObjectId()
  46. inS3: true
  47. rev: 6
  48. }, {
  49. _id: ObjectId()
  50. inS3: true
  51. rev: 6
  52. }, {
  53. _id: ObjectId()
  54. lines: ["111", "222", "333"]
  55. rev: 6
  56. }]
  57. @MongoManager =
  58. markDocAsArchived: sinon.stub().callsArgWith(2, null)
  59. upsertIntoDocCollection: sinon.stub().callsArgWith(3, null)
  60. getProjectsDocs: sinon.stub().callsArgWith(3, null, @mongoDocs)
  61. getArchivedProjectDocs: sinon.stub().callsArgWith(2, null, @mongoDocs)
  62. @requires =
  63. "settings-sharelatex": @settings
  64. "./MongoManager": @MongoManager
  65. "request": @request
  66. "./RangeManager": @RangeManager = {}
  67. "logger-sharelatex":
  68. log:->
  69. err:->
  70. @globals =
  71. JSON: JSON
  72. @error = "my errror"
  73. @project_id = ObjectId().toString()
  74. @stubbedError = new Errors.NotFoundError("Error in S3 request")
  75. @DocArchiveManager = SandboxedModule.require modulePath, requires: @requires, globals: @globals
  76. describe "archiveDoc", ->
  77. it "should use correct options", (done)->
  78. @request.put = sinon.stub().callsArgWith(1, null, {statusCode:200,headers:{etag:""}})
  79. @DocArchiveManager.archiveDoc @project_id, @mongoDocs[0], (err)=>
  80. opts = @request.put.args[0][0]
  81. assert.deepEqual(opts.aws, {key:@settings.docstore.s3.key, secret:@settings.docstore.s3.secret, bucket:@settings.docstore.s3.bucket})
  82. opts.body.should.equal JSON.stringify(
  83. lines: @mongoDocs[0].lines
  84. ranges: @mongoDocs[0].ranges
  85. schema_v: 1
  86. )
  87. opts.timeout.should.equal (30*1000)
  88. opts.uri.should.equal "https://#{@settings.docstore.s3.bucket}.s3.amazonaws.com/#{@project_id}/#{@mongoDocs[0]._id}"
  89. done()
  90. it "should return no md5 error", (done)->
  91. data = JSON.stringify(
  92. lines: @mongoDocs[0].lines
  93. ranges: @mongoDocs[0].ranges
  94. schema_v: 1
  95. )
  96. @md5 = crypto.createHash("md5").update(data).digest("hex")
  97. @request.put = sinon.stub().callsArgWith(1, null, {statusCode:200,headers:{etag:@md5}})
  98. @DocArchiveManager.archiveDoc @project_id, @mongoDocs[0], (err)=>
  99. should.not.exist err
  100. done()
  101. it "should return the error", (done)->
  102. @request.put = sinon.stub().callsArgWith(1, @stubbedError, {statusCode:400,headers:{etag:""}})
  103. @DocArchiveManager.archiveDoc @project_id, @mongoDocs[0], (err)=>
  104. should.exist err
  105. done()
  106. describe "unarchiveDoc", ->
  107. it "should use correct options", (done)->
  108. @request.get = sinon.stub().callsArgWith(1, null, statusCode:200, @mongoDocs[0].lines)
  109. @request.del = sinon.stub().callsArgWith(1, null, statusCode:204, {})
  110. @DocArchiveManager.unarchiveDoc @project_id, @mongoDocs[0]._id, (err)=>
  111. opts = @request.get.args[0][0]
  112. assert.deepEqual(opts.aws, {key:@settings.docstore.s3.key, secret:@settings.docstore.s3.secret, bucket:@settings.docstore.s3.bucket})
  113. opts.json.should.equal true
  114. opts.timeout.should.equal (30*1000)
  115. opts.uri.should.equal "https://#{@settings.docstore.s3.bucket}.s3.amazonaws.com/#{@project_id}/#{@mongoDocs[0]._id}"
  116. done()
  117. it "should return the error", (done)->
  118. @request.get = sinon.stub().callsArgWith(1, @stubbedError, {}, {})
  119. @DocArchiveManager.unarchiveDoc @project_id, @mongoDocs[0], (err)=>
  120. should.exist err
  121. done()
  122. it "should error if the doc lines are a string not an array", (done)->
  123. @request.get = sinon.stub().callsArgWith(1, null, statusCode:200, "this is a string")
  124. @request.del = sinon.stub()
  125. @DocArchiveManager.unarchiveDoc @project_id, @mongoDocs[0], (err)=>
  126. should.exist err
  127. @request.del.called.should.equal false
  128. done()
  129. describe "archiveAllDocs", ->
  130. it "should archive all project docs which are not in s3", (done)->
  131. @MongoManager.getProjectsDocs = sinon.stub().callsArgWith(3, null, @mongoDocs)
  132. @DocArchiveManager.archiveDoc = sinon.stub().callsArgWith(2, null)
  133. @DocArchiveManager.archiveAllDocs @project_id, (err)=>
  134. @DocArchiveManager.archiveDoc.calledWith(@project_id, @mongoDocs[0]).should.equal true
  135. @DocArchiveManager.archiveDoc.calledWith(@project_id, @mongoDocs[1]).should.equal true
  136. @DocArchiveManager.archiveDoc.calledWith(@project_id, @mongoDocs[4]).should.equal true
  137. @DocArchiveManager.archiveDoc.calledWith(@project_id, @mongoDocs[2]).should.equal false
  138. @DocArchiveManager.archiveDoc.calledWith(@project_id, @mongoDocs[3]).should.equal false
  139. should.not.exist err
  140. done()
  141. it "should return error if have no docs", (done)->
  142. @MongoManager.getProjectsDocs = sinon.stub().callsArgWith(3, null, null)
  143. @DocArchiveManager.archiveAllDocs @project_id, (err)=>
  144. should.exist err
  145. done()
  146. it "should return the error", (done)->
  147. @MongoManager.getProjectsDocs = sinon.stub().callsArgWith(3, @error, null)
  148. @DocArchiveManager.archiveAllDocs @project_id, (err)=>
  149. err.should.equal @error
  150. done()
  151. describe "when most have been already put in s3", ->
  152. beforeEach ->
  153. numberOfDocs = 10 * 1000
  154. @mongoDocs = []
  155. while --numberOfDocs != 0
  156. @mongoDocs.push({inS3:true, _id: ObjectId()})
  157. @MongoManager.getProjectsDocs = sinon.stub().callsArgWith(3, null, @mongoDocs)
  158. @DocArchiveManager.archiveDoc = sinon.stub().callsArgWith(2, null)
  159. it "should not throw and error", (done)->
  160. @DocArchiveManager.archiveAllDocs @project_id, (err)=>
  161. should.not.exist err
  162. done()
  163. describe "unArchiveAllDocs", ->
  164. it "should unarchive all inS3 docs", (done)->
  165. @MongoManager.getArchivedProjectDocs = sinon.stub().callsArgWith(1, null, @archivedDocs)
  166. @DocArchiveManager.unarchiveDoc = sinon.stub().callsArgWith(2, null)
  167. @DocArchiveManager.unArchiveAllDocs @project_id, (err)=>
  168. for doc in @archivedDocs
  169. @DocArchiveManager.unarchiveDoc.calledWith(@project_id, doc._id).should.equal true
  170. should.not.exist err
  171. done()
  172. it "should return error if have no docs", (done)->
  173. @MongoManager.getArchivedProjectDocs = sinon.stub().callsArgWith(1, null, null)
  174. @DocArchiveManager.unArchiveAllDocs @project_id, (err)=>
  175. should.exist err
  176. done()
  177. it "should return the error", (done)->
  178. @MongoManager.getArchivedProjectDocs = sinon.stub().callsArgWith(1, @error, null)
  179. @DocArchiveManager.unArchiveAllDocs @project_id, (err)=>
  180. err.should.equal @error
  181. done()
  182. describe "_s3DocToMongoDoc", ->
  183. describe "with the old schema", ->
  184. it "should return the docs lines", (done) ->
  185. @DocArchiveManager._s3DocToMongoDoc ["doc", "lines"], (error, doc) ->
  186. expect(doc).to.deep.equal {
  187. lines: ["doc", "lines"]
  188. }
  189. done()
  190. describe "with the new schema", ->
  191. it "should return the doc lines and ranges", (done) ->
  192. @RangeManager.jsonRangesToMongo = sinon.stub().returns {"mongo": "ranges"}
  193. @DocArchiveManager._s3DocToMongoDoc {
  194. lines: ["doc", "lines"]
  195. ranges: {"json": "ranges"}
  196. schema_v: 1
  197. }, (error, doc) ->
  198. expect(doc).to.deep.equal {
  199. lines: ["doc", "lines"]
  200. ranges: {"mongo": "ranges"}
  201. }
  202. done()
  203. it "should return just the doc lines when there are no ranges", (done) ->
  204. @DocArchiveManager._s3DocToMongoDoc {
  205. lines: ["doc", "lines"]
  206. schema_v: 1
  207. }, (error, doc) ->
  208. expect(doc).to.deep.equal {
  209. lines: ["doc", "lines"]
  210. }
  211. done()
  212. describe "with an unrecognised schema", ->
  213. it "should return an error", (done) ->
  214. @DocArchiveManager._s3DocToMongoDoc {
  215. schema_v: 2
  216. }, (error, doc) ->
  217. expect(error).to.exist
  218. done()
  219. describe "_mongoDocToS3Doc", ->
  220. describe "with a valid doc", ->
  221. it "should return the json version", (done) ->
  222. @DocArchiveManager._mongoDocToS3Doc doc = {
  223. lines: ["doc", "lines"]
  224. ranges: { "mock": "ranges" }
  225. }, (err, s3_doc) ->
  226. expect(s3_doc).to.equal JSON.stringify({
  227. lines: ["doc", "lines"]
  228. ranges: { "mock": "ranges" }
  229. schema_v: 1
  230. })
  231. done()
  232. describe "with null bytes in the result", ->
  233. beforeEach ->
  234. @_stringify = JSON.stringify
  235. JSON.stringify = sinon.stub().returns '{"bad": "\u0000"}'
  236. afterEach ->
  237. JSON.stringify = @_stringify
  238. it "should return an error", (done) ->
  239. @DocArchiveManager._mongoDocToS3Doc {
  240. lines: ["doc", "lines"]
  241. ranges: { "mock": "ranges" }
  242. }, (err, s3_doc) ->
  243. expect(err).to.exist
  244. done()
  245. describe "without doc lines", ->
  246. it "should return an error", (done) ->
  247. @DocArchiveManager._mongoDocToS3Doc {}, (err, s3_doc) ->
  248. expect(err).to.exist
  249. done()