blob: d6eac31e1953eec5964fc31fbb6935fb51f0b563 [file] [log] [blame]
'use strict';
var adapterPairs = [
['local', 'local'],
['local', 'http'],
['http', 'local'],
['http', 'http']
];
adapterPairs.forEach(function (adapters) {
describe('test.issue2674.js- ' +
adapters[0] + '-' + adapters[1], function () {
var dbs = {};
beforeEach(function () {
dbs.name = testUtils.adapterUrl(adapters[0], 'testdb');
dbs.secondDB = testUtils.adapterUrl(adapters[0], 'test_repl_remote');
//
// "test_slash_ids" is just a convenient name, b/c we re-use db
// names to avoid the Safari popup bug in long-running tests.
//
// Also for this test, it's only important that the fourth DB be
// truly remote.
dbs.thirdDB = testUtils.adapterUrl(adapters[0], 'test_slash_ids');
dbs.fourthDB = testUtils.adapterUrl(adapters[1], 'test_slash_ids_remote');
});
afterEach(function (done) {
testUtils.cleanup([dbs.name, dbs.secondDB, dbs.thirdDB, dbs.fourthDB],
done);
});
it('Should correctly synchronize attachments (#2674)', function () {
if (testUtils.isIE()) {
return Promise.resolve();
}
// 1. So I ran client app on two browsers (let’s call them A and B).
// 2. Then on client A I created plain document (without any attachments).
// 3. After that I put two attachments one by one by using putAttachment method.
// Let’s call them image1.jpg and image2.jpg.
// 4. In next step I synchronized local db on A with remote db (by using sync
// method without any additional options like live mode, retry, etc.).
// 5. On client B I synchronized its local db with remote db (in the same way
// like above).
// 6. On client A I removed one attachment, for example image1.jpg
// 7. On B I modified plain content of this document (without touching
// attachments) and put to local db.
// 8. Then I synchronized dbs on A as first
// 9. After that I synchronized dbs on B
// 10. I ran client app on another browser (C) where local db was empty.
// 11. Then I started the synchronization process on C and got an error (that
// image1.jpg was not found)
// 12. As the result the synchronization process failed and the data were not
// replicated at all (the local db on C was still empty)
var doc = {_id: 'a', a: 1};
var img1 = 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAMFBMVEX+9+' +
'j+9OD+7tL95rr93qT80YD7x2L6vkn6syz5qRT4ogT4nwD4ngD4nQD4nQD4' +
'nQDT2nT/AAAAcElEQVQY002OUQLEQARDw1D14f7X3TCdbfPnhQTqI5UqvG' +
'OWIz8gAIXFH9zmC63XRyTsOsCWk2A9Ga7wCXlA9m2S6G4JlVwQkpw/Ymxr' +
'UgNoMoyxBwSMH/WnAzy5cnfLFu+dK2l5gMvuPGLGJd1/9AOiBQiEgkzOpg' +
'AAAABJRU5ErkJggg==';
var img2 = 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAMFBMVEX+9+' +
'j+9OD+7tL95rr93qT80YD7x2L6vkn6syz5qRT4ogT4nwD4ngD4nQD4nQD4' +
'nQDT2nT/AAAAcElEQVQY002OUQLEQARDw1D14f7X3TCdbfPnhQTqI5UqvG' +
'OWIz8gAIXFH9zmC63XRyTsOsCWk2A9Ga7wCXlA9m2S6G4JlVwQkpw/Ymxr' +
'UgNoMoyxBwSMH/WnAzy5cnfLFu+dK2l5gMvuPGLGJd1/9AOiBQiEgkzOpg' +
'AAAABJRU5ErkJffQ==';
var dbA = new PouchDB(dbs.name);
var dbB = new PouchDB(dbs.secondDB);
var dbC = new PouchDB(dbs.thirdDB);
var remoteDb = new PouchDB(dbs.fourthDB);
// browser a:
// create document, no atts
function createDoc() {
return dbA.put(doc)
.then(addRev)
.catch(handleError);
}
// add image1.jpg
function addImg1() {
return dbA.putAttachment(
doc._id, 'image1.png', doc._rev, img1, 'image/png')
.then(addRev)
.catch(handleError);
}
// add image2.jpg
function addImg2() {
return dbA.putAttachment(
doc._id, 'image2.png', doc._rev, img2, 'image/png')
.then(addRev)
.catch(handleError);
}
// sync() with remote CouchDB
function syncWithRemote(source) {
return new testUtils.Promise(function (resolve, reject) {
source.sync(remoteDb).on('complete', function () {
resolve();
}).on('error', function (error) {
reject(error);
});
});
}
// remove image1.jpg from doc with an extra revision
// to guarantee conflict winning revision from dbA
function removeImg1() {
return dbA.get(doc._id)
.then(function (doc) {
return dbA.put(doc);
})
.then(addRev)
.then(function () {
return dbA.removeAttachment(doc._id, 'image1.png', doc._rev);
})
.catch(handleError);
}
// browser b:
// sync from remote CouchDB
// sync(updateDoc)
// update doc json, leave attachments alone
function updateDoc() {
var newDoc = {
_id: doc._id,
_rev: revs[2],
a: 2
};
return dbB.put(newDoc)
.catch(handleError);
}
// utils:
function handleError(error) {
throw error;
}
var revs = [];
function addRev(result) {
doc._rev = result.rev;
revs.push(result.rev);
}
return createDoc() // create document, no atts
.then(addImg1) // add image1.jpg
.then(addImg2) // add image2.jpg
.then(syncWithRemote.bind(this, dbA))// sync() with remote CouchDB
.then(removeImg1) // remove image1.jpg from doc
// go to browser b
.then(syncWithRemote.bind(this, dbB))// sync from remote CouchDB
.then(updateDoc) // update doc json, leave attachments alone
// go to browser a
.then(syncWithRemote.bind(this, dbA)) // sync with remote CouchDB,
// syncs up the delete
// go to browser b
.then(syncWithRemote.bind(this, dbB)) // sync with remote CouchDB,
// syncs up the conflictin non-att-change
// go to browser C
.then(syncWithRemote.bind(this, dbC)) // sync from remote CouchDB
// see replication error
.then(function () {
return dbC.allDocs({include_docs: true, attachments: true});
}).then(function (res) {
res.rows.forEach(function (row) {
delete row.value;
delete row.doc._rev;
delete row.doc._attachments['image2.png'].digest;
delete row.doc._attachments['image2.png'].revpos;
});
var expected = { "total_rows": 1,
"offset": 0,
"rows": [
{
"id": "a",
"key": "a",
"doc": {
"a": 1,
"_attachments": {
"image2.png": {
"content_type": "image/png",
"data": img2
}
},
"_id": "a"
}
}
]
};
res.should.deep.equal(expected);
});
});
});
});