|
1 | 1 | const async = require('async');
|
2 | 2 | const assert = require('assert');
|
| 3 | +const sinon = require('sinon'); |
3 | 4 | const werelogs = require('werelogs');
|
4 | 5 | const { MongoMemoryReplSet } = require('mongodb-memory-server');
|
5 | 6 | const { errors, versioning } = require('../../../../index');
|
6 | 7 | const logger = new werelogs.Logger('MongoClientInterface', 'debug', 'debug');
|
7 | 8 | const BucketInfo = require('../../../../lib/models/BucketInfo').default;
|
8 | 9 | const MetadataWrapper =
|
9 | 10 | require('../../../../lib/storage/metadata/MetadataWrapper');
|
| 11 | +const { VersionID } = require('../../../../lib/versioning'); |
10 | 12 | const { BucketVersioningKeyFormat } = versioning.VersioningConstants;
|
11 | 13 |
|
12 | 14 | const IMPL_NAME = 'mongodb';
|
@@ -128,6 +130,7 @@ describe('MongoClientInterface:metadata.putObjectMD', () => {
|
128 | 130 | });
|
129 | 131 |
|
130 | 132 | afterEach(done => {
|
| 133 | + sinon.restore(); |
131 | 134 | metadata.deleteBucket(BUCKET_NAME, logger, done);
|
132 | 135 | });
|
133 | 136 |
|
@@ -354,6 +357,71 @@ describe('MongoClientInterface:metadata.putObjectMD', () => {
|
354 | 357 | ], done);
|
355 | 358 | });
|
356 | 359 |
|
| 360 | + it(`should fail on versionID conflict when putting a new version ${variation.it}`, done => { |
| 361 | + const objVal = { |
| 362 | + key: OBJECT_NAME, |
| 363 | + }; |
| 364 | + const params = { |
| 365 | + versioning: true, |
| 366 | + versionId: null, |
| 367 | + repairMaster: null, |
| 368 | + }; |
| 369 | + |
| 370 | + // simulate a versionId collision by always generating the same versionId |
| 371 | + const genVID = sinon.stub(VersionID, 'generateUniqueVersionId') |
| 372 | + .returns('test-version-id'); |
| 373 | + |
| 374 | + async.series([ |
| 375 | + // We first create a master and a version |
| 376 | + next => metadata.putObjectMD(BUCKET_NAME, OBJECT_NAME, objVal, params, logger, next), |
| 377 | + // We put another version of the object with the same versionId |
| 378 | + next => metadata.putObjectMD(BUCKET_NAME, OBJECT_NAME, objVal, params, logger, next), |
| 379 | + ], err => { |
| 380 | + assert(err.is.InternalError, |
| 381 | + `expected InternalError, got ${err.name} with message: ${err.message}`, |
| 382 | + ); |
| 383 | + // make sure the retry triggered on the first collision detection |
| 384 | + assert(genVID.calledThrice, |
| 385 | + `expected generateUniqueVersionId to be called thrice, got ${genVID.callCount} times`); |
| 386 | + done(); |
| 387 | + }); |
| 388 | + }); |
| 389 | + |
| 390 | + it(`should succeed on version creation after a versionId collision ${variation.it}`, done => { |
| 391 | + const objVal = { |
| 392 | + key: OBJECT_NAME, |
| 393 | + }; |
| 394 | + const params = { |
| 395 | + versioning: true, |
| 396 | + versionId: null, |
| 397 | + repairMaster: null, |
| 398 | + }; |
| 399 | + |
| 400 | + // simulate a versionId collision by always generating the same versionId |
| 401 | + const genVID = sinon.stub(VersionID, 'generateUniqueVersionId') |
| 402 | + .onFirstCall().returns('test-version-id') |
| 403 | + .onSecondCall().returns('test-version-id') // trigger collision |
| 404 | + .onThirdCall().returns('test-version-id-retry'); // change versionId on retry |
| 405 | + |
| 406 | + async.series([ |
| 407 | + // We first create a master and a version |
| 408 | + next => metadata.putObjectMD(BUCKET_NAME, OBJECT_NAME, objVal, params, logger, next), |
| 409 | + // We put another version of the object with the same versionId |
| 410 | + next => metadata.putObjectMD(BUCKET_NAME, OBJECT_NAME, objVal, params, logger, next), |
| 411 | + ], (err, res) => { |
| 412 | + assert.ifError(err, `expected no error, got ${err}`); |
| 413 | + // make sure the retry triggered on the first collision detection |
| 414 | + assert(genVID.calledThrice, |
| 415 | + `expected generateUniqueVersionId to be called thrice, got ${genVID.callCount} times`); |
| 416 | + // make sure the last call returned a different versionId |
| 417 | + const vid1 = JSON.parse(res[0]).versionId; |
| 418 | + const vid2 = JSON.parse(res[1]).versionId; |
| 419 | + assert.notStrictEqual(vid1, vid2, |
| 420 | + `expected different versionIds, got ${vid1} and ${vid2}`); |
| 421 | + done(); |
| 422 | + }); |
| 423 | + }); |
| 424 | + |
357 | 425 | itOnlyInV1(`Should delete master when last version is delete marker ${variation.it}`, done => {
|
358 | 426 | const objVal = {
|
359 | 427 | key: OBJECT_NAME,
|
|
0 commit comments