Source code

Revision control

Copy as Markdown

Other Tools

Test Info:

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