UpdatingDocsTests.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395
  1. import mongodb from 'mongodb-legacy'
  2. import DocstoreApp from './helpers/DocstoreApp.js'
  3. import DocstoreClient from './helpers/DocstoreClient.js'
  4. const { ObjectId } = mongodb
  5. describe('Applying updates to a doc', function () {
  6. beforeEach(async function () {
  7. this.project_id = new ObjectId()
  8. this.doc_id = new ObjectId()
  9. this.originalLines = ['original', 'lines', '$1.00', '$foo']
  10. this.newLines = ['new', 'lines', '$2.00', '$bar']
  11. this.originalRanges = {
  12. changes: [
  13. {
  14. id: new ObjectId().toString(),
  15. op: { i: '$foo', p: 3 },
  16. meta: {
  17. user_id: new ObjectId().toString(),
  18. ts: new Date().toString(),
  19. },
  20. },
  21. ],
  22. }
  23. this.newRanges = {
  24. changes: [
  25. {
  26. id: new ObjectId().toString(),
  27. op: { i: '$bar', p: 6 },
  28. meta: {
  29. user_id: new ObjectId().toString(),
  30. ts: new Date().toString(),
  31. },
  32. },
  33. ],
  34. }
  35. this.version = 42
  36. await DocstoreApp.ensureRunning()
  37. await DocstoreClient.createDoc(
  38. this.project_id,
  39. this.doc_id,
  40. this.originalLines,
  41. this.version,
  42. this.originalRanges
  43. )
  44. })
  45. describe('when nothing has been updated', function () {
  46. beforeEach(async function () {
  47. this.body = await DocstoreClient.updateDoc(
  48. this.project_id,
  49. this.doc_id,
  50. this.originalLines,
  51. this.version,
  52. this.originalRanges
  53. )
  54. })
  55. it('should return modified = false', function () {
  56. this.body.modified.should.equal(false)
  57. })
  58. it('should not update the doc in the API', async function () {
  59. const doc = await DocstoreClient.getDoc(this.project_id, this.doc_id)
  60. doc.lines.should.deep.equal(this.originalLines)
  61. doc.version.should.equal(this.version)
  62. doc.ranges.should.deep.equal(this.originalRanges)
  63. })
  64. })
  65. describe('when the lines have changed', function () {
  66. beforeEach(async function () {
  67. this.body = await DocstoreClient.updateDoc(
  68. this.project_id,
  69. this.doc_id,
  70. this.newLines,
  71. this.version,
  72. this.originalRanges
  73. )
  74. })
  75. it('should return modified = true', function () {
  76. this.body.modified.should.equal(true)
  77. })
  78. it('should return the rev', function () {
  79. this.body.rev.should.equal(2)
  80. })
  81. it('should update the doc in the API', async function () {
  82. const doc = await DocstoreClient.getDoc(this.project_id, this.doc_id)
  83. doc.lines.should.deep.equal(this.newLines)
  84. doc.version.should.equal(this.version)
  85. doc.ranges.should.deep.equal(this.originalRanges)
  86. })
  87. })
  88. describe('when the version has changed', function () {
  89. beforeEach(async function () {
  90. this.body = await DocstoreClient.updateDoc(
  91. this.project_id,
  92. this.doc_id,
  93. this.originalLines,
  94. this.version + 1,
  95. this.originalRanges
  96. )
  97. })
  98. it('should return modified = true', function () {
  99. this.body.modified.should.equal(true)
  100. })
  101. it('should return the rev', function () {
  102. this.body.rev.should.equal(1)
  103. })
  104. it('should update the doc in the API', async function () {
  105. const doc = await DocstoreClient.getDoc(this.project_id, this.doc_id)
  106. doc.lines.should.deep.equal(this.originalLines)
  107. doc.version.should.equal(this.version + 1)
  108. doc.ranges.should.deep.equal(this.originalRanges)
  109. })
  110. })
  111. describe('when the version was decremented', function () {
  112. let statusCode
  113. beforeEach(async function () {
  114. try {
  115. this.body = await DocstoreClient.updateDoc(
  116. this.project_id,
  117. this.doc_id,
  118. this.newLines,
  119. this.version - 1,
  120. this.newRanges
  121. )
  122. } catch (error) {
  123. statusCode = error.info.status
  124. }
  125. })
  126. it('should return 409', function () {
  127. statusCode.should.equal(409)
  128. })
  129. it('should not update the doc in the API', async function () {
  130. const doc = await DocstoreClient.getDoc(this.project_id, this.doc_id)
  131. doc.lines.should.deep.equal(this.originalLines)
  132. doc.version.should.equal(this.version)
  133. doc.ranges.should.deep.equal(this.originalRanges)
  134. })
  135. })
  136. describe('when the ranges have changed', function () {
  137. beforeEach(async function () {
  138. this.body = await DocstoreClient.updateDoc(
  139. this.project_id,
  140. this.doc_id,
  141. this.originalLines,
  142. this.version,
  143. this.newRanges
  144. )
  145. })
  146. it('should return modified = true', function () {
  147. this.body.modified.should.equal(true)
  148. })
  149. it('should return the rev', function () {
  150. this.body.rev.should.equal(2)
  151. })
  152. it('should update the doc in the API', async function () {
  153. const doc = await DocstoreClient.getDoc(this.project_id, this.doc_id)
  154. doc.lines.should.deep.equal(this.originalLines)
  155. doc.version.should.equal(this.version)
  156. doc.ranges.should.deep.equal(this.newRanges)
  157. })
  158. })
  159. describe('when the doc does not exist', function () {
  160. beforeEach(async function () {
  161. this.missing_doc_id = new ObjectId()
  162. this.body = await DocstoreClient.updateDoc(
  163. this.project_id,
  164. this.missing_doc_id,
  165. this.originalLines,
  166. 0,
  167. this.originalRanges
  168. )
  169. })
  170. it('should create the doc', function () {
  171. this.body.rev.should.equal(1)
  172. })
  173. it('should be retreivable', async function () {
  174. const doc = await DocstoreClient.getDoc(
  175. this.project_id,
  176. this.missing_doc_id
  177. )
  178. doc.lines.should.deep.equal(this.originalLines)
  179. doc.version.should.equal(0)
  180. doc.ranges.should.deep.equal(this.originalRanges)
  181. })
  182. })
  183. describe('when malformed doc lines are provided', function () {
  184. describe('when the lines are not an array', function () {
  185. let statusCode
  186. beforeEach(async function () {
  187. try {
  188. this.body = await DocstoreClient.updateDoc(
  189. this.project_id,
  190. this.doc_id,
  191. { foo: 'bar' },
  192. this.version,
  193. this.originalRanges
  194. )
  195. } catch (error) {
  196. statusCode = error.info.status
  197. }
  198. })
  199. it('should return 400', function () {
  200. statusCode.should.equal(400)
  201. })
  202. it('should not update the doc in the API', async function () {
  203. const doc = await DocstoreClient.getDoc(this.project_id, this.doc_id)
  204. doc.lines.should.deep.equal(this.originalLines)
  205. })
  206. })
  207. describe('when the lines are not present', function () {
  208. let statusCode
  209. beforeEach(async function () {
  210. try {
  211. this.body = await DocstoreClient.updateDoc(
  212. this.project_id,
  213. this.doc_id,
  214. null,
  215. this.version,
  216. this.originalRanges
  217. )
  218. } catch (error) {
  219. statusCode = error.info.status
  220. }
  221. })
  222. it('should return 400', function () {
  223. statusCode.should.equal(400)
  224. })
  225. it('should not update the doc in the API', async function () {
  226. const doc = await DocstoreClient.getDoc(this.project_id, this.doc_id)
  227. doc.lines.should.deep.equal(this.originalLines)
  228. })
  229. })
  230. })
  231. describe('when no version is provided', function () {
  232. let statusCode
  233. beforeEach(async function () {
  234. try {
  235. this.body = await DocstoreClient.updateDoc(
  236. this.project_id,
  237. this.doc_id,
  238. this.originalLines,
  239. null,
  240. this.originalRanges
  241. )
  242. } catch (error) {
  243. statusCode = error.info.status
  244. }
  245. })
  246. it('should return 400', function () {
  247. statusCode.should.equal(400)
  248. })
  249. it('should not update the doc in the API', async function () {
  250. const doc = await DocstoreClient.getDoc(this.project_id, this.doc_id)
  251. doc.lines.should.deep.equal(this.originalLines)
  252. doc.version.should.equal(this.version)
  253. })
  254. })
  255. describe('when the content is large', function () {
  256. beforeEach(async function () {
  257. const line = new Array(1025).join('x') // 1kb
  258. this.largeLines = Array.apply(null, Array(1024)).map(() => line) // 1mb
  259. this.body = await DocstoreClient.updateDoc(
  260. this.project_id,
  261. this.doc_id,
  262. this.largeLines,
  263. this.version,
  264. this.originalRanges
  265. )
  266. })
  267. it('should return modified = true', function () {
  268. this.body.modified.should.equal(true)
  269. })
  270. it('should update the doc in the API', async function () {
  271. const doc = await DocstoreClient.getDoc(this.project_id, this.doc_id)
  272. doc.lines.should.deep.equal(this.largeLines)
  273. })
  274. })
  275. describe('when there is a large json payload', function () {
  276. beforeEach(async function () {
  277. const line = new Array(1025).join('x') // 1kb
  278. this.largeLines = Array.apply(null, Array(1024)).map(() => line) // 1kb
  279. this.originalRanges.padding = Array.apply(null, Array(2049)).map(
  280. () => line
  281. ) // 2mb + 1kb
  282. this.body = await DocstoreClient.updateDoc(
  283. this.project_id,
  284. this.doc_id,
  285. this.largeLines,
  286. this.version,
  287. this.originalRanges
  288. )
  289. })
  290. it('should return modified = true', function () {
  291. this.body.modified.should.equal(true)
  292. })
  293. it('should update the doc in the API', async function () {
  294. const doc = await DocstoreClient.getDoc(this.project_id, this.doc_id)
  295. doc.lines.should.deep.equal(this.largeLines)
  296. })
  297. })
  298. describe('when the document body is too large', function () {
  299. let statusCode, body
  300. beforeEach(async function () {
  301. const line = new Array(1025).join('x') // 1kb
  302. this.largeLines = Array.apply(null, Array(2049)).map(() => line) // 2mb + 1kb
  303. try {
  304. this.body = await DocstoreClient.updateDoc(
  305. this.project_id,
  306. this.doc_id,
  307. this.largeLines,
  308. this.version,
  309. this.originalRanges
  310. )
  311. } catch (error) {
  312. statusCode = error.info.status
  313. body = error.body
  314. }
  315. })
  316. it('should return 413', function () {
  317. statusCode.should.equal(413)
  318. })
  319. it('should report body too large', function () {
  320. body.should.equal('document body too large')
  321. })
  322. it('should not update the doc in the API', async function () {
  323. const doc = await DocstoreClient.getDoc(this.project_id, this.doc_id)
  324. doc.lines.should.deep.equal(this.originalLines)
  325. })
  326. })
  327. describe('when the json payload is too large', function () {
  328. beforeEach(async function () {
  329. const line = new Array(1024).join('x') // 1 KB
  330. this.largeLines = new Array(8192).fill(line) // 8 MB
  331. this.originalRanges.padding = new Array(6144).fill(line) // 6 MB
  332. try {
  333. this.body = await DocstoreClient.updateDoc(
  334. this.project_id,
  335. this.doc_id,
  336. this.largeLines,
  337. this.version,
  338. this.originalRanges
  339. )
  340. } catch (error) {
  341. // ignore error response
  342. }
  343. })
  344. it('should not update the doc in the API', async function () {
  345. const doc = await DocstoreClient.getDoc(this.project_id, this.doc_id)
  346. doc.lines.should.deep.equal(this.originalLines)
  347. })
  348. })
  349. })