Publish Vulpes Preview 15 sources for desktop, Android and Tundra
Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
This commit is contained in:
parent
93e3a48004
commit
b38dea0597
25901 changed files with 9982925 additions and 369 deletions
36
gaia/apps/sharedtest/test/unit/sync/sync-engine/adapter-mock.js
Executable file
36
gaia/apps/sharedtest/test/unit/sync/sync-engine/adapter-mock.js
Executable file
|
|
@ -0,0 +1,36 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
'use strict';
|
||||
|
||||
/* exported
|
||||
AdapterMock
|
||||
*/
|
||||
|
||||
var AdapterMock = (action, args) => {
|
||||
return {
|
||||
update(kintoColl, options) {
|
||||
AdapterMock.options = options;
|
||||
if (action === 'noop') {
|
||||
// Even if the adapter makes changes, we want to see that
|
||||
// SyncEngine does not sync those changes up if the adapter
|
||||
// return false.
|
||||
return kintoColl.update.apply(kintoColl, args).then(() => {
|
||||
return false;
|
||||
});
|
||||
}
|
||||
if (action) {
|
||||
return kintoColl[action].apply(kintoColl, args).then(() => {
|
||||
return true;
|
||||
});
|
||||
}
|
||||
return Promise.resolve(false);
|
||||
},
|
||||
handleConflict(conflict) {
|
||||
return Promise.resolve(conflict.local);
|
||||
},
|
||||
reset() {
|
||||
return Promise.resolve();
|
||||
}
|
||||
};
|
||||
};
|
||||
51
gaia/apps/sharedtest/test/unit/sync/sync-engine/fxsyncwebcrypto-mock.js
Executable file
51
gaia/apps/sharedtest/test/unit/sync/sync-engine/fxsyncwebcrypto-mock.js
Executable file
|
|
@ -0,0 +1,51 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
'use strict';
|
||||
|
||||
/* global SynctoServerFixture */
|
||||
/* exported FxSyncWebCrypto */
|
||||
|
||||
requireApp('sharedtest/test/unit/sync/fixtures/synctoserver.js');
|
||||
|
||||
var FxSyncWebCrypto = function() {};
|
||||
FxSyncWebCrypto.prototype = {
|
||||
setKeys: function(kB, cryptoKeys) {
|
||||
this.shouldWork = true;
|
||||
if (kB !== SynctoServerFixture.syncEngineOptions.kB) {
|
||||
this.shouldWork = false;
|
||||
}
|
||||
var correctCryptoKeys = JSON.parse(
|
||||
SynctoServerFixture.remoteData.crypto.payload);
|
||||
if (cryptoKeys.ciphertext !== correctCryptoKeys.ciphertext ||
|
||||
cryptoKeys.IV !== correctCryptoKeys.IV ||
|
||||
cryptoKeys.hmac !== correctCryptoKeys.hmac) {
|
||||
this.shouldWork = false;
|
||||
}
|
||||
if (this.shouldWork) {
|
||||
this.bulkKeyBundle = true;
|
||||
return Promise.resolve();
|
||||
}
|
||||
return Promise.reject(`SyncKeys hmac could not be verified with current mai\
|
||||
n key`);
|
||||
},
|
||||
encrypt: function(payload) {
|
||||
if (this.shouldWork) {
|
||||
return Promise.resolve({ mockEncrypted: JSON.stringify(payload) });
|
||||
}
|
||||
return Promise.reject();
|
||||
},
|
||||
decrypt: function(record) {
|
||||
var decryptablePayload = JSON.parse(
|
||||
SynctoServerFixture.remoteData.history.payload);
|
||||
if (this.shouldWork &&
|
||||
record.ciphertext === decryptablePayload.ciphertext &&
|
||||
record.IV === decryptablePayload.IV &&
|
||||
record.hmac === decryptablePayload.hmac) {
|
||||
return Promise.resolve(
|
||||
SynctoServerFixture.historyEntryDec.payload);
|
||||
}
|
||||
return Promise.reject(new Error(
|
||||
'payload.ciphertext is not a Base64 string'));
|
||||
}
|
||||
};
|
||||
256
gaia/apps/sharedtest/test/unit/sync/sync-engine/kinto-mock.js
Executable file
256
gaia/apps/sharedtest/test/unit/sync/sync-engine/kinto-mock.js
Executable file
|
|
@ -0,0 +1,256 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
'use strict';
|
||||
|
||||
/* global SynctoServerFixture */
|
||||
/* exported Kinto */
|
||||
|
||||
var Kinto = (function() {
|
||||
var mockProblem = null;
|
||||
var listData = {};
|
||||
|
||||
var KintoCollectionMock = function(collectionName, options = {}) {
|
||||
this.collectionName = collectionName;
|
||||
this.data = null;
|
||||
this.fireConflicts = options.fireConflicts || [];
|
||||
this._idSchemaUsed = options.idSchema; // Not mocking default UUID IdSchema.
|
||||
this._remoteTransformersUsed = options.remoteTransformers;
|
||||
};
|
||||
KintoCollectionMock.prototype = {
|
||||
sync: function() {
|
||||
var dataRecordIn = JSON.parse(JSON.stringify(
|
||||
SynctoServerFixture.remoteData[this.collectionName]));
|
||||
var transformOut = (item) => {
|
||||
if (!this._remoteTransformersUsed) {
|
||||
return Promise.resolve(item);
|
||||
}
|
||||
// We're only mocking the case where there is just one
|
||||
// remoteTransformer.
|
||||
return this._remoteTransformersUsed[0].encode(item);
|
||||
};
|
||||
|
||||
var transformIn = () => {
|
||||
if (!this._remoteTransformersUsed) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
try {
|
||||
// We're only mocking the case where there is just one
|
||||
// remoteTransformer.
|
||||
return this._remoteTransformersUsed[0].decode(dataRecordIn).
|
||||
then(decoded => {
|
||||
dataRecordIn = decoded;
|
||||
});
|
||||
} catch(err) {
|
||||
return Promise.reject(err);
|
||||
}
|
||||
};
|
||||
|
||||
var pushOut = () => {
|
||||
if (!listData[this.collectionName]) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
return Promise.all(listData[this.collectionName].data.map(item => {
|
||||
var dataRecordOut = JSON.parse(JSON.stringify(item));
|
||||
transformOut(dataRecordOut).then(encoded => {
|
||||
this.pushData.push(encoded);
|
||||
});
|
||||
}));
|
||||
};
|
||||
|
||||
var checkIdSchema = () => {
|
||||
if (!this._idSchemaUsed.validate(dataRecordIn.id)) {
|
||||
return Promise.reject(new Error('Invalid id: ' +
|
||||
dataRecordIn.id));
|
||||
}
|
||||
};
|
||||
|
||||
var storeListData = () => {
|
||||
if (!listData[this.collectionName]) {
|
||||
listData[this.collectionName] = { data: [ dataRecordIn ] };
|
||||
}
|
||||
return Promise.resolve({ ok: true, conflicts: this.fireConflicts });
|
||||
};
|
||||
|
||||
var markSyncCount = (syncResults) => {
|
||||
if (!Kinto.syncCount[this.collectionName]) {
|
||||
Kinto.syncCount[this.collectionName] = 0;
|
||||
}
|
||||
Kinto.syncCount[this.collectionName]++;
|
||||
return Promise.resolve(syncResults);
|
||||
};
|
||||
|
||||
var reportError = (error) => {
|
||||
listData[this.collectionName] = { data: [] };
|
||||
return Promise.resolve({ ok: false, conflicts: [], errors: [ error ] });
|
||||
};
|
||||
|
||||
this.pushData = [];
|
||||
|
||||
return pushOut().
|
||||
then(transformIn).
|
||||
then(checkIdSchema).
|
||||
then(storeListData).
|
||||
then(markSyncCount).
|
||||
catch(reportError);
|
||||
},
|
||||
|
||||
resolve: function(conflict, resolution) {
|
||||
listData[this.collectionName] = { data: [ resolution ] };
|
||||
return Promise.resolve();
|
||||
},
|
||||
|
||||
list: function() {
|
||||
return Promise.resolve(listData[this.collectionName]);
|
||||
},
|
||||
|
||||
get: function(id) {
|
||||
if (listData[this.collectionName]) {
|
||||
return Promise.resolve({
|
||||
data: listData[this.collectionName].data[0]
|
||||
});
|
||||
}
|
||||
return Promise.reject(new Error(`Record with id=${id} not found.`));
|
||||
},
|
||||
|
||||
create: function(obj, options = {}) {
|
||||
if (options.forceId) {
|
||||
if(!this._idSchemaUsed.validate(options.forceId)) {
|
||||
return Promise.reject(new Error('Invalid id: ' + options.forceId));
|
||||
}
|
||||
obj.id = options.forceId;
|
||||
} else {
|
||||
obj.id = this._idSchemaUsed.generate();
|
||||
}
|
||||
listData[this.collectionName].data.push(obj);
|
||||
return Promise.resolve();
|
||||
},
|
||||
|
||||
update: function(obj) {
|
||||
for (var i = 0; i < listData[this.collectionName].data.length; i++) {
|
||||
if (listData[this.collectionName].data[i].id === obj.id) {
|
||||
listData[this.collectionName].data[i] = obj;
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
return Promise.reject('not found!');
|
||||
},
|
||||
|
||||
delete: function(id) {
|
||||
for (var i = 0; i < listData[this.collectionName].data.length; i++) {
|
||||
if (listData[this.collectionName].data[i].id === id) {
|
||||
listData[this.collectionName].data.splice(i, 1);
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
return Promise.reject('not found!');
|
||||
},
|
||||
|
||||
clear: function() {
|
||||
Kinto.clearCalled = true;
|
||||
listData[this.collectionName] = { data: [] };
|
||||
return Promise.resolve();
|
||||
}
|
||||
};
|
||||
|
||||
var UnreachableKintoCollectionMock = function() {};
|
||||
UnreachableKintoCollectionMock.prototype = {
|
||||
sync() {
|
||||
return Promise.reject(new Error(`HTTP 0; TypeError: NetworkError when att\
|
||||
empting to fetch resource.`));
|
||||
},
|
||||
list() {},
|
||||
get(id) {
|
||||
return Promise.reject(new Error(`Record with id=${id} not found.`));
|
||||
},
|
||||
create() {},
|
||||
update() {},
|
||||
delete() {},
|
||||
clear() {
|
||||
Kinto.clearCalled = true;
|
||||
},
|
||||
};
|
||||
|
||||
var HttpCodeKintoCollectionMock = function(status) {
|
||||
this.status = status;
|
||||
};
|
||||
HttpCodeKintoCollectionMock.prototype = {
|
||||
sync() {
|
||||
var err = new Error();
|
||||
err.response = {
|
||||
status: this.status
|
||||
};
|
||||
return Promise.reject(err);
|
||||
},
|
||||
list() {},
|
||||
get(id) {
|
||||
return Promise.reject(new Error(`Record with id=${id} not found.`));
|
||||
},
|
||||
create() {},
|
||||
update() {},
|
||||
delete() {},
|
||||
clear() {
|
||||
Kinto.clearCalled = true;
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
var Kinto = function(kintoOptions) {
|
||||
this.options = kintoOptions;
|
||||
this.collection = function(collectionName, collectionOptions = {}) {
|
||||
var mockProblemCase = () => {
|
||||
var httpCode;
|
||||
if (mockProblem &&
|
||||
mockProblem.collectionName === collectionName) {
|
||||
httpCode = parseInt(mockProblem.problem);
|
||||
if (isNaN(httpCode)) {
|
||||
if (mockProblem.problem === 'conflicts') {
|
||||
collectionOptions.fireConflicts = [{
|
||||
local: { bar: 'local' },
|
||||
remote: SynctoServerFixture.historyEntryDec.payload
|
||||
}];
|
||||
return new KintoCollectionMock(collectionName, collectionOptions);
|
||||
}
|
||||
return new KintoCollectionMock(mockProblem.problem,
|
||||
collectionOptions);
|
||||
}
|
||||
return new HttpCodeKintoCollectionMock(httpCode);
|
||||
}
|
||||
};
|
||||
|
||||
var unauthCase = () => {
|
||||
if (kintoOptions.headers.Authorization !==
|
||||
'BrowserID test-assertion-mock') {
|
||||
return new HttpCodeKintoCollectionMock(401);
|
||||
}
|
||||
};
|
||||
|
||||
var unreachableCase = () => {
|
||||
if (kintoOptions.remote !== 'http://localhost:8000/v1/') {
|
||||
return new UnreachableKintoCollectionMock(collectionName);
|
||||
}
|
||||
};
|
||||
|
||||
var defaultCase = () => {
|
||||
return new KintoCollectionMock(collectionName, collectionOptions);
|
||||
};
|
||||
|
||||
return mockProblemCase() ||
|
||||
unauthCase() ||
|
||||
unreachableCase() ||
|
||||
defaultCase();
|
||||
};
|
||||
};
|
||||
|
||||
Kinto.syncCount = {};
|
||||
Kinto.clearCalled = false;
|
||||
|
||||
Kinto.setMockProblem = (problem) => {
|
||||
mockProblem = problem;
|
||||
Kinto.syncCount = {};
|
||||
Kinto.clearCalled = false;
|
||||
listData = {};
|
||||
};
|
||||
|
||||
return Kinto;
|
||||
})();
|
||||
397
gaia/apps/sharedtest/test/unit/sync/sync-engine/syncengine_test.js
Executable file
397
gaia/apps/sharedtest/test/unit/sync/sync-engine/syncengine_test.js
Executable file
|
|
@ -0,0 +1,397 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
'use strict';
|
||||
|
||||
/* global
|
||||
AdapterMock,
|
||||
FxSyncWebCrypto,
|
||||
Kinto,
|
||||
SyncEngine,
|
||||
SynctoServerFixture
|
||||
*/
|
||||
|
||||
requireApp('sharedtest/test/unit/sync/sync-engine/adapter-mock.js');
|
||||
requireApp('sharedtest/test/unit/sync/fixtures/synctoserver.js');
|
||||
requireApp('sharedtest/test/unit/sync/sync-engine/fxsyncwebcrypto-mock.js');
|
||||
requireApp('sharedtest/test/unit/sync/sync-engine/kinto-mock.js');
|
||||
require('/shared/js/sync/engine.js');
|
||||
|
||||
var cloneObject = (obj) => {
|
||||
return JSON.parse(JSON.stringify(obj));
|
||||
};
|
||||
|
||||
suite('SyncEngine', function() {
|
||||
suite('constructor', function() {
|
||||
test('constructs a SyncEngine object', function(done) {
|
||||
const options = SynctoServerFixture.syncEngineOptions;
|
||||
const se = new SyncEngine(options);
|
||||
expect(se).to.be.an('object');
|
||||
expect(se._kB).to.equal(options.kB);
|
||||
expect(se._collections).to.be.an('object');
|
||||
expect(se._controlCollections).to.be.an('object');
|
||||
expect(se._fswc).to.be.instanceOf(FxSyncWebCrypto);
|
||||
expect(se._adapters).to.deep.equal(options.adapters);
|
||||
expect(se._ready).to.equal(false);
|
||||
done();
|
||||
});
|
||||
|
||||
test('checks that options is an Object', function(done) {
|
||||
try {
|
||||
var se = new SyncEngine(5);
|
||||
} catch(err) {
|
||||
expect(se).to.equal(undefined);
|
||||
expect(err.message).to.equal('options should be an Object');
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
||||
['URL', 'assertion', 'kB'].forEach(field => {
|
||||
test(`checks that ${field} is a String`, function(done) {
|
||||
var credentials = cloneObject(SynctoServerFixture.syncEngineOptions);
|
||||
credentials[field] = 7.2;
|
||||
try {
|
||||
var se = new SyncEngine(credentials);
|
||||
} catch(err) {
|
||||
expect(se).to.equal(undefined);
|
||||
expect(err.message).to.equal(`options.${field} should be a String`);
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test('checks that options.adapters is an Object', function(done) {
|
||||
var credentials = cloneObject(SynctoServerFixture.syncEngineOptions);
|
||||
credentials.adapters = 'foo';
|
||||
try {
|
||||
var se = new SyncEngine(credentials);
|
||||
} catch(err) {
|
||||
expect(se).to.equal(undefined);
|
||||
expect(err.message).to.equal('options.adapters should be an Object');
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
||||
test('checks that options.adapters members are Objects', function(done) {
|
||||
var credentials = cloneObject(SynctoServerFixture.syncEngineOptions);
|
||||
credentials.adapters = {
|
||||
foo: 'bar'
|
||||
};
|
||||
try {
|
||||
var se = new SyncEngine(credentials);
|
||||
} catch(err) {
|
||||
expect(se).to.equal(undefined);
|
||||
expect(err.message).to.equal(
|
||||
'options.adapters.foo should be an Object');
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
||||
['update', 'handleConflict'].forEach(methodName => {
|
||||
test(`checks that options.adapters[x].${methodName} are Functions`,
|
||||
function(done) {
|
||||
var credentials = cloneObject(SynctoServerFixture.syncEngineOptions);
|
||||
credentials.adapters = {
|
||||
foo: AdapterMock()
|
||||
};
|
||||
credentials.adapters.foo[methodName] = 8;
|
||||
try {
|
||||
var se = new SyncEngine(credentials);
|
||||
} catch(err) {
|
||||
expect(se).to.equal(undefined);
|
||||
expect(err.message).to.equal(`options.adapters.foo.${methodName} shou\
|
||||
ld be a Function`);
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
suite('syncNow', function() {
|
||||
setup(function() {
|
||||
Kinto.setMockProblem();
|
||||
});
|
||||
|
||||
test('resolves its promise', function(done) {
|
||||
var se = new SyncEngine(SynctoServerFixture.syncEngineOptions);
|
||||
expect(se.syncNow({ history: {} })).to.eventually.
|
||||
equal(undefined).and.notify(done);
|
||||
});
|
||||
|
||||
test('initializes the Kinto object', function(done) {
|
||||
var se = new SyncEngine(SynctoServerFixture.syncEngineOptions);
|
||||
se.syncNow({ history: {} }).then(function() {
|
||||
expect(se._kinto).to.be.instanceOf(Kinto);
|
||||
expect(se._kinto.options).to.deep.equal({
|
||||
bucket: SynctoServerFixture.xClientState,
|
||||
remote: SynctoServerFixture.syncEngineOptions.URL,
|
||||
timeout: 180 * 1000,
|
||||
headers: {
|
||||
'Authorization': 'BrowserID ' +
|
||||
SynctoServerFixture.syncEngineOptions.assertion
|
||||
}
|
||||
});
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('sets the correct xClientState as the bucket', function(done) {
|
||||
var se = new SyncEngine(SynctoServerFixture.syncEngineOptions);
|
||||
se.syncNow({ history: {} }).then(function() {
|
||||
expect(se._kinto.options.bucket).to.equal(
|
||||
SynctoServerFixture.xClientState);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('Passes options to the DataAdapter', function(done) {
|
||||
var se = new SyncEngine(SynctoServerFixture.syncEngineOptions);
|
||||
se.syncNow({ history: { readonly: true } }).then(function() {
|
||||
expect(AdapterMock.options).to.deep.equal({
|
||||
readonly: true,
|
||||
userid: SynctoServerFixture.xClientState
|
||||
});
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('Syncs crypto collection only first time', function(done) {
|
||||
var se = new SyncEngine(SynctoServerFixture.syncEngineOptions);
|
||||
se.syncNow({ history: {} }).then(() => {
|
||||
expect(Kinto.syncCount.meta).to.equal(1);
|
||||
expect(Kinto.syncCount.crypto).to.equal(1);
|
||||
expect(Kinto.syncCount.history).to.equal(1);
|
||||
var se2 = new SyncEngine(SynctoServerFixture.syncEngineOptions);
|
||||
return se2.syncNow({ history: {} });
|
||||
}).then(() => {
|
||||
expect(Kinto.syncCount.meta).to.equal(2);
|
||||
expect(Kinto.syncCount.crypto).to.equal(1);
|
||||
expect(Kinto.syncCount.history).to.equal(2);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('retrieves and decrypts the remote data', function(done) {
|
||||
var se = new SyncEngine(SynctoServerFixture.syncEngineOptions);
|
||||
se.syncNow({ history: {} }).then(() => {
|
||||
expect(se._collections.history).to.be.an('object');
|
||||
return se._collections.history.list();
|
||||
}).then(list => {
|
||||
expect(list).to.be.an('object');
|
||||
expect(list.data).to.be.instanceOf(Array);
|
||||
expect(list.data.length).to.equal(1);
|
||||
expect(Object.keys(list.data[0]).sort())
|
||||
.to.deep.equal(['id', 'last_modified', 'payload']);
|
||||
expect(list.data[0].payload).to.be.an('object');
|
||||
expect(list.data[0].payload.histUri).to.be.a('string');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('encrypts and pushes added records', function(done) {
|
||||
var credentials = cloneObject(SynctoServerFixture.syncEngineOptions);
|
||||
credentials.adapters.history = AdapterMock('create', [
|
||||
{ payload: 'foo' }
|
||||
]);
|
||||
var se = new SyncEngine(credentials);
|
||||
se.syncNow({ history: {} }).then(() => {
|
||||
return se._collections.history.list();
|
||||
}).then(list => {
|
||||
expect(list.data.length).to.equal(2);
|
||||
expect(se._collections.history.pushData.length).to.equal(2);
|
||||
expect(list.data[0].payload.histUri).to.be.a('string');
|
||||
expect(list.data[1].payload).to.equal('foo');
|
||||
expect(se._collections.history.pushData[1].payload).to.equal(
|
||||
'{"mockEncrypted":"\\"foo\\""}');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('enforces FxSyncIdSchema on added records', function(done) {
|
||||
var credentials = cloneObject(SynctoServerFixture.syncEngineOptions);
|
||||
credentials.adapters.history = AdapterMock('create', [
|
||||
{ payload: 'foo' },
|
||||
{ forceId: 8.4 }
|
||||
]);
|
||||
|
||||
var se = new SyncEngine(credentials);
|
||||
expect(se.syncNow({ history: {} })).to.be.rejectedWith(Error, `Invalid id\
|
||||
: 8.4`).and.notify(done);
|
||||
});
|
||||
|
||||
test('encrypts and pushes updated records', function(done) {
|
||||
var credentials = cloneObject(SynctoServerFixture.syncEngineOptions);
|
||||
credentials.adapters.history = AdapterMock('update', [ {
|
||||
id: SynctoServerFixture.remoteData.history.id,
|
||||
payload: 'foo'
|
||||
}]);
|
||||
|
||||
var se = new SyncEngine(credentials);
|
||||
se.syncNow({ 'history': {} }).then(() => {
|
||||
return se._collections.history.list();
|
||||
}).then(list => {
|
||||
expect(list.data.length).to.equal(1);
|
||||
expect(se._collections.history.pushData.length).to.equal(1);
|
||||
expect(se._collections.history.pushData[0].payload).to.equal(
|
||||
'{"mockEncrypted":"\\"foo\\""}');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('only uploads encrypted payload and id', function(done) {
|
||||
var credentials = cloneObject(SynctoServerFixture.syncEngineOptions);
|
||||
credentials.adapters.history = AdapterMock('update', [ {
|
||||
id: SynctoServerFixture.remoteData.history.id,
|
||||
payload: 'foo',
|
||||
strayField: 'bar'
|
||||
}]);
|
||||
|
||||
var se = new SyncEngine(credentials);
|
||||
se.syncNow({ history: {} }).then(() => {
|
||||
return se._collections.history.list();
|
||||
}).then(list => {
|
||||
expect(list.data.length).to.equal(1);
|
||||
expect(Object.keys(se._collections.history.pushData[0]).sort())
|
||||
.to.deep.equal(['id', 'payload']);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
test('pushes deletes of records', function(done) {
|
||||
var credentials = cloneObject(SynctoServerFixture.syncEngineOptions);
|
||||
credentials.adapters.history = AdapterMock('delete', [
|
||||
SynctoServerFixture.remoteData.history.id
|
||||
]);
|
||||
|
||||
var se = new SyncEngine(credentials);
|
||||
se.syncNow({ history: {} }).then(() => {
|
||||
return se._collections.history.list();
|
||||
}).then(list => {
|
||||
expect(list.data.length).to.equal(0);
|
||||
expect(se._collections.history.pushData.length).to.equal(0);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('encrypts and pushes conflict resolutions', function(done) {
|
||||
Kinto.setMockProblem({ collectionName: 'history', problem: 'conflicts' });
|
||||
var se = new SyncEngine(SynctoServerFixture.syncEngineOptions);
|
||||
se.syncNow({ history: {} }).then(() => {
|
||||
return se._collections.history.list();
|
||||
}).then(list => {
|
||||
expect(list.data.length).to.equal(1);
|
||||
expect(list.data[0].bar).to.equal('local');
|
||||
expect(se._collections.history.pushData[0].payload).to.equal('{}');
|
||||
Kinto.setMockProblem();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('does not push if nothing changed', function(done) {
|
||||
var credentials = cloneObject(SynctoServerFixture.syncEngineOptions);
|
||||
credentials.adapters.history = AdapterMock('noop', [ {
|
||||
id: SynctoServerFixture.remoteData.history.id,
|
||||
foo: 'bar'
|
||||
}]);
|
||||
|
||||
var se = new SyncEngine(credentials);
|
||||
se.syncNow({ 'history': {} }).then(() => {
|
||||
return se._collections.history.list();
|
||||
}).then(list => {
|
||||
expect(list.data.length).to.equal(1);
|
||||
expect(se._collections.history.pushData.length).to.equal(0);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test(`rejects its promise if collectionOptions is not an object`,
|
||||
function(done) {
|
||||
var se = new SyncEngine(SynctoServerFixture.syncEngineOptions);
|
||||
expect(se.syncNow('foo')).to.be.rejectedWith(Error, `collectionOptions sh\
|
||||
ould be an object`).and.notify(done);
|
||||
});
|
||||
|
||||
['URL', 'assertion', 'kB'].forEach(function(field) {
|
||||
test(`rejects its promise if ${field} is wrong`, function(done) {
|
||||
var credentials = cloneObject(SynctoServerFixture.syncEngineOptions);
|
||||
credentials.adapters = {
|
||||
history: AdapterMock()
|
||||
};
|
||||
credentials[field] = 'whoopsie';
|
||||
var se = new SyncEngine(credentials);
|
||||
se.syncNow({ history: {} }).catch(err => {
|
||||
if (field === 'assertion') {
|
||||
expect(err).to.be.instanceOf(SyncEngine.AuthError);
|
||||
} else if (field === 'URL') {
|
||||
expect(err).to.be.instanceOf(SyncEngine.TryLaterError);
|
||||
} else {
|
||||
expect(err).to.equal(`SyncKeys hmac could not be verified with curr\
|
||||
ent main key`);
|
||||
}
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
['meta', 'crypto'].forEach(function(collectionName) {
|
||||
[
|
||||
'401',
|
||||
'404',
|
||||
'500',
|
||||
'503',
|
||||
'wrong-payload-meta',
|
||||
'wrong-payload-crypto',
|
||||
'wrong-ciphertext',
|
||||
'wrong-id'
|
||||
].forEach(function(problem) {
|
||||
test(`rejects its promise if ${collectionName} response is ${problem}`,
|
||||
function(done) {
|
||||
Kinto.setMockProblem({ collectionName, problem });
|
||||
var se = new SyncEngine(SynctoServerFixture.syncEngineOptions);
|
||||
se.syncNow({ history: {} }).catch(err => {
|
||||
if (problem === '401') {
|
||||
expect(err.message).to.equal('unauthorized');
|
||||
} else if (['404', '500', '503'].indexOf(problem) !== -1) {
|
||||
expect(err.message).to.equal('try later');
|
||||
} else {
|
||||
expect(err.message).to.equal('unrecoverable');
|
||||
}
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
[
|
||||
'401',
|
||||
'404',
|
||||
'500',
|
||||
'503',
|
||||
'wrong-payload-history',
|
||||
'wrong-ciphertext',
|
||||
'wrong-id'
|
||||
].forEach(function(problem) {
|
||||
test(`rejects its promise if response is ${problem}`, function(done) {
|
||||
Kinto.setMockProblem({ collectionName:'history', problem });
|
||||
|
||||
var se = new SyncEngine(SynctoServerFixture.syncEngineOptions);
|
||||
se.syncNow({ history: {} }).catch(err => {
|
||||
if (problem === '401') {
|
||||
expect(err.message).to.equal('unauthorized');
|
||||
} else if (['404', '500', '503'].indexOf(problem) !== -1) {
|
||||
expect(err.message).to.equal('try later');
|
||||
} else {
|
||||
expect(err.message).to.equal('unrecoverable');
|
||||
}
|
||||
if (['401', '404', '500', '503'].indexOf(problem) === -1) {
|
||||
expect(Kinto.clearCalled).to.equal(true);
|
||||
} else {
|
||||
expect(Kinto.clearCalled).to.equal(false);
|
||||
}
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
Loading…
Reference in a new issue