|
Packit |
f0b94e |
/* Any copyright is dedicated to the Public Domain.
|
|
Packit |
f0b94e |
http://creativecommons.org/publicdomain/zero/1.0/ */
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
ChromeUtils.import("resource://services-sync/constants.js");
|
|
Packit |
f0b94e |
ChromeUtils.import("resource://services-sync/engines.js");
|
|
Packit |
f0b94e |
ChromeUtils.import("resource://services-sync/policies.js");
|
|
Packit |
f0b94e |
ChromeUtils.import("resource://services-sync/record.js");
|
|
Packit |
f0b94e |
ChromeUtils.import("resource://services-sync/service.js");
|
|
Packit |
f0b94e |
ChromeUtils.import("resource://services-sync/status.js");
|
|
Packit |
f0b94e |
ChromeUtils.import("resource://services-sync/util.js");
|
|
Packit |
f0b94e |
ChromeUtils.import("resource://testing-common/services/sync/fakeservices.js");
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
var engineManager = Service.engineManager;
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
function CatapultEngine() {
|
|
Packit |
f0b94e |
SyncEngine.call(this, "Catapult", Service);
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
CatapultEngine.prototype = {
|
|
Packit |
f0b94e |
__proto__: SyncEngine.prototype,
|
|
Packit |
f0b94e |
exception: null, // tests fill this in
|
|
Packit |
f0b94e |
async _sync() {
|
|
Packit |
f0b94e |
throw this.exception;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
};
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
async function sync_httpd_setup() {
|
|
Packit |
f0b94e |
let collectionsHelper = track_collections_helper();
|
|
Packit |
f0b94e |
let upd = collectionsHelper.with_updated_collection;
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
let catapultEngine = engineManager.get("catapult");
|
|
Packit |
f0b94e |
let engines = {catapult: {version: catapultEngine.version,
|
|
Packit |
f0b94e |
syncID: catapultEngine.syncID}};
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// Track these using the collections helper, which keeps modified times
|
|
Packit |
f0b94e |
// up-to-date.
|
|
Packit |
f0b94e |
let clientsColl = new ServerCollection({}, true);
|
|
Packit |
f0b94e |
let keysWBO = new ServerWBO("keys");
|
|
Packit |
f0b94e |
let globalWBO = new ServerWBO("global", {storageVersion: STORAGE_VERSION,
|
|
Packit |
f0b94e |
syncID: Utils.makeGUID(),
|
|
Packit |
f0b94e |
engines});
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
let handlers = {
|
|
Packit |
f0b94e |
"/1.1/johndoe/info/collections": collectionsHelper.handler,
|
|
Packit |
f0b94e |
"/1.1/johndoe/storage/meta/global": upd("meta", globalWBO.handler()),
|
|
Packit |
f0b94e |
"/1.1/johndoe/storage/clients": upd("clients", clientsColl.handler()),
|
|
Packit |
f0b94e |
"/1.1/johndoe/storage/crypto/keys": upd("crypto", keysWBO.handler())
|
|
Packit |
f0b94e |
};
|
|
Packit |
f0b94e |
return httpd_setup(handlers);
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
async function setUp(server) {
|
|
Packit |
f0b94e |
await configureIdentity({username: "johndoe"}, server);
|
|
Packit |
f0b94e |
new FakeCryptoService();
|
|
Packit |
f0b94e |
syncTestLogging();
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
async function generateAndUploadKeys(server) {
|
|
Packit |
f0b94e |
await generateNewKeys(Service.collectionKeys);
|
|
Packit |
f0b94e |
let serverKeys = Service.collectionKeys.asWBO("crypto", "keys");
|
|
Packit |
f0b94e |
await serverKeys.encrypt(Service.identity.syncKeyBundle);
|
|
Packit |
f0b94e |
let res = Service.resource(server.baseURI + "/1.1/johndoe/storage/crypto/keys");
|
|
Packit |
f0b94e |
return (await serverKeys.upload(res)).success;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
add_task(async function setup() {
|
|
Packit |
f0b94e |
await engineManager.clear();
|
|
Packit |
f0b94e |
validate_all_future_pings();
|
|
Packit |
f0b94e |
await engineManager.register(CatapultEngine);
|
|
Packit |
f0b94e |
});
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
add_task(async function test_backoff500() {
|
|
Packit |
f0b94e |
enableValidationPrefs();
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
_("Test: HTTP 500 sets backoff status.");
|
|
Packit |
f0b94e |
let server = await sync_httpd_setup();
|
|
Packit |
f0b94e |
await setUp(server);
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
let engine = engineManager.get("catapult");
|
|
Packit |
f0b94e |
engine.enabled = true;
|
|
Packit |
f0b94e |
engine.exception = {status: 500};
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
try {
|
|
Packit |
f0b94e |
Assert.ok(!Status.enforceBackoff);
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// Forcibly create and upload keys here -- otherwise we don't get to the 500!
|
|
Packit |
f0b94e |
Assert.ok(await generateAndUploadKeys(server));
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
await Service.login();
|
|
Packit |
f0b94e |
await Service.sync();
|
|
Packit |
f0b94e |
Assert.ok(Status.enforceBackoff);
|
|
Packit |
f0b94e |
Assert.equal(Status.sync, SYNC_SUCCEEDED);
|
|
Packit |
f0b94e |
Assert.equal(Status.service, SYNC_FAILED_PARTIAL);
|
|
Packit |
f0b94e |
} finally {
|
|
Packit |
f0b94e |
Status.resetBackoff();
|
|
Packit |
f0b94e |
await Service.startOver();
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
await promiseStopServer(server);
|
|
Packit |
f0b94e |
});
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
add_task(async function test_backoff503() {
|
|
Packit |
f0b94e |
enableValidationPrefs();
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
_("Test: HTTP 503 with Retry-After header leads to backoff notification and sets backoff status.");
|
|
Packit |
f0b94e |
let server = await sync_httpd_setup();
|
|
Packit |
f0b94e |
await setUp(server);
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
const BACKOFF = 42;
|
|
Packit |
f0b94e |
let engine = engineManager.get("catapult");
|
|
Packit |
f0b94e |
engine.enabled = true;
|
|
Packit |
f0b94e |
engine.exception = {status: 503,
|
|
Packit |
f0b94e |
headers: {"retry-after": BACKOFF}};
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
let backoffInterval;
|
|
Packit |
f0b94e |
Svc.Obs.add("weave:service:backoff:interval", function(subject) {
|
|
Packit |
f0b94e |
backoffInterval = subject;
|
|
Packit |
f0b94e |
});
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
try {
|
|
Packit |
f0b94e |
Assert.ok(!Status.enforceBackoff);
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
Assert.ok(await generateAndUploadKeys(server));
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
await Service.login();
|
|
Packit |
f0b94e |
await Service.sync();
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
Assert.ok(Status.enforceBackoff);
|
|
Packit |
f0b94e |
Assert.equal(backoffInterval, BACKOFF);
|
|
Packit |
f0b94e |
Assert.equal(Status.service, SYNC_FAILED_PARTIAL);
|
|
Packit |
f0b94e |
Assert.equal(Status.sync, SERVER_MAINTENANCE);
|
|
Packit |
f0b94e |
} finally {
|
|
Packit |
f0b94e |
Status.resetBackoff();
|
|
Packit |
f0b94e |
Status.resetSync();
|
|
Packit |
f0b94e |
await Service.startOver();
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
await promiseStopServer(server);
|
|
Packit |
f0b94e |
});
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
add_task(async function test_overQuota() {
|
|
Packit |
f0b94e |
enableValidationPrefs();
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
_("Test: HTTP 400 with body error code 14 means over quota.");
|
|
Packit |
f0b94e |
let server = await sync_httpd_setup();
|
|
Packit |
f0b94e |
await setUp(server);
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
let engine = engineManager.get("catapult");
|
|
Packit |
f0b94e |
engine.enabled = true;
|
|
Packit |
f0b94e |
engine.exception = {status: 400,
|
|
Packit |
f0b94e |
toString() {
|
|
Packit |
f0b94e |
return "14";
|
|
Packit |
f0b94e |
}};
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
try {
|
|
Packit |
f0b94e |
Assert.equal(Status.sync, SYNC_SUCCEEDED);
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
Assert.ok(await generateAndUploadKeys(server));
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
await Service.login();
|
|
Packit |
f0b94e |
await Service.sync();
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
Assert.equal(Status.sync, OVER_QUOTA);
|
|
Packit |
f0b94e |
Assert.equal(Status.service, SYNC_FAILED_PARTIAL);
|
|
Packit |
f0b94e |
} finally {
|
|
Packit |
f0b94e |
Status.resetSync();
|
|
Packit |
f0b94e |
await Service.startOver();
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
await promiseStopServer(server);
|
|
Packit |
f0b94e |
});
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
add_task(async function test_service_networkError() {
|
|
Packit |
f0b94e |
enableValidationPrefs();
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
_("Test: Connection refused error from Service.sync() leads to the right status code.");
|
|
Packit |
f0b94e |
let server = await sync_httpd_setup();
|
|
Packit |
f0b94e |
await setUp(server);
|
|
Packit |
f0b94e |
await promiseStopServer(server);
|
|
Packit |
f0b94e |
// Provoke connection refused.
|
|
Packit |
f0b94e |
Service.clusterURL = "http://localhost:12345/";
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
try {
|
|
Packit |
f0b94e |
Assert.equal(Status.sync, SYNC_SUCCEEDED);
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
Service._loggedIn = true;
|
|
Packit |
f0b94e |
await Service.sync();
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
Assert.equal(Status.sync, LOGIN_FAILED_NETWORK_ERROR);
|
|
Packit |
f0b94e |
Assert.equal(Status.service, SYNC_FAILED);
|
|
Packit |
f0b94e |
} finally {
|
|
Packit |
f0b94e |
Status.resetSync();
|
|
Packit |
f0b94e |
await Service.startOver();
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
});
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
add_task(async function test_service_offline() {
|
|
Packit |
f0b94e |
enableValidationPrefs();
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
_("Test: Wanting to sync in offline mode leads to the right status code but does not increment the ignorable error count.");
|
|
Packit |
f0b94e |
let server = await sync_httpd_setup();
|
|
Packit |
f0b94e |
await setUp(server);
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
await promiseStopServer(server);
|
|
Packit |
f0b94e |
Services.io.offline = true;
|
|
Packit |
f0b94e |
Services.prefs.setBoolPref("network.dns.offline-localhost", false);
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
try {
|
|
Packit |
f0b94e |
Assert.equal(Status.sync, SYNC_SUCCEEDED);
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
Service._loggedIn = true;
|
|
Packit |
f0b94e |
await Service.sync();
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
Assert.equal(Status.sync, LOGIN_FAILED_NETWORK_ERROR);
|
|
Packit |
f0b94e |
Assert.equal(Status.service, SYNC_FAILED);
|
|
Packit |
f0b94e |
} finally {
|
|
Packit |
f0b94e |
Status.resetSync();
|
|
Packit |
f0b94e |
await Service.startOver();
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
Services.io.offline = false;
|
|
Packit |
f0b94e |
Services.prefs.clearUserPref("network.dns.offline-localhost");
|
|
Packit |
f0b94e |
});
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
add_task(async function test_engine_networkError() {
|
|
Packit |
f0b94e |
enableValidationPrefs();
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
_("Test: Network related exceptions from engine.sync() lead to the right status code.");
|
|
Packit |
f0b94e |
let server = await sync_httpd_setup();
|
|
Packit |
f0b94e |
await setUp(server);
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
let engine = engineManager.get("catapult");
|
|
Packit |
f0b94e |
engine.enabled = true;
|
|
Packit |
f0b94e |
engine.exception = Components.Exception("NS_ERROR_UNKNOWN_HOST",
|
|
Packit |
f0b94e |
Cr.NS_ERROR_UNKNOWN_HOST);
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
try {
|
|
Packit |
f0b94e |
Assert.equal(Status.sync, SYNC_SUCCEEDED);
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
Assert.ok(await generateAndUploadKeys(server));
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
await Service.login();
|
|
Packit |
f0b94e |
await Service.sync();
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
Assert.equal(Status.sync, LOGIN_FAILED_NETWORK_ERROR);
|
|
Packit |
f0b94e |
Assert.equal(Status.service, SYNC_FAILED_PARTIAL);
|
|
Packit |
f0b94e |
} finally {
|
|
Packit |
f0b94e |
Status.resetSync();
|
|
Packit |
f0b94e |
await Service.startOver();
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
await promiseStopServer(server);
|
|
Packit |
f0b94e |
});
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
add_task(async function test_resource_timeout() {
|
|
Packit |
f0b94e |
enableValidationPrefs();
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
let server = await sync_httpd_setup();
|
|
Packit |
f0b94e |
await setUp(server);
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
let engine = engineManager.get("catapult");
|
|
Packit |
f0b94e |
engine.enabled = true;
|
|
Packit |
f0b94e |
// Resource throws this when it encounters a timeout.
|
|
Packit |
f0b94e |
engine.exception = Components.Exception("Aborting due to channel inactivity.",
|
|
Packit |
f0b94e |
Cr.NS_ERROR_NET_TIMEOUT);
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
try {
|
|
Packit |
f0b94e |
Assert.equal(Status.sync, SYNC_SUCCEEDED);
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
Assert.ok(await generateAndUploadKeys(server));
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
await Service.login();
|
|
Packit |
f0b94e |
await Service.sync();
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
Assert.equal(Status.sync, LOGIN_FAILED_NETWORK_ERROR);
|
|
Packit |
f0b94e |
Assert.equal(Status.service, SYNC_FAILED_PARTIAL);
|
|
Packit |
f0b94e |
} finally {
|
|
Packit |
f0b94e |
Status.resetSync();
|
|
Packit |
f0b94e |
await Service.startOver();
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
await promiseStopServer(server);
|
|
Packit |
f0b94e |
});
|