MongoManagerTests.coffee 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486
  1. sinon = require('sinon')
  2. chai = require('chai')
  3. should = chai.should()
  4. expect = chai.expect
  5. modulePath = "../../../../app/js/MongoManager.js"
  6. packModulePath = "../../../../app/js/PackManager.js"
  7. SandboxedModule = require('sandboxed-module')
  8. {ObjectId} = require("mongojs")
  9. tk = require "timekeeper"
  10. describe "MongoManager", ->
  11. beforeEach ->
  12. tk.freeze(new Date())
  13. @MongoManager = SandboxedModule.require modulePath, requires:
  14. "./mongojs" : { db: @db = {}, ObjectId: ObjectId }
  15. "./PackManager" : SandboxedModule.require packModulePath, requires:
  16. "./LockManager" : {}
  17. @callback = sinon.stub()
  18. @doc_id = ObjectId().toString()
  19. @project_id = ObjectId().toString()
  20. afterEach ->
  21. tk.reset()
  22. describe "getLastCompressedUpdate", ->
  23. beforeEach ->
  24. @update = "mock-update"
  25. @db.docHistory = {}
  26. @db.docHistory.find = sinon.stub().returns @db.docHistory
  27. @db.docHistory.sort = sinon.stub().returns @db.docHistory
  28. @db.docHistory.limit = sinon.stub().returns @db.docHistory
  29. @db.docHistory.toArray = sinon.stub().callsArgWith(0, null, [@update])
  30. @MongoManager.getLastCompressedUpdate @doc_id, @callback
  31. it "should find the updates for the doc", ->
  32. @db.docHistory.find
  33. .calledWith(doc_id: ObjectId(@doc_id))
  34. .should.equal true
  35. it "should limit to one result", ->
  36. @db.docHistory.limit
  37. .calledWith(1)
  38. .should.equal true
  39. it "should sort in descending version order", ->
  40. @db.docHistory.sort
  41. .calledWith(v: -1)
  42. .should.equal true
  43. it "should call the call back with the update", ->
  44. @callback.calledWith(null, @update).should.equal true
  45. describe "deleteCompressedUpdate", ->
  46. beforeEach ->
  47. @update_id = ObjectId().toString()
  48. @db.docHistory =
  49. remove: sinon.stub().callsArg(1)
  50. @MongoManager.deleteCompressedUpdate(@update_id, @callback)
  51. it "should remove the update", ->
  52. @db.docHistory.remove
  53. .calledWith(_id: ObjectId(@update_id))
  54. .should.equal true
  55. it "should call the callback", ->
  56. @callback.called.should.equal true
  57. describe "popLastCompressedUpdate", ->
  58. describe "when there is no last update", ->
  59. beforeEach ->
  60. @MongoManager.getLastCompressedUpdate = sinon.stub().callsArgWith(1, null, null)
  61. @MongoManager.deleteCompressedUpdate = sinon.stub()
  62. @MongoManager.popLastCompressedUpdate @doc_id, @callback
  63. it "should get the last update", ->
  64. @MongoManager.getLastCompressedUpdate
  65. .calledWith(@doc_id)
  66. .should.equal true
  67. it "should not try to delete the last update", ->
  68. @MongoManager.deleteCompressedUpdate.called.should.equal false
  69. it "should call the callback with no update", ->
  70. @callback.calledWith(null, null).should.equal true
  71. describe "when there is an update", ->
  72. beforeEach ->
  73. @update = { _id: Object() }
  74. @MongoManager.getLastCompressedUpdate = sinon.stub().callsArgWith(1, null, @update)
  75. @MongoManager.deleteCompressedUpdate = sinon.stub().callsArgWith(1, null)
  76. @MongoManager.popLastCompressedUpdate @doc_id, @callback
  77. it "should get the last update", ->
  78. @MongoManager.getLastCompressedUpdate
  79. .calledWith(@doc_id)
  80. .should.equal true
  81. it "should delete the last update", ->
  82. @MongoManager.deleteCompressedUpdate
  83. .calledWith(@update._id)
  84. .should.equal true
  85. it "should call the callback with the update", ->
  86. @callback.calledWith(null, @update).should.equal true
  87. describe "insertCompressedUpdate", ->
  88. beforeEach ->
  89. @update = { op: "op", meta: "meta", v: "v"}
  90. @db.docHistory =
  91. insert: sinon.stub().callsArg(1)
  92. describe "temporarly", ->
  93. beforeEach ->
  94. @MongoManager.insertCompressedUpdate @project_id, @doc_id, @update, true, @callback
  95. it "should insert the update with a expiresAt field one week away", ->
  96. @db.docHistory.insert
  97. .calledWith({
  98. project_id: ObjectId(@project_id),
  99. doc_id: ObjectId(@doc_id),
  100. op: @update.op,
  101. meta: @update.meta,
  102. v: @update.v
  103. expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000)
  104. })
  105. .should.equal true
  106. it "should call the callback", ->
  107. @callback.called.should.equal true
  108. describe "permanenty", ->
  109. beforeEach ->
  110. @MongoManager.insertCompressedUpdate @project_id, @doc_id, @update, false, @callback
  111. it "should insert the update with no expiresAt field", ->
  112. @db.docHistory.insert
  113. .calledWith({
  114. project_id: ObjectId(@project_id),
  115. doc_id: ObjectId(@doc_id),
  116. op: @update.op,
  117. meta: @update.meta,
  118. v: @update.v
  119. })
  120. .should.equal true
  121. it "should call the callback", ->
  122. @callback.called.should.equal true
  123. describe "insertCompressedUpdates", ->
  124. beforeEach (done) ->
  125. @updates = [ "mock-update-1", "mock-update-2" ]
  126. @MongoManager.insertCompressedUpdate = sinon.stub().callsArg(4)
  127. @MongoManager.insertCompressedUpdates @project_id, @doc_id, @updates, @temporary = true, (args...) =>
  128. @callback(args...)
  129. done()
  130. it "should insert each update", ->
  131. for update in @updates
  132. @MongoManager.insertCompressedUpdate
  133. .calledWith(@project_id, @doc_id, update, @temporary)
  134. .should.equal true
  135. it "should call the callback", ->
  136. @callback.called.should.equal true
  137. describe "getDocUpdates", ->
  138. beforeEach ->
  139. @results = [
  140. {foo: "mock-update", v: 56, doc_id: 100, project_id: 1},
  141. {foo: "mock-update", v: 55, doc_id: 100, project_id: 1},
  142. {pack: [ {foo: "mock-update", v: 54, doc_id: 100, project_id: 1},
  143. {foo: "mock-update", v: 53, doc_id: 100, project_id: 1},
  144. {foo: "mock-update", v: 52, doc_id: 100, project_id: 1} ]
  145. , v: 52, doc_id: 100, project_id: 1},
  146. {foo: "mock-update", v: 42, doc_id: 100, project_id: 1},
  147. {foo: "mock-update", v: 41, doc_id: 100, project_id: 1}
  148. ]
  149. @updates_between = [
  150. {foo: "mock-update", v: 55, doc_id: 100, project_id: 1},
  151. {foo: "mock-update", v: 54, doc_id: 100, project_id: 1},
  152. {foo: "mock-update", v: 53, doc_id: 100, project_id: 1},
  153. {foo: "mock-update", v: 52, doc_id: 100, project_id: 1},
  154. {foo: "mock-update", v: 42, doc_id: 100, project_id: 1}
  155. ]
  156. @updates_after = [
  157. {foo: "mock-update", v: 56, doc_id: 100, project_id: 1},
  158. {foo: "mock-update", v: 55, doc_id: 100, project_id: 1},
  159. {foo: "mock-update", v: 54, doc_id: 100, project_id: 1},
  160. {foo: "mock-update", v: 53, doc_id: 100, project_id: 1},
  161. {foo: "mock-update", v: 52, doc_id: 100, project_id: 1},
  162. {foo: "mock-update", v: 42, doc_id: 100, project_id: 1}
  163. ]
  164. @db.docHistory = {}
  165. @db.docHistory.find = sinon.stub().returns @db.docHistory
  166. @db.docHistory.sort = sinon.stub().returns @db.docHistory
  167. @db.docHistory.limit = sinon.stub().returns @db.docHistory
  168. @db.docHistory.toArray = sinon.stub().callsArgWith(0, null, @results)
  169. @from = 42
  170. @to = 55
  171. describe "with a to version", ->
  172. beforeEach ->
  173. @MongoManager.getDocUpdates @doc_id, from: @from, to: @to, @callback
  174. it "should find the all updates between the to and from versions", ->
  175. @db.docHistory.find
  176. .calledWith({
  177. doc_id: ObjectId(@doc_id)
  178. v: { $gte: @from, $lte: @to }
  179. })
  180. .should.equal true
  181. it "should sort in descending version order", ->
  182. @db.docHistory.sort
  183. .calledWith("v": -1)
  184. .should.equal true
  185. #it "should not limit the results", ->
  186. # @db.docHistory.limit
  187. # .called.should.equal false
  188. it "should call the call back with the results", ->
  189. @callback.calledWith(null, @updates_between).should.equal true
  190. describe "without a to version", ->
  191. beforeEach ->
  192. @MongoManager.getDocUpdates @doc_id, from: @from, @callback
  193. it "should find the all updates after the from version", ->
  194. @db.docHistory.find
  195. .calledWith({
  196. doc_id: ObjectId(@doc_id)
  197. v: { $gte: @from }
  198. })
  199. .should.equal true
  200. it "should call the call back with the updates", ->
  201. @callback.calledWith(null, @updates_after).should.equal true
  202. describe "with a limit", ->
  203. beforeEach ->
  204. @MongoManager.getDocUpdates @doc_id, from: @from, limit: @limit = 10, @callback
  205. it "should limit the results", ->
  206. @db.docHistory.limit
  207. .calledWith(@limit)
  208. .should.equal true
  209. describe "getDocUpdates", ->
  210. beforeEach ->
  211. @results = [
  212. {foo: "mock-update", v: 56, meta: {end_ts: 110}, doc_id: 100, project_id: 1},
  213. {foo: "mock-update", v: 55, meta: {end_ts: 100}, doc_id: 100, project_id: 1},
  214. {pack: [
  215. {foo: "mock-update", v: 54, meta: {end_ts: 99}, doc_id: 300, project_id: 1},
  216. {foo: "mock-update", v: 53, meta: {end_ts: 98}, doc_id: 300, project_id: 1},
  217. {foo: "mock-update", v: 52, meta: {end_ts: 97}, doc_id: 300, project_id: 1} ]
  218. , v: 52, meta: {end_ts: 100}, doc_id: 300, project_id: 1},
  219. {pack: [
  220. {foo: "mock-update", v: 54, meta: {end_ts: 103}, doc_id: 200, project_id: 1},
  221. {foo: "mock-update", v: 53, meta: {end_ts: 101}, doc_id: 200, project_id: 1},
  222. {foo: "mock-update", v: 52, meta: {end_ts: 99}, doc_id: 200, project_id: 1} ]
  223. , v: 52, meta: {end_ts: 103}, doc_id: 200, project_id: 1},
  224. {foo: "mock-update", v: 42, meta:{end_ts: 90}, doc_id: 100, project_id: 1}
  225. ]
  226. @updates_before = [
  227. {foo: "mock-update", v: 55, meta: {end_ts: 100}, doc_id: 100, project_id: 1},
  228. {foo: "mock-update", v: 52, meta: {end_ts: 99}, doc_id: 200, project_id: 1},
  229. {foo: "mock-update", v: 54, meta: {end_ts: 99}, doc_id: 300, project_id: 1},
  230. {foo: "mock-update", v: 53, meta: {end_ts: 98}, doc_id: 300, project_id: 1},
  231. {foo: "mock-update", v: 52, meta: {end_ts: 97}, doc_id: 300, project_id: 1},
  232. {foo: "mock-update", v: 42, meta: {end_ts: 90}, doc_id: 100, project_id: 1},
  233. ]
  234. @updates_all = [
  235. {foo: "mock-update", v: 56, meta: {end_ts: 110}, doc_id: 100, project_id: 1},
  236. {foo: "mock-update", v: 54, meta: {end_ts: 103}, doc_id: 200, project_id: 1},
  237. {foo: "mock-update", v: 53, meta: {end_ts: 101}, doc_id: 200, project_id: 1},
  238. {foo: "mock-update", v: 55, meta: {end_ts: 100}, doc_id: 100, project_id: 1},
  239. {foo: "mock-update", v: 52, meta: {end_ts: 99}, doc_id: 200, project_id: 1},
  240. {foo: "mock-update", v: 54, meta: {end_ts: 99}, doc_id: 300, project_id: 1},
  241. {foo: "mock-update", v: 53, meta: {end_ts: 98}, doc_id: 300, project_id: 1},
  242. {foo: "mock-update", v: 52, meta: {end_ts: 97}, doc_id: 300, project_id: 1},
  243. {foo: "mock-update", v: 42, meta: {end_ts: 90}, doc_id: 100, project_id: 1}
  244. ]
  245. @db.docHistory = {}
  246. @db.docHistory.find = sinon.stub().returns @db.docHistory
  247. @db.docHistory.sort = sinon.stub().returns @db.docHistory
  248. @db.docHistory.limit = sinon.stub().returns @db.docHistory
  249. @db.docHistory.toArray = sinon.stub().callsArgWith(0, null, @results)
  250. @before = 101
  251. describe "with a before timestamp", ->
  252. beforeEach ->
  253. @MongoManager.getProjectUpdates @project_id, before: @before, @callback
  254. it "should find the all updates before the timestamp", ->
  255. @db.docHistory.find
  256. .calledWith({
  257. project_id: ObjectId(@project_id)
  258. "meta.end_ts": { $lt: @before }
  259. })
  260. .should.equal true
  261. it "should sort in descending version order", ->
  262. @db.docHistory.sort
  263. .calledWith("meta.end_ts": -1)
  264. .should.equal true
  265. it "should not limit the results", ->
  266. @db.docHistory.limit
  267. .called.should.equal false
  268. it "should call the call back with the updates", ->
  269. @callback.calledWith(null, @updates_before).should.equal true
  270. describe "without a before timestamp", ->
  271. beforeEach ->
  272. @MongoManager.getProjectUpdates @project_id, {}, @callback
  273. it "should find the all updates", ->
  274. @db.docHistory.find
  275. .calledWith({
  276. project_id: ObjectId(@project_id)
  277. })
  278. .should.equal true
  279. it "should call the call back with the updates", ->
  280. @callback.calledWith(null, @updates_all).should.equal true
  281. describe "with a limit", ->
  282. beforeEach ->
  283. @MongoManager.getProjectUpdates @project_id, before: @before, limit: @limit = 10, @callback
  284. it "should limit the results", ->
  285. @db.docHistory.limit
  286. .calledWith(@limit)
  287. .should.equal true
  288. describe "backportProjectId", ->
  289. beforeEach ->
  290. @db.docHistory =
  291. update: sinon.stub().callsArg(3)
  292. @MongoManager.backportProjectId @project_id, @doc_id, @callback
  293. it "should insert the project_id into all entries for the doc_id which don't have it set", ->
  294. @db.docHistory.update
  295. .calledWith({
  296. doc_id: ObjectId(@doc_id)
  297. project_id: { $exists: false }
  298. }, {
  299. $set: { project_id: ObjectId(@project_id) }
  300. }, {
  301. multi: true
  302. })
  303. .should.equal true
  304. it "should call the callback", ->
  305. @callback.called.should.equal true
  306. describe "getProjectMetaData", ->
  307. beforeEach ->
  308. @metadata = { "mock": "metadata" }
  309. @db.projectHistoryMetaData =
  310. find: sinon.stub().callsArgWith(1, null, [@metadata])
  311. @MongoManager.getProjectMetaData @project_id, @callback
  312. it "should look up the meta data in the db", ->
  313. @db.projectHistoryMetaData.find
  314. .calledWith({ project_id: ObjectId(@project_id) })
  315. .should.equal true
  316. it "should return the metadata", ->
  317. @callback.calledWith(null, @metadata).should.equal true
  318. describe "setProjectMetaData", ->
  319. beforeEach ->
  320. @metadata = { "mock": "metadata" }
  321. @db.projectHistoryMetaData =
  322. update: sinon.stub().callsArgWith(3, null, [@metadata])
  323. @MongoManager.setProjectMetaData @project_id, @metadata, @callback
  324. it "should upsert the metadata into the DB", ->
  325. @db.projectHistoryMetaData.update
  326. .calledWith({
  327. project_id: ObjectId(@project_id)
  328. }, {
  329. $set: @metadata
  330. }, {
  331. upsert: true
  332. })
  333. .should.equal true
  334. it "should call the callback", ->
  335. @callback.called.should.equal true
  336. describe "getDocChangesCount", ->
  337. beforeEach ->
  338. @db.docHistory =
  339. count: sinon.stub().callsArg(2)
  340. @MongoManager.getDocChangesCount @doc_id, @callback
  341. it "should return if there is any doc changes", ->
  342. @db.docHistory.count
  343. .calledWith({
  344. doc_id: ObjectId(@doc_id)
  345. inS3 : { $exists : false }
  346. }, {
  347. })
  348. .should.equal true
  349. it "should call the callback", ->
  350. @callback.called.should.equal true
  351. describe "getArchivedDocChanges", ->
  352. beforeEach ->
  353. @db.docHistory =
  354. count: sinon.stub().callsArg(2)
  355. @MongoManager.getArchivedDocChanges @doc_id, @callback
  356. it "should return if there is any archived doc changes", ->
  357. @db.docHistory.count
  358. .calledWith({
  359. doc_id: ObjectId(@doc_id)
  360. inS3 : true
  361. }, {
  362. })
  363. .should.equal true
  364. it "should call the callback", ->
  365. @callback.called.should.equal true
  366. describe "markDocHistoryAsArchived", ->
  367. beforeEach ->
  368. @update = { _id: ObjectId(), op: "op", meta: "meta", v: "v"}
  369. @db.docHistory =
  370. update: sinon.stub().callsArg(2)
  371. remove: sinon.stub().callsArg(1)
  372. @MongoManager.markDocHistoryAsArchived @doc_id, @update, @callback
  373. it "should update last doc change with inS3 flag", ->
  374. @db.docHistory.update
  375. .calledWith({
  376. _id: ObjectId(@update._id)
  377. },{
  378. $set : { inS3 : true }
  379. })
  380. .should.equal true
  381. it "should remove any other doc changes before last update", ->
  382. @db.docHistory.remove
  383. .calledWith({
  384. doc_id: ObjectId(@doc_id)
  385. inS3 : { $exists : false }
  386. v: { $lt : @update.v }
  387. expiresAt: {$exists : false}
  388. })
  389. .should.equal true
  390. it "should call the callback", ->
  391. @callback.called.should.equal true
  392. describe "markDocHistoryAsUnarchived", ->
  393. beforeEach ->
  394. @db.docHistory =
  395. update: sinon.stub().callsArg(3)
  396. @MongoManager.markDocHistoryAsUnarchived @doc_id, @callback
  397. it "should remove any doc changes inS3 flag", ->
  398. @db.docHistory.update
  399. .calledWith({
  400. doc_id: ObjectId(@doc_id)
  401. },{
  402. $unset : { inS3 : true }
  403. },{
  404. multi: true
  405. })
  406. .should.equal true
  407. it "should call the callback", ->
  408. @callback.called.should.equal true