mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-10-16 20:27:31 +09:00
Import Tycho weave client
This commit is contained in:
parent
e4ac0e17f8
commit
c53787dfdb
183 changed files with 7272 additions and 16759 deletions
|
|
@ -12,7 +12,7 @@
|
|||
<application_id>1</application_id>
|
||||
<min_version>3.6</min_version>
|
||||
<max_version>*</max_version>
|
||||
<appID>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</appID>
|
||||
<appID>{8de7fcbb-c55c-4fbe-bfc5-fc555c87dbc4}</appID>
|
||||
</application></compatible_applications>
|
||||
<all_compatible_os><os>ALL</os></all_compatible_os>
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
<application_id>1</application_id>
|
||||
<min_version>3.6</min_version>
|
||||
<max_version>*</max_version>
|
||||
<appID>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</appID>
|
||||
<appID>{8de7fcbb-c55c-4fbe-bfc5-fc555c87dbc4}</appID>
|
||||
</application></compatible_applications>
|
||||
<all_compatible_os><os>ALL</os></all_compatible_os>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
{ "tests": [
|
||||
"test_bookmark_conflict.js",
|
||||
"test_sync.js",
|
||||
"test_prefs.js",
|
||||
"test_tabs.js",
|
||||
|
|
@ -17,6 +16,7 @@
|
|||
"test_bug575423.js",
|
||||
"test_bug546807.js",
|
||||
"test_history_collision.js",
|
||||
"test_privbrw_formdata.js",
|
||||
"test_privbrw_passwords.js",
|
||||
"test_privbrw_tabs.js",
|
||||
"test_bookmarks_in_same_named_folder.js",
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
// syncs between profiles.
|
||||
EnableEngines(["addons"]);
|
||||
|
||||
var phases = {
|
||||
let phases = {
|
||||
"phase01": "profile1",
|
||||
"phase02": "profile1",
|
||||
"phase03": "profile2",
|
||||
|
|
@ -33,8 +33,7 @@ Phase("phase01", [
|
|||
[Sync]
|
||||
]);
|
||||
Phase("phase02", [
|
||||
[Addons.verify, [id], STATE_ENABLED],
|
||||
[Sync]
|
||||
[Addons.verify, [id], STATE_ENABLED]
|
||||
]);
|
||||
Phase("phase03", [
|
||||
[Addons.verifyNot, [id]],
|
||||
|
|
@ -42,7 +41,6 @@ Phase("phase03", [
|
|||
]);
|
||||
Phase("phase04", [
|
||||
[Addons.verify, [id], STATE_ENABLED],
|
||||
[Sync]
|
||||
]);
|
||||
|
||||
// Now we disable the add-on
|
||||
|
|
@ -53,15 +51,13 @@ Phase("phase05", [
|
|||
]);
|
||||
Phase("phase06", [
|
||||
[Addons.verify, [id], STATE_DISABLED],
|
||||
[Sync]
|
||||
]);
|
||||
Phase("phase07", [
|
||||
[Addons.verify, [id], STATE_ENABLED],
|
||||
[Sync]
|
||||
]);
|
||||
Phase("phase08", [
|
||||
[Addons.verify, [id], STATE_DISABLED],
|
||||
[Sync]
|
||||
[Addons.verify, [id], STATE_DISABLED]
|
||||
]);
|
||||
|
||||
// Now we re-enable it again.
|
||||
|
|
@ -72,15 +68,13 @@ Phase("phase09", [
|
|||
]);
|
||||
Phase("phase10", [
|
||||
[Addons.verify, [id], STATE_ENABLED],
|
||||
[Sync]
|
||||
]);
|
||||
Phase("phase11", [
|
||||
[Addons.verify, [id], STATE_DISABLED],
|
||||
[Sync]
|
||||
]);
|
||||
Phase("phase12", [
|
||||
[Addons.verify, [id], STATE_ENABLED],
|
||||
[Sync]
|
||||
[Addons.verify, [id], STATE_ENABLED]
|
||||
]);
|
||||
|
||||
// And we uninstall it
|
||||
|
|
@ -92,14 +86,12 @@ Phase("phase13", [
|
|||
[Sync]
|
||||
]);
|
||||
Phase("phase14", [
|
||||
[Addons.verifyNot, [id]],
|
||||
[Sync]
|
||||
[Addons.verifyNot, [id]]
|
||||
]);
|
||||
Phase("phase15", [
|
||||
[Addons.verify, [id], STATE_ENABLED],
|
||||
[Sync]
|
||||
]);
|
||||
Phase("phase16", [
|
||||
[Addons.verifyNot, [id]],
|
||||
[Sync]
|
||||
[Addons.verifyNot, [id]]
|
||||
]);
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
// the proper action is taken.
|
||||
EnableEngines(["addons"]);
|
||||
|
||||
var phases = {
|
||||
let phases = {
|
||||
"phase01": "profile1",
|
||||
"phase02": "profile2",
|
||||
"phase03": "profile1",
|
||||
|
|
@ -34,9 +34,6 @@ Phase("phase02", [
|
|||
Phase("phase03", [
|
||||
[Sync], // Get GUID updates, potentially.
|
||||
[Addons.setEnabled, [id], STATE_DISABLED],
|
||||
// We've changed the state, but don't want this profile to sync until phase5,
|
||||
// so if we ran a validation now we'd be expecting to find errors.
|
||||
[Addons.skipValidation]
|
||||
]);
|
||||
Phase("phase04", [
|
||||
[EnsureTracking],
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
// other profiles.
|
||||
EnableEngines(["addons"]);
|
||||
|
||||
var phases = {
|
||||
let phases = {
|
||||
"phase01": "profile1",
|
||||
"phase02": "profile2",
|
||||
"phase03": "profile1",
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
EnableEngines(["addons"]);
|
||||
|
||||
var phases = { "phase1": "profile1",
|
||||
let phases = { "phase1": "profile1",
|
||||
"phase2": "profile1" };
|
||||
|
||||
const id = "unsigned-xpi@tests.mozilla.org";
|
||||
|
|
@ -25,6 +25,5 @@ Phase("phase1", [
|
|||
|
||||
Phase("phase2", [
|
||||
// Add-on should be present after restart
|
||||
[Addons.verify, [id], STATE_ENABLED],
|
||||
[Sync] // Sync to ensure everything is initialized enough for the addon validator to run
|
||||
[Addons.verify, [id], STATE_ENABLED]
|
||||
]);
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
EnableEngines(["addons"]);
|
||||
|
||||
var phases = {
|
||||
let phases = {
|
||||
"phase01": "profile1",
|
||||
"phase02": "profile1",
|
||||
"phase03": "profile1"
|
||||
|
|
@ -30,6 +30,5 @@ Phase("phase02", [
|
|||
]);
|
||||
Phase("phase03", [
|
||||
[Addons.verify, [id1], STATE_ENABLED],
|
||||
[Addons.verify, [id2], STATE_ENABLED],
|
||||
[Sync] // Sync to ensure that the addon validator can run without error
|
||||
[Addons.verify, [id2], STATE_ENABLED]
|
||||
]);
|
||||
|
|
|
|||
|
|
@ -1,143 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/*
|
||||
* The list of phases mapped to their corresponding profiles. The object
|
||||
* here must be in strict JSON format, as it will get parsed by the Python
|
||||
* testrunner (no single quotes, extra comma's, etc).
|
||||
*/
|
||||
EnableEngines(["bookmarks"]);
|
||||
|
||||
var phases = { "phase1": "profile1",
|
||||
"phase2": "profile2",
|
||||
"phase3": "profile1",
|
||||
"phase4": "profile2" };
|
||||
|
||||
|
||||
// the initial list of bookmarks to add to the browser
|
||||
var bookmarksInitial = {
|
||||
"menu": [
|
||||
{ folder: "foldera" },
|
||||
{ folder: "folderb" },
|
||||
{ folder: "folderc" },
|
||||
{ folder: "folderd" },
|
||||
],
|
||||
|
||||
"menu/foldera": [{ uri: "http://www.cnn.com", title: "CNN" }],
|
||||
"menu/folderb": [{ uri: "http://www.apple.com", title: "Apple", tags: [] }],
|
||||
"menu/folderc": [{ uri: "http://www.yahoo.com", title: "Yahoo" }],
|
||||
|
||||
"menu/folderd": []
|
||||
};
|
||||
|
||||
// a list of bookmarks to delete during a 'delete' action on P2
|
||||
var bookmarksToDelete = {
|
||||
"menu": [
|
||||
{ folder: "foldera" },
|
||||
{ folder: "folderb" },
|
||||
],
|
||||
"menu/folderc": [{ uri: "http://www.yahoo.com", title: "Yahoo" }],
|
||||
};
|
||||
|
||||
|
||||
// the modifications to make on P1, after P2 has synced, but before P1 has gotten
|
||||
// P2's changes
|
||||
var bookmarkMods = {
|
||||
"menu": [
|
||||
{ folder: "foldera" },
|
||||
{ folder: "folderb" },
|
||||
{ folder: "folderc" },
|
||||
{ folder: "folderd" },
|
||||
],
|
||||
|
||||
// we move this child out of its folder (p1), after deleting the folder (p2)
|
||||
// and expect the child to come back to p2 after sync.
|
||||
"menu/foldera": [{
|
||||
uri: "http://www.cnn.com",
|
||||
title: "CNN",
|
||||
changes: { location: "menu/folderd" }
|
||||
}],
|
||||
|
||||
// we rename this child (p1) after deleting the folder (p2), and expect the child
|
||||
// to be moved into great grandparent (menu)
|
||||
"menu/folderb": [{
|
||||
uri: "http://www.apple.com",
|
||||
title: "Apple",
|
||||
tags: [],
|
||||
changes: { title: "Mac" }
|
||||
}],
|
||||
|
||||
|
||||
// we move this child (p1) after deleting the child (p2) and expect it to survive
|
||||
"menu/folderc": [{
|
||||
uri: "http://www.yahoo.com",
|
||||
title: "Yahoo",
|
||||
changes: { location: "menu/folderd" }
|
||||
}],
|
||||
|
||||
"menu/folderd": []
|
||||
};
|
||||
|
||||
// a list of bookmarks to delete during a 'delete' action
|
||||
var bookmarksToDelete = {
|
||||
"menu": [
|
||||
{ folder: "foldera" },
|
||||
{ folder: "folderb" },
|
||||
],
|
||||
"menu/folderc": [
|
||||
{ uri: "http://www.yahoo.com", title: "Yahoo" },
|
||||
],
|
||||
};
|
||||
|
||||
|
||||
|
||||
// expected bookmark state after conflict resolution
|
||||
var bookmarksExpected = {
|
||||
"menu": [
|
||||
{ folder: "folderc" },
|
||||
{ folder: "folderd" },
|
||||
{ uri: "http://www.apple.com", title: "Mac", },
|
||||
],
|
||||
|
||||
"menu/folderc": [],
|
||||
|
||||
"menu/folderd": [
|
||||
{ uri: "http://www.cnn.com", title: "CNN" },
|
||||
{ uri: "http://www.yahoo.com", title: "Yahoo" }
|
||||
]
|
||||
};
|
||||
|
||||
// Add bookmarks to profile1 and sync.
|
||||
Phase("phase1", [
|
||||
[Bookmarks.add, bookmarksInitial],
|
||||
[Bookmarks.verify, bookmarksInitial],
|
||||
[Sync],
|
||||
[Bookmarks.verify, bookmarksInitial],
|
||||
]);
|
||||
|
||||
// Sync to profile2 and verify that the bookmarks are present. Delete
|
||||
// bookmarks/folders, verify that it's not present, and sync
|
||||
Phase("phase2", [
|
||||
[Sync],
|
||||
[Bookmarks.verify, bookmarksInitial],
|
||||
[Bookmarks.delete, bookmarksToDelete],
|
||||
[Bookmarks.verifyNot, bookmarksToDelete],
|
||||
[Sync]
|
||||
]);
|
||||
|
||||
// Using profile1, modify the bookmarks, and sync *after* the modification,
|
||||
// and then sync again to propagate the reconciliation changes.
|
||||
Phase("phase3", [
|
||||
[Bookmarks.verify, bookmarksInitial],
|
||||
[Bookmarks.modify, bookmarkMods],
|
||||
[Sync],
|
||||
[Bookmarks.verify, bookmarksExpected],
|
||||
[Bookmarks.verifyNot, bookmarksToDelete],
|
||||
]);
|
||||
|
||||
// Back in profile2, do a sync and verify that we're in the expected state
|
||||
Phase("phase4", [
|
||||
[Sync],
|
||||
[Bookmarks.verify, bookmarksExpected],
|
||||
[Bookmarks.verifyNot, bookmarksToDelete],
|
||||
]);
|
||||
|
|
@ -23,7 +23,7 @@ var prefs1 = [
|
|||
{ name: "browser.urlbar.maxRichResults",
|
||||
value: 20
|
||||
},
|
||||
{ name: "privacy.clearOnShutdown.siteSettings",
|
||||
{ name: "security.OCSP.require",
|
||||
value: true
|
||||
}
|
||||
];
|
||||
|
|
@ -35,7 +35,7 @@ var prefs2 = [
|
|||
{ name: "browser.urlbar.maxRichResults",
|
||||
value: 18
|
||||
},
|
||||
{ name: "privacy.clearOnShutdown.siteSettings",
|
||||
{ name: "security.OCSP.require",
|
||||
value: false
|
||||
}
|
||||
];
|
||||
|
|
|
|||
|
|
@ -88,8 +88,7 @@ Phase('phase2', [
|
|||
[Sync],
|
||||
[Bookmarks.verify, bookmarks_initial],
|
||||
[Bookmarks.delete, bookmarks_to_delete],
|
||||
[Bookmarks.verifyNot, bookmarks_to_delete],
|
||||
[Bookmarks.skipValidation]
|
||||
[Bookmarks.verifyNot, bookmarks_to_delete]
|
||||
]);
|
||||
|
||||
// Using profile1, sync again with wipe-server set to true. Verify our
|
||||
|
|
|
|||
|
|
@ -108,7 +108,7 @@ var prefs1 = [
|
|||
{ name: "browser.urlbar.maxRichResults",
|
||||
value: 20
|
||||
},
|
||||
{ name: "privacy.clearOnShutdown.siteSettings",
|
||||
{ name: "security.OCSP.require",
|
||||
value: true
|
||||
}
|
||||
];
|
||||
|
|
@ -120,7 +120,7 @@ var prefs2 = [
|
|||
{ name: "browser.urlbar.maxRichResults",
|
||||
value: 18
|
||||
},
|
||||
{ name: "privacy.clearOnShutdown.siteSettings",
|
||||
{ name: "security.OCSP.require",
|
||||
value: false
|
||||
}
|
||||
];
|
||||
|
|
|
|||
|
|
@ -31,11 +31,6 @@ var formdata1 = [
|
|||
}
|
||||
];
|
||||
|
||||
// This is currently pointless - it *looks* like it is trying to check that
|
||||
// one of the entries in formdata1 has been removed, but (a) the delete code
|
||||
// isn't active (see comments below), and (b) the way the verification works
|
||||
// means it would never do the right thing - it only checks all the entries
|
||||
// here exist, but not that they are the only entries in the DB.
|
||||
var formdata2 = [
|
||||
{ fieldname: "testing",
|
||||
value: "success",
|
||||
|
|
@ -52,11 +47,6 @@ var formdata_delete = [
|
|||
}
|
||||
];
|
||||
|
||||
var formdata_new = [
|
||||
{ fieldname: "new-field",
|
||||
value: "new-value"
|
||||
}
|
||||
]
|
||||
/*
|
||||
* Test phases
|
||||
*/
|
||||
|
|
@ -82,15 +72,12 @@ Phase('phase3', [
|
|||
[Formdata.delete, formdata_delete],
|
||||
//[Formdata.verifyNot, formdata_delete],
|
||||
[Formdata.verify, formdata2],
|
||||
// add new data after the first Sync, ensuring the tracker works.
|
||||
[Formdata.add, formdata_new],
|
||||
[Sync],
|
||||
]);
|
||||
|
||||
Phase('phase4', [
|
||||
[Sync],
|
||||
[Formdata.verify, formdata2],
|
||||
[Formdata.verify, formdata_new],
|
||||
//[Formdata.verifyNot, formdata_delete]
|
||||
]);
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ var prefs1 = [
|
|||
{ name: "browser.urlbar.maxRichResults",
|
||||
value: 20
|
||||
},
|
||||
{ name: "privacy.clearOnShutdown.siteSettings",
|
||||
{ name: "security.OCSP.require",
|
||||
value: true
|
||||
}
|
||||
];
|
||||
|
|
@ -31,7 +31,7 @@ var prefs2 = [
|
|||
{ name: "browser.urlbar.maxRichResults",
|
||||
value: 18
|
||||
},
|
||||
{ name: "privacy.clearOnShutdown.siteSettings",
|
||||
{ name: "security.OCSP.require",
|
||||
value: false
|
||||
}
|
||||
];
|
||||
|
|
|
|||
73
services/sync/tests/tps/test_privbrw_formdata.js
Normal file
73
services/sync/tests/tps/test_privbrw_formdata.js
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/*
|
||||
* The list of phases mapped to their corresponding profiles. The object
|
||||
* here must be in strict JSON format, as it will get parsed by the Python
|
||||
* testrunner (no single quotes, extra comma's, etc).
|
||||
*/
|
||||
EnableEngines(["forms"]);
|
||||
|
||||
var phases = { "phase1": "profile1",
|
||||
"phase2": "profile2",
|
||||
"phase3": "profile1",
|
||||
"phase4": "profile2" };
|
||||
|
||||
/*
|
||||
* Form data
|
||||
*/
|
||||
|
||||
// the form data to add to the browser
|
||||
var formdata1 = [
|
||||
{ fieldname: "name",
|
||||
value: "xyz",
|
||||
date: -1
|
||||
},
|
||||
{ fieldname: "email",
|
||||
value: "abc@gmail.com",
|
||||
date: -2
|
||||
},
|
||||
{ fieldname: "username",
|
||||
value: "joe"
|
||||
}
|
||||
];
|
||||
|
||||
// the form data to add in private browsing mode
|
||||
var formdata2 = [
|
||||
{ fieldname: "password",
|
||||
value: "secret",
|
||||
date: -1
|
||||
},
|
||||
{ fieldname: "city",
|
||||
value: "mtview"
|
||||
}
|
||||
];
|
||||
|
||||
/*
|
||||
* Test phases
|
||||
*/
|
||||
|
||||
Phase('phase1', [
|
||||
[Formdata.add, formdata1],
|
||||
[Formdata.verify, formdata1],
|
||||
[Sync]
|
||||
]);
|
||||
|
||||
Phase('phase2', [
|
||||
[Sync],
|
||||
[Formdata.verify, formdata1]
|
||||
]);
|
||||
|
||||
Phase('phase3', [
|
||||
[Sync],
|
||||
[Windows.add, { private: true }],
|
||||
[Formdata.add, formdata2],
|
||||
[Formdata.verify, formdata2],
|
||||
[Sync],
|
||||
]);
|
||||
|
||||
Phase('phase4', [
|
||||
[Sync],
|
||||
[Formdata.verify, formdata1],
|
||||
[Formdata.verifyNot, formdata2]
|
||||
]);
|
||||
|
|
@ -4,7 +4,7 @@ Cu.import("resource://services-sync/util.js");
|
|||
// Fake Sample Data
|
||||
// ----------------------------------------
|
||||
|
||||
var fakeSampleLogins = [
|
||||
let fakeSampleLogins = [
|
||||
// Fake nsILoginInfo object.
|
||||
{hostname: "www.boogle.com",
|
||||
formSubmitURL: "http://www.boogle.com/search",
|
||||
|
|
|
|||
|
|
@ -1,54 +1,65 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
var {classes: Cc, interfaces: Ci, results: Cr, utils: Cu} = Components;
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
const {classes: Cc, interfaces: Ci, results: Cr, utils: Cu} = Components;
|
||||
|
||||
var gSyncProfile;
|
||||
let gSyncProfile;
|
||||
|
||||
gSyncProfile = do_get_profile();
|
||||
|
||||
// Init FormHistoryStartup and pretend we opened a profile.
|
||||
var fhs = Cc["@mozilla.org/satchel/form-history-startup;1"]
|
||||
let fhs = Cc["@mozilla.org/satchel/form-history-startup;1"]
|
||||
.getService(Ci.nsIObserver);
|
||||
fhs.observe(null, "profile-after-change", null);
|
||||
|
||||
// An app is going to have some prefs set which xpcshell tests don't.
|
||||
Services.prefs.setCharPref("identity.sync.tokenserver.uri", "http://token-server");
|
||||
|
||||
// Set the validation prefs to attempt validation every time to avoid non-determinism.
|
||||
Services.prefs.setIntPref("services.sync.validation.interval", 0);
|
||||
Services.prefs.setIntPref("services.sync.validation.percentageChance", 100);
|
||||
Services.prefs.setIntPref("services.sync.validation.maxRecords", -1);
|
||||
Services.prefs.setBoolPref("services.sync.validation.enabled", true);
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
// Make sure to provide the right OS so crypto loads the right binaries
|
||||
function getOS() {
|
||||
switch (mozinfo.os) {
|
||||
case "win":
|
||||
return "WINNT";
|
||||
case "mac":
|
||||
return "Darwin";
|
||||
default:
|
||||
return "Linux";
|
||||
}
|
||||
}
|
||||
let OS = "XPCShell";
|
||||
if ("@mozilla.org/windows-registry-key;1" in Cc)
|
||||
OS = "WINNT";
|
||||
else if ("nsILocalFileMac" in Ci)
|
||||
OS = "Darwin";
|
||||
else
|
||||
OS = "Linux";
|
||||
|
||||
Cu.import("resource://testing-common/AppInfo.jsm", this);
|
||||
updateAppInfo({
|
||||
let XULAppInfo = {
|
||||
vendor: "Mozilla",
|
||||
name: "XPCShell",
|
||||
ID: "xpcshell@tests.mozilla.org",
|
||||
version: "1",
|
||||
appBuildID: "20100621",
|
||||
platformVersion: "",
|
||||
OS: getOS(),
|
||||
});
|
||||
platformBuildID: "20100621",
|
||||
inSafeMode: false,
|
||||
logConsoleErrors: true,
|
||||
OS: OS,
|
||||
XPCOMABI: "noarch-spidermonkey",
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIXULAppInfo, Ci.nsIXULRuntime]),
|
||||
invalidateCachesOnRestart: function invalidateCachesOnRestart() { }
|
||||
};
|
||||
|
||||
let XULAppInfoFactory = {
|
||||
createInstance: function (outer, iid) {
|
||||
if (outer != null)
|
||||
throw Cr.NS_ERROR_NO_AGGREGATION;
|
||||
return XULAppInfo.QueryInterface(iid);
|
||||
}
|
||||
};
|
||||
|
||||
let registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
|
||||
registrar.registerFactory(Components.ID("{fbfae60b-64a4-44ef-a911-08ceb70b9f31}"),
|
||||
"XULAppInfo", "@mozilla.org/xre/app-info;1",
|
||||
XULAppInfoFactory);
|
||||
|
||||
|
||||
// Register resource aliases. Normally done in SyncComponents.manifest.
|
||||
function addResourceAlias() {
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
const resProt = Services.io.getProtocolHandler("resource")
|
||||
.QueryInterface(Ci.nsIResProtocolHandler);
|
||||
for (let s of ["common", "sync", "crypto"]) {
|
||||
for each (let s in ["common", "sync", "crypto"]) {
|
||||
let uri = Services.io.newURI("resource://gre/modules/services-" + s + "/", null,
|
||||
null);
|
||||
resProt.setSubstitution("services-" + s, uri);
|
||||
|
|
|
|||
|
|
@ -1,112 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
Cu.import("resource://services-sync/engines.js");
|
||||
|
||||
// Common code for test_errorhandler_{1,2}.js -- pulled out to make it less
|
||||
// monolithic and take less time to execute.
|
||||
const EHTestsCommon = {
|
||||
|
||||
service_unavailable(request, response) {
|
||||
let body = "Service Unavailable";
|
||||
response.setStatusLine(request.httpVersion, 503, "Service Unavailable");
|
||||
response.setHeader("Retry-After", "42");
|
||||
response.bodyOutputStream.write(body, body.length);
|
||||
},
|
||||
|
||||
sync_httpd_setup() {
|
||||
let global = new ServerWBO("global", {
|
||||
syncID: Service.syncID,
|
||||
storageVersion: STORAGE_VERSION,
|
||||
engines: {clients: {version: Service.clientsEngine.version,
|
||||
syncID: Service.clientsEngine.syncID},
|
||||
catapult: {version: Service.engineManager.get("catapult").version,
|
||||
syncID: Service.engineManager.get("catapult").syncID}}
|
||||
});
|
||||
let clientsColl = new ServerCollection({}, true);
|
||||
|
||||
// Tracking info/collections.
|
||||
let collectionsHelper = track_collections_helper();
|
||||
let upd = collectionsHelper.with_updated_collection;
|
||||
|
||||
let handler_401 = httpd_handler(401, "Unauthorized");
|
||||
return httpd_setup({
|
||||
// Normal server behaviour.
|
||||
"/1.1/johndoe/storage/meta/global": upd("meta", global.handler()),
|
||||
"/1.1/johndoe/info/collections": collectionsHelper.handler,
|
||||
"/1.1/johndoe/storage/crypto/keys":
|
||||
upd("crypto", (new ServerWBO("keys")).handler()),
|
||||
"/1.1/johndoe/storage/clients": upd("clients", clientsColl.handler()),
|
||||
|
||||
// Credentials are wrong or node reallocated.
|
||||
"/1.1/janedoe/storage/meta/global": handler_401,
|
||||
"/1.1/janedoe/info/collections": handler_401,
|
||||
|
||||
// Maintenance or overloaded (503 + Retry-After) at info/collections.
|
||||
"/maintenance/1.1/broken.info/info/collections": EHTestsCommon.service_unavailable,
|
||||
|
||||
// Maintenance or overloaded (503 + Retry-After) at meta/global.
|
||||
"/maintenance/1.1/broken.meta/storage/meta/global": EHTestsCommon.service_unavailable,
|
||||
"/maintenance/1.1/broken.meta/info/collections": collectionsHelper.handler,
|
||||
|
||||
// Maintenance or overloaded (503 + Retry-After) at crypto/keys.
|
||||
"/maintenance/1.1/broken.keys/storage/meta/global": upd("meta", global.handler()),
|
||||
"/maintenance/1.1/broken.keys/info/collections": collectionsHelper.handler,
|
||||
"/maintenance/1.1/broken.keys/storage/crypto/keys": EHTestsCommon.service_unavailable,
|
||||
|
||||
// Maintenance or overloaded (503 + Retry-After) at wiping collection.
|
||||
"/maintenance/1.1/broken.wipe/info/collections": collectionsHelper.handler,
|
||||
"/maintenance/1.1/broken.wipe/storage/meta/global": upd("meta", global.handler()),
|
||||
"/maintenance/1.1/broken.wipe/storage/crypto/keys":
|
||||
upd("crypto", (new ServerWBO("keys")).handler()),
|
||||
"/maintenance/1.1/broken.wipe/storage": EHTestsCommon.service_unavailable,
|
||||
"/maintenance/1.1/broken.wipe/storage/clients": upd("clients", clientsColl.handler()),
|
||||
"/maintenance/1.1/broken.wipe/storage/catapult": EHTestsCommon.service_unavailable
|
||||
});
|
||||
},
|
||||
|
||||
CatapultEngine: (function() {
|
||||
function CatapultEngine() {
|
||||
SyncEngine.call(this, "Catapult", Service);
|
||||
}
|
||||
CatapultEngine.prototype = {
|
||||
__proto__: SyncEngine.prototype,
|
||||
exception: null, // tests fill this in
|
||||
_sync: function _sync() {
|
||||
if (this.exception) {
|
||||
throw this.exception;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return CatapultEngine;
|
||||
}()),
|
||||
|
||||
|
||||
generateCredentialsChangedFailure() {
|
||||
// Make sync fail due to changed credentials. We simply re-encrypt
|
||||
// the keys with a different Sync Key, without changing the local one.
|
||||
let newSyncKeyBundle = new SyncKeyBundle("johndoe", "23456234562345623456234562");
|
||||
let keys = Service.collectionKeys.asWBO();
|
||||
keys.encrypt(newSyncKeyBundle);
|
||||
keys.upload(Service.resource(Service.cryptoKeysURL));
|
||||
},
|
||||
|
||||
setUp(server) {
|
||||
return configureIdentity({ username: "johndoe" }).then(
|
||||
() => {
|
||||
Service.serverURL = server.baseURI + "/";
|
||||
Service.clusterURL = server.baseURI + "/";
|
||||
}
|
||||
).then(
|
||||
() => EHTestsCommon.generateAndUploadKeys()
|
||||
);
|
||||
},
|
||||
|
||||
generateAndUploadKeys() {
|
||||
generateNewKeys(Service.collectionKeys);
|
||||
let serverKeys = Service.collectionKeys.asWBO("crypto", "keys");
|
||||
serverKeys.encrypt(Service.identity.syncKeyBundle);
|
||||
return serverKeys.upload(Service.resource(Service.cryptoKeysURL)).success;
|
||||
}
|
||||
};
|
||||
|
|
@ -4,39 +4,8 @@
|
|||
Cu.import("resource://services-common/async.js");
|
||||
Cu.import("resource://testing-common/services/common/utils.js");
|
||||
Cu.import("resource://testing-common/PlacesTestUtils.jsm");
|
||||
Cu.import("resource://services-sync/util.js");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, 'SyncPingSchema', function() {
|
||||
let ns = {};
|
||||
Cu.import("resource://gre/modules/FileUtils.jsm", ns);
|
||||
let stream = Cc["@mozilla.org/network/file-input-stream;1"]
|
||||
.createInstance(Ci.nsIFileInputStream);
|
||||
let jsonReader = Cc["@mozilla.org/dom/json;1"]
|
||||
.createInstance(Components.interfaces.nsIJSON);
|
||||
let schema;
|
||||
try {
|
||||
let schemaFile = do_get_file("sync_ping_schema.json");
|
||||
stream.init(schemaFile, ns.FileUtils.MODE_RDONLY, ns.FileUtils.PERMS_FILE, 0);
|
||||
schema = jsonReader.decodeFromStream(stream, stream.available());
|
||||
} finally {
|
||||
stream.close();
|
||||
}
|
||||
|
||||
// Allow tests to make whatever engines they want, this shouldn't cause
|
||||
// validation failure.
|
||||
schema.definitions.engine.properties.name = { type: "string" };
|
||||
return schema;
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, 'SyncPingValidator', function() {
|
||||
let ns = {};
|
||||
Cu.import("resource://testing-common/ajv-4.1.1.js", ns);
|
||||
let ajv = new ns.Ajv({ async: "co*" });
|
||||
return ajv.compile(SyncPingSchema);
|
||||
});
|
||||
|
||||
var provider = {
|
||||
let provider = {
|
||||
getFile: function(prop, persistent) {
|
||||
persistent.value = true;
|
||||
switch (prop) {
|
||||
|
|
@ -51,7 +20,7 @@ var provider = {
|
|||
Services.dirsvc.QueryInterface(Ci.nsIDirectoryService).registerProvider(provider);
|
||||
|
||||
// This is needed for loadAddonTestFunctions().
|
||||
var gGlobalScope = this;
|
||||
let gGlobalScope = this;
|
||||
|
||||
function ExtensionsTestPath(path) {
|
||||
if (path[0] != "/") {
|
||||
|
|
@ -76,24 +45,6 @@ function loadAddonTestFunctions() {
|
|||
createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
|
||||
}
|
||||
|
||||
function webExtensionsTestPath(path) {
|
||||
if (path[0] != "/") {
|
||||
throw Error("Path must begin with '/': " + path);
|
||||
}
|
||||
|
||||
return "../../../../toolkit/components/extensions/test/xpcshell" + path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the WebExtension test functions by importing its test file.
|
||||
*/
|
||||
function loadWebExtensionTestFunctions() {
|
||||
const path = webExtensionsTestPath("/head_sync.js");
|
||||
let file = do_get_file(path);
|
||||
let uri = Services.io.newFileURI(file);
|
||||
Services.scriptloader.loadSubScript(uri.spec, gGlobalScope);
|
||||
}
|
||||
|
||||
function getAddonInstall(name) {
|
||||
let f = do_get_file(ExtensionsTestPath("/addons/" + name + ".xpi"));
|
||||
let cb = Async.makeSyncCallback();
|
||||
|
|
@ -255,192 +206,3 @@ function do_check_array_eq(a1, a2) {
|
|||
do_check_eq(a1[i], a2[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to get the sync telemetry and add the typically used test
|
||||
// engine names to its list of allowed engines.
|
||||
function get_sync_test_telemetry() {
|
||||
let ns = {};
|
||||
Cu.import("resource://services-sync/telemetry.js", ns);
|
||||
let testEngines = ["rotary", "steam", "sterling", "catapult"];
|
||||
for (let engineName of testEngines) {
|
||||
ns.SyncTelemetry.allowedEngines.add(engineName);
|
||||
}
|
||||
ns.SyncTelemetry.submissionInterval = -1;
|
||||
return ns.SyncTelemetry;
|
||||
}
|
||||
|
||||
function assert_valid_ping(record) {
|
||||
// This is called as the test harness tears down due to shutdown. This
|
||||
// will typically have no recorded syncs, and the validator complains about
|
||||
// it. So ignore such records (but only ignore when *both* shutdown and
|
||||
// no Syncs - either of them not being true might be an actual problem)
|
||||
if (record && (record.why != "shutdown" || record.syncs.length != 0)) {
|
||||
if (!SyncPingValidator(record)) {
|
||||
deepEqual([], SyncPingValidator.errors, "Sync telemetry ping validation failed");
|
||||
}
|
||||
equal(record.version, 1);
|
||||
record.syncs.forEach(p => {
|
||||
lessOrEqual(p.when, Date.now());
|
||||
if (p.devices) {
|
||||
ok(!p.devices.some(device => device.id == p.deviceID));
|
||||
equal(new Set(p.devices.map(device => device.id)).size,
|
||||
p.devices.length, "Duplicate device ids in ping devices list");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Asserts that `ping` is a ping that doesn't contain any failure information
|
||||
function assert_success_ping(ping) {
|
||||
ok(!!ping);
|
||||
assert_valid_ping(ping);
|
||||
ping.syncs.forEach(record => {
|
||||
ok(!record.failureReason);
|
||||
equal(undefined, record.status);
|
||||
greater(record.engines.length, 0);
|
||||
for (let e of record.engines) {
|
||||
ok(!e.failureReason);
|
||||
equal(undefined, e.status);
|
||||
if (e.validation) {
|
||||
equal(undefined, e.validation.problems);
|
||||
equal(undefined, e.validation.failureReason);
|
||||
}
|
||||
if (e.outgoing) {
|
||||
for (let o of e.outgoing) {
|
||||
equal(undefined, o.failed);
|
||||
notEqual(undefined, o.sent);
|
||||
}
|
||||
}
|
||||
if (e.incoming) {
|
||||
equal(undefined, e.incoming.failed);
|
||||
equal(undefined, e.incoming.newFailed);
|
||||
notEqual(undefined, e.incoming.applied || e.incoming.reconciled);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Hooks into telemetry to validate all pings after calling.
|
||||
function validate_all_future_pings() {
|
||||
let telem = get_sync_test_telemetry();
|
||||
telem.submit = assert_valid_ping;
|
||||
}
|
||||
|
||||
function wait_for_ping(callback, allowErrorPings, getFullPing = false) {
|
||||
return new Promise(resolve => {
|
||||
let telem = get_sync_test_telemetry();
|
||||
let oldSubmit = telem.submit;
|
||||
telem.submit = function(record) {
|
||||
telem.submit = oldSubmit;
|
||||
if (allowErrorPings) {
|
||||
assert_valid_ping(record);
|
||||
} else {
|
||||
assert_success_ping(record);
|
||||
}
|
||||
if (getFullPing) {
|
||||
resolve(record);
|
||||
} else {
|
||||
equal(record.syncs.length, 1);
|
||||
resolve(record.syncs[0]);
|
||||
}
|
||||
};
|
||||
callback();
|
||||
});
|
||||
}
|
||||
|
||||
// Short helper for wait_for_ping
|
||||
function sync_and_validate_telem(allowErrorPings, getFullPing = false) {
|
||||
return wait_for_ping(() => Service.sync(), allowErrorPings, getFullPing);
|
||||
}
|
||||
|
||||
// Used for the (many) cases where we do a 'partial' sync, where only a single
|
||||
// engine is actually synced, but we still want to ensure we're generating a
|
||||
// valid ping. Returns a promise that resolves to the ping, or rejects with the
|
||||
// thrown error after calling an optional callback.
|
||||
function sync_engine_and_validate_telem(engine, allowErrorPings, onError) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let telem = get_sync_test_telemetry();
|
||||
let caughtError = null;
|
||||
// Clear out status, so failures from previous syncs won't show up in the
|
||||
// telemetry ping.
|
||||
let ns = {};
|
||||
Cu.import("resource://services-sync/status.js", ns);
|
||||
ns.Status._engines = {};
|
||||
ns.Status.partial = false;
|
||||
// Ideally we'd clear these out like we do with engines, (probably via
|
||||
// Status.resetSync()), but this causes *numerous* tests to fail, so we just
|
||||
// assume that if no failureReason or engine failures are set, and the
|
||||
// status properties are the same as they were initially, that it's just
|
||||
// a leftover.
|
||||
// This is only an issue since we're triggering the sync of just one engine,
|
||||
// without doing any other parts of the sync.
|
||||
let initialServiceStatus = ns.Status._service;
|
||||
let initialSyncStatus = ns.Status._sync;
|
||||
|
||||
let oldSubmit = telem.submit;
|
||||
telem.submit = function(ping) {
|
||||
telem.submit = oldSubmit;
|
||||
ping.syncs.forEach(record => {
|
||||
if (record && record.status) {
|
||||
// did we see anything to lead us to believe that something bad actually happened
|
||||
let realProblem = record.failureReason || record.engines.some(e => {
|
||||
if (e.failureReason || e.status) {
|
||||
return true;
|
||||
}
|
||||
if (e.outgoing && e.outgoing.some(o => o.failed > 0)) {
|
||||
return true;
|
||||
}
|
||||
return e.incoming && e.incoming.failed;
|
||||
});
|
||||
if (!realProblem) {
|
||||
// no, so if the status is the same as it was initially, just assume
|
||||
// that its leftover and that we can ignore it.
|
||||
if (record.status.sync && record.status.sync == initialSyncStatus) {
|
||||
delete record.status.sync;
|
||||
}
|
||||
if (record.status.service && record.status.service == initialServiceStatus) {
|
||||
delete record.status.service;
|
||||
}
|
||||
if (!record.status.sync && !record.status.service) {
|
||||
delete record.status;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
if (allowErrorPings) {
|
||||
assert_valid_ping(ping);
|
||||
} else {
|
||||
assert_success_ping(ping);
|
||||
}
|
||||
equal(ping.syncs.length, 1);
|
||||
if (caughtError) {
|
||||
if (onError) {
|
||||
onError(ping.syncs[0]);
|
||||
}
|
||||
reject(caughtError);
|
||||
} else {
|
||||
resolve(ping.syncs[0]);
|
||||
}
|
||||
}
|
||||
Svc.Obs.notify("weave:service:sync:start");
|
||||
try {
|
||||
engine.sync();
|
||||
} catch (e) {
|
||||
caughtError = e;
|
||||
}
|
||||
if (caughtError) {
|
||||
Svc.Obs.notify("weave:service:sync:error", caughtError);
|
||||
} else {
|
||||
Svc.Obs.notify("weave:service:sync:finish");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Avoid an issue where `client.name2` containing unicode characters causes
|
||||
// a number of tests to fail, due to them assuming that we do not need to utf-8
|
||||
// encode or decode data sent through the mocked server (see bug 1268912).
|
||||
Utils.getDefaultDeviceName = function() {
|
||||
return "Test device name";
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
var Cm = Components.manager;
|
||||
const Cm = Components.manager;
|
||||
|
||||
// Shared logging for all HTTP server functions.
|
||||
Cu.import("resource://gre/modules/Log.jsm");
|
||||
|
|
@ -178,13 +178,9 @@ ServerCollection.prototype = {
|
|||
* @return an array of IDs.
|
||||
*/
|
||||
keys: function keys(filter) {
|
||||
let ids = [];
|
||||
for (let [id, wbo] of Object.entries(this._wbos)) {
|
||||
if (wbo.payload && (!filter || filter(id, wbo))) {
|
||||
ids.push(id);
|
||||
}
|
||||
}
|
||||
return ids;
|
||||
return [id for ([id, wbo] in Iterator(this._wbos))
|
||||
if (wbo.payload &&
|
||||
(!filter || filter(id, wbo)))];
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
@ -198,13 +194,8 @@ ServerCollection.prototype = {
|
|||
* @return an array of ServerWBOs.
|
||||
*/
|
||||
wbos: function wbos(filter) {
|
||||
let os = [];
|
||||
for (let [id, wbo] of Object.entries(this._wbos)) {
|
||||
if (wbo.payload) {
|
||||
os.push(wbo);
|
||||
}
|
||||
}
|
||||
|
||||
let os = [wbo for ([id, wbo] in Iterator(this._wbos))
|
||||
if (wbo.payload)];
|
||||
if (filter) {
|
||||
return os.filter(filter);
|
||||
}
|
||||
|
|
@ -276,7 +267,7 @@ ServerCollection.prototype = {
|
|||
count: function(options) {
|
||||
options = options || {};
|
||||
let c = 0;
|
||||
for (let [id, wbo] of Object.entries(this._wbos)) {
|
||||
for (let [id, wbo] in Iterator(this._wbos)) {
|
||||
if (wbo.modified && this._inResultSet(wbo, options)) {
|
||||
c++;
|
||||
}
|
||||
|
|
@ -287,23 +278,12 @@ ServerCollection.prototype = {
|
|||
get: function(options) {
|
||||
let result;
|
||||
if (options.full) {
|
||||
let data = [];
|
||||
for (let [id, wbo] of Object.entries(this._wbos)) {
|
||||
// Drop deleted.
|
||||
if (wbo.modified && this._inResultSet(wbo, options)) {
|
||||
data.push(wbo.get());
|
||||
}
|
||||
}
|
||||
let start = options.offset || 0;
|
||||
let data = [wbo.get() for ([id, wbo] in Iterator(this._wbos))
|
||||
// Drop deleted.
|
||||
if (wbo.modified &&
|
||||
this._inResultSet(wbo, options))];
|
||||
if (options.limit) {
|
||||
let numItemsPastOffset = data.length - start;
|
||||
data = data.slice(start, start + options.limit);
|
||||
// use options as a backchannel to set x-weave-next-offset
|
||||
if (numItemsPastOffset > options.limit) {
|
||||
options.nextOffset = start + options.limit;
|
||||
}
|
||||
} else if (start) {
|
||||
data = data.slice(start);
|
||||
data = data.slice(0, options.limit);
|
||||
}
|
||||
// Our implementation of application/newlines.
|
||||
result = data.join("\n") + "\n";
|
||||
|
|
@ -311,18 +291,10 @@ ServerCollection.prototype = {
|
|||
// Use options as a backchannel to report count.
|
||||
options.recordCount = data.length;
|
||||
} else {
|
||||
let data = [];
|
||||
for (let [id, wbo] of Object.entries(this._wbos)) {
|
||||
if (this._inResultSet(wbo, options)) {
|
||||
data.push(id);
|
||||
}
|
||||
}
|
||||
let start = options.offset || 0;
|
||||
let data = [id for ([id, wbo] in Iterator(this._wbos))
|
||||
if (this._inResultSet(wbo, options))];
|
||||
if (options.limit) {
|
||||
data = data.slice(start, start + options.limit);
|
||||
options.nextOffset = start + options.limit;
|
||||
} else if (start) {
|
||||
data = data.slice(start);
|
||||
data = data.slice(0, options.limit);
|
||||
}
|
||||
result = JSON.stringify(data);
|
||||
options.recordCount = data.length;
|
||||
|
|
@ -337,8 +309,7 @@ ServerCollection.prototype = {
|
|||
|
||||
// This will count records where we have an existing ServerWBO
|
||||
// registered with us as successful and all other records as failed.
|
||||
for (let key in input) {
|
||||
let record = input[key];
|
||||
for each (let record in input) {
|
||||
let wbo = this.wbo(record.id);
|
||||
if (!wbo && this.acceptNew) {
|
||||
this._log.debug("Creating WBO " + JSON.stringify(record.id) +
|
||||
|
|
@ -361,7 +332,7 @@ ServerCollection.prototype = {
|
|||
|
||||
delete: function(options) {
|
||||
let deleted = [];
|
||||
for (let [id, wbo] of Object.entries(this._wbos)) {
|
||||
for (let [id, wbo] in Iterator(this._wbos)) {
|
||||
if (this._inResultSet(wbo, options)) {
|
||||
this._log.debug("Deleting " + JSON.stringify(wbo));
|
||||
deleted.push(wbo.id);
|
||||
|
|
@ -383,7 +354,7 @@ ServerCollection.prototype = {
|
|||
|
||||
// Parse queryString
|
||||
let options = {};
|
||||
for (let chunk of request.queryString.split("&")) {
|
||||
for each (let chunk in request.queryString.split("&")) {
|
||||
if (!chunk) {
|
||||
continue;
|
||||
}
|
||||
|
|
@ -403,36 +374,29 @@ ServerCollection.prototype = {
|
|||
if (options.limit) {
|
||||
options.limit = parseInt(options.limit, 10);
|
||||
}
|
||||
if (options.offset) {
|
||||
options.offset = parseInt(options.offset, 10);
|
||||
}
|
||||
|
||||
switch(request.method) {
|
||||
case "GET":
|
||||
body = self.get(options, request);
|
||||
// see http://moz-services-docs.readthedocs.io/en/latest/storage/apis-1.5.html
|
||||
// for description of these headers.
|
||||
let { recordCount: records, nextOffset } = options;
|
||||
|
||||
self._log.info("Records: " + records + ", nextOffset: " + nextOffset);
|
||||
body = self.get(options);
|
||||
// "If supported by the db, this header will return the number of
|
||||
// records total in the request body of any multiple-record GET
|
||||
// request."
|
||||
let records = options.recordCount;
|
||||
self._log.info("Records: " + records);
|
||||
if (records != null) {
|
||||
response.setHeader("X-Weave-Records", "" + records);
|
||||
}
|
||||
if (nextOffset) {
|
||||
response.setHeader("X-Weave-Next-Offset", "" + nextOffset);
|
||||
}
|
||||
response.setHeader("X-Last-Modified", "" + this.timestamp);
|
||||
break;
|
||||
|
||||
case "POST":
|
||||
let res = self.post(readBytesFromInputStream(request.bodyInputStream), request);
|
||||
let res = self.post(readBytesFromInputStream(request.bodyInputStream));
|
||||
body = JSON.stringify(res);
|
||||
response.newModified = res.modified;
|
||||
break;
|
||||
|
||||
case "DELETE":
|
||||
self._log.debug("Invoking ServerCollection.DELETE.");
|
||||
let deleted = self.delete(options, request);
|
||||
let deleted = self.delete(options);
|
||||
let ts = new_timestamp();
|
||||
body = JSON.stringify(ts);
|
||||
response.newModified = ts;
|
||||
|
|
@ -541,7 +505,7 @@ function track_collections_helper() {
|
|||
* find out what it needs without monkeypatching. Use this object as your
|
||||
* prototype, and override as appropriate.
|
||||
*/
|
||||
var SyncServerCallback = {
|
||||
let SyncServerCallback = {
|
||||
onCollectionDeleted: function onCollectionDeleted(user, collection) {},
|
||||
onItemDeleted: function onItemDeleted(user, collection, wboID) {},
|
||||
|
||||
|
|
@ -581,13 +545,13 @@ SyncServer.prototype = {
|
|||
* Start the SyncServer's underlying HTTP server.
|
||||
*
|
||||
* @param port
|
||||
* The numeric port on which to start. -1 implies the default, a
|
||||
* randomly chosen port.
|
||||
* The numeric port on which to start. A falsy value implies the
|
||||
* default, a randomly chosen port.
|
||||
* @param cb
|
||||
* A callback function (of no arguments) which is invoked after
|
||||
* startup.
|
||||
*/
|
||||
start: function start(port = -1, cb) {
|
||||
start: function start(port, cb) {
|
||||
if (this.started) {
|
||||
this._log.warn("Warning: server already started on " + this.port);
|
||||
return;
|
||||
|
|
@ -605,7 +569,7 @@ SyncServer.prototype = {
|
|||
} catch (ex) {
|
||||
_("==========================================");
|
||||
_("Got exception starting Sync HTTP server.");
|
||||
_("Error: " + Log.exceptionStr(ex));
|
||||
_("Error: " + Utils.exceptionStr(ex));
|
||||
_("Is there a process already listening on port " + port + "?");
|
||||
_("==========================================");
|
||||
do_throw(ex);
|
||||
|
|
@ -703,10 +667,10 @@ SyncServer.prototype = {
|
|||
throw new Error("Unknown user.");
|
||||
}
|
||||
let userCollections = this.users[username].collections;
|
||||
for (let [id, contents] of Object.entries(collections)) {
|
||||
for (let [id, contents] in Iterator(collections)) {
|
||||
let coll = userCollections[id] ||
|
||||
this._insertCollection(userCollections, id);
|
||||
for (let [wboID, payload] of Object.entries(contents)) {
|
||||
for (let [wboID, payload] in Iterator(contents)) {
|
||||
coll.insert(wboID, payload);
|
||||
}
|
||||
}
|
||||
|
|
@ -740,8 +704,7 @@ SyncServer.prototype = {
|
|||
throw new Error("Unknown user.");
|
||||
}
|
||||
let userCollections = this.users[username].collections;
|
||||
for (let name in userCollections) {
|
||||
let coll = userCollections[name];
|
||||
for each (let [name, coll] in Iterator(userCollections)) {
|
||||
this._log.trace("Bulk deleting " + name + " for " + username + "...");
|
||||
coll.delete({});
|
||||
}
|
||||
|
|
@ -805,10 +768,7 @@ SyncServer.prototype = {
|
|||
*/
|
||||
respond: function respond(req, resp, code, status, body, headers) {
|
||||
resp.setStatusLine(req.httpVersion, code, status);
|
||||
if (!headers)
|
||||
headers = this.defaultHeaders;
|
||||
for (let header in headers) {
|
||||
let value = headers[header];
|
||||
for each (let [header, value] in Iterator(headers || this.defaultHeaders)) {
|
||||
resp.setHeader(header, value);
|
||||
}
|
||||
resp.setHeader("X-Weave-Timestamp", "" + this.timestamp(), false);
|
||||
|
|
@ -1035,7 +995,7 @@ SyncServer.prototype = {
|
|||
*/
|
||||
function serverForUsers(users, contents, callback) {
|
||||
let server = new SyncServer(callback);
|
||||
for (let [user, pass] of Object.entries(users)) {
|
||||
for (let [user, pass] in Iterator(users)) {
|
||||
server.registerUser(user, pass);
|
||||
server.createContents(user, contents);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,25 +0,0 @@
|
|||
// This is a "preferences" file used by test_prefs_store.js
|
||||
|
||||
// The prefs that control what should be synced.
|
||||
// Most of these are "default" prefs, so the value itself will not sync.
|
||||
pref("services.sync.prefs.sync.testing.int", true);
|
||||
pref("services.sync.prefs.sync.testing.string", true);
|
||||
pref("services.sync.prefs.sync.testing.bool", true);
|
||||
pref("services.sync.prefs.sync.testing.dont.change", true);
|
||||
// this one is a user pref, so it *will* sync.
|
||||
user_pref("services.sync.prefs.sync.testing.turned.off", false);
|
||||
pref("services.sync.prefs.sync.testing.nonexistent", true);
|
||||
pref("services.sync.prefs.sync.testing.default", true);
|
||||
|
||||
// The preference values - these are all user_prefs, otherwise their value
|
||||
// will not be synced.
|
||||
user_pref("testing.int", 123);
|
||||
user_pref("testing.string", "ohai");
|
||||
user_pref("testing.bool", true);
|
||||
user_pref("testing.dont.change", "Please don't change me.");
|
||||
user_pref("testing.turned.off", "I won't get synced.");
|
||||
user_pref("testing.not.turned.on", "I won't get synced either!");
|
||||
|
||||
// A pref that exists but still has the default value - will be synced with
|
||||
// null as the value.
|
||||
pref("testing.default", "I'm the default value");
|
||||
|
|
@ -1,198 +0,0 @@
|
|||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"description": "schema for Sync pings, documentation avaliable in toolkit/components/telemetry/docs/sync-ping.rst",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["version", "syncs", "why"],
|
||||
"properties": {
|
||||
"version": { "type": "integer", "minimum": 0 },
|
||||
"discarded": { "type": "integer", "minimum": 1 },
|
||||
"why": { "enum": ["shutdown", "schedule"] },
|
||||
"syncs": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": { "$ref": "#/definitions/payload" }
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"payload": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["when", "uid", "took"],
|
||||
"properties": {
|
||||
"didLogin": { "type": "boolean" },
|
||||
"when": { "type": "integer" },
|
||||
"uid": {
|
||||
"type": "string",
|
||||
"pattern": "^[0-9a-f]{32}$"
|
||||
},
|
||||
"devices": {
|
||||
"type": "array",
|
||||
"items": { "$ref": "#/definitions/device" }
|
||||
},
|
||||
"deviceID": {
|
||||
"type": "string",
|
||||
"pattern": "^[0-9a-f]{64}$"
|
||||
},
|
||||
"status": {
|
||||
"type": "object",
|
||||
"anyOf": [
|
||||
{ "required": ["sync"] },
|
||||
{ "required": ["service"] }
|
||||
],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"sync": { "type": "string" },
|
||||
"service": { "type": "string" }
|
||||
}
|
||||
},
|
||||
"why": { "enum": ["startup", "schedule", "score", "user", "tabs"] },
|
||||
"took": { "type": "integer", "minimum": -1 },
|
||||
"failureReason": { "$ref": "#/definitions/error" },
|
||||
"engines": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": { "$ref": "#/definitions/engine" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"device": {
|
||||
"required": ["os", "id", "version"],
|
||||
"additionalProperties": false,
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": { "type": "string", "pattern": "^[0-9a-f]{64}$" },
|
||||
"os": { "type": "string" },
|
||||
"version": { "type": "string" }
|
||||
}
|
||||
},
|
||||
"engine": {
|
||||
"required": ["name"],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"failureReason": { "$ref": "#/definitions/error" },
|
||||
"name": { "enum": ["addons", "bookmarks", "clients", "forms", "history", "passwords", "prefs", "tabs"] },
|
||||
"took": { "type": "integer", "minimum": 1 },
|
||||
"status": { "type": "string" },
|
||||
"incoming": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"anyOf": [
|
||||
{"required": ["applied"]},
|
||||
{"required": ["failed"]},
|
||||
{"required": ["newFailed"]},
|
||||
{"required": ["reconciled"]}
|
||||
],
|
||||
"properties": {
|
||||
"applied": { "type": "integer", "minimum": 1 },
|
||||
"failed": { "type": "integer", "minimum": 1 },
|
||||
"newFailed": { "type": "integer", "minimum": 1 },
|
||||
"reconciled": { "type": "integer", "minimum": 1 }
|
||||
}
|
||||
},
|
||||
"outgoing": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": { "$ref": "#/definitions/outgoingBatch" }
|
||||
},
|
||||
"validation": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"anyOf": [
|
||||
{ "required": ["checked"] },
|
||||
{ "required": ["failureReason"] }
|
||||
],
|
||||
"properties": {
|
||||
"checked": { "type": "integer", "minimum": 0 },
|
||||
"failureReason": { "$ref": "#/definitions/error" },
|
||||
"took": { "type": "integer" },
|
||||
"version": { "type": "integer" },
|
||||
"problems": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"$ref": "#/definitions/validationProblem"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"outgoingBatch": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"anyOf": [
|
||||
{"required": ["sent"]},
|
||||
{"required": ["failed"]}
|
||||
],
|
||||
"properties": {
|
||||
"sent": { "type": "integer", "minimum": 1 },
|
||||
"failed": { "type": "integer", "minimum": 1 }
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
"oneOf": [
|
||||
{ "$ref": "#/definitions/httpError" },
|
||||
{ "$ref": "#/definitions/nsError" },
|
||||
{ "$ref": "#/definitions/shutdownError" },
|
||||
{ "$ref": "#/definitions/authError" },
|
||||
{ "$ref": "#/definitions/otherError" },
|
||||
{ "$ref": "#/definitions/unexpectedError" },
|
||||
{ "$ref": "#/definitions/sqlError" }
|
||||
]
|
||||
},
|
||||
"httpError": {
|
||||
"required": ["name", "code"],
|
||||
"properties": {
|
||||
"name": { "enum": ["httperror"] },
|
||||
"code": { "type": "integer" }
|
||||
}
|
||||
},
|
||||
"nsError": {
|
||||
"required": ["name", "code"],
|
||||
"properties": {
|
||||
"name": { "enum": ["nserror"] },
|
||||
"code": { "type": "integer" }
|
||||
}
|
||||
},
|
||||
"shutdownError": {
|
||||
"required": ["name"],
|
||||
"properties": {
|
||||
"name": { "enum": ["shutdownerror"] }
|
||||
}
|
||||
},
|
||||
"authError": {
|
||||
"required": ["name"],
|
||||
"properties": {
|
||||
"name": { "enum": ["autherror"] },
|
||||
"from": { "enum": ["tokenserver", "fxaccounts", "hawkclient"] }
|
||||
}
|
||||
},
|
||||
"otherError": {
|
||||
"required": ["name"],
|
||||
"properties": {
|
||||
"name": { "enum": ["othererror"] },
|
||||
"error": { "type": "string" }
|
||||
}
|
||||
},
|
||||
"unexpectedError": {
|
||||
"required": ["name"],
|
||||
"properties": {
|
||||
"name": { "enum": ["unexpectederror"] },
|
||||
"error": { "type": "string" }
|
||||
}
|
||||
},
|
||||
"sqlError": {
|
||||
"required": ["name"],
|
||||
"properties": {
|
||||
"name": { "enum": ["sqlerror"] },
|
||||
"code": { "type": "integer" }
|
||||
}
|
||||
},
|
||||
"validationProblem": {
|
||||
"required": ["name", "count"],
|
||||
"properties": {
|
||||
"name": { "type": "string" },
|
||||
"count": { "type": "integer" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<searchresults total_results="1">
|
||||
<addon id="5618">
|
||||
<name>System Add-on Test</name>
|
||||
<type id="1">Extension</type>
|
||||
<guid>system1@tests.mozilla.org</guid>
|
||||
<slug>addon11</slug>
|
||||
<version>1.0</version>
|
||||
|
||||
<compatible_applications><application>
|
||||
<name>Firefox</name>
|
||||
<application_id>1</application_id>
|
||||
<min_version>3.6</min_version>
|
||||
<max_version>*</max_version>
|
||||
<appID>xpcshell@tests.mozilla.org</appID>
|
||||
</application></compatible_applications>
|
||||
<all_compatible_os><os>ALL</os></all_compatible_os>
|
||||
|
||||
<install os="ALL" size="999">http://127.0.0.1:8888/system.xpi</install>
|
||||
<created epoch="1252903662">
|
||||
2009-09-14T04:47:42Z
|
||||
</created>
|
||||
<last_updated epoch="1315255329">
|
||||
2011-09-05T20:42:09Z
|
||||
</last_updated>
|
||||
</addon>
|
||||
</searchresults>
|
||||
|
|
@ -3,7 +3,6 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
Cu.import("resource://gre/modules/Log.jsm");
|
||||
Cu.import("resource://gre/modules/Preferences.jsm");
|
||||
Cu.import("resource://services-sync/addonutils.js");
|
||||
Cu.import("resource://services-sync/util.js");
|
||||
|
|
@ -11,7 +10,7 @@ Cu.import("resource://services-sync/util.js");
|
|||
const HTTP_PORT = 8888;
|
||||
const SERVER_ADDRESS = "http://127.0.0.1:8888";
|
||||
|
||||
var prefs = new Preferences();
|
||||
let prefs = new Preferences();
|
||||
|
||||
prefs.set("extensions.getAddons.get.url",
|
||||
SERVER_ADDRESS + "/search/guid:%IDS%");
|
||||
|
|
@ -36,7 +35,7 @@ function createAndStartHTTPServer(port=HTTP_PORT) {
|
|||
return server;
|
||||
} catch (ex) {
|
||||
_("Got exception starting HTTP server on port " + port);
|
||||
_("Error: " + Log.exceptionStr(ex));
|
||||
_("Error: " + Utils.exceptionStr(ex));
|
||||
do_throw(ex);
|
||||
}
|
||||
}
|
||||
|
|
@ -61,9 +60,6 @@ add_test(function test_handle_empty_source_uri() {
|
|||
do_check_true("installedIDs" in result);
|
||||
do_check_eq(0, result.installedIDs.length);
|
||||
|
||||
do_check_true("skipped" in result);
|
||||
do_check_true(result.skipped.includes(ID));
|
||||
|
||||
server.stop(run_next_test);
|
||||
});
|
||||
|
||||
|
|
@ -83,18 +79,44 @@ add_test(function test_ignore_untrusted_source_uris() {
|
|||
let sourceURI = ioService.newURI(s, null, null);
|
||||
let addon = {sourceURI: sourceURI, name: "bad", id: "bad"};
|
||||
|
||||
let canInstall = AddonUtils.canInstallAddon(addon);
|
||||
do_check_false(canInstall, "Correctly rejected a bad URL");
|
||||
try {
|
||||
let cb = Async.makeSpinningCallback();
|
||||
AddonUtils.getInstallFromSearchResult(addon, cb, true);
|
||||
cb.wait();
|
||||
} catch (ex) {
|
||||
do_check_neq(null, ex);
|
||||
do_check_eq(0, ex.message.indexOf("Insecure source URI"));
|
||||
continue;
|
||||
}
|
||||
|
||||
// We should never get here if an exception is thrown.
|
||||
do_check_true(false);
|
||||
}
|
||||
|
||||
let count = 0;
|
||||
for (let s of good) {
|
||||
let sourceURI = ioService.newURI(s, null, null);
|
||||
let addon = {sourceURI: sourceURI, name: "good", id: "good"};
|
||||
|
||||
let canInstall = AddonUtils.canInstallAddon(addon);
|
||||
do_check_true(canInstall, "Correctly accepted a good URL");
|
||||
// Despite what you might think, we don't get an error in the callback.
|
||||
// The install won't work because the underlying Addon instance wasn't
|
||||
// proper. But, that just results in an AddonInstall that is missing
|
||||
// certain values. We really just care that the callback is being invoked
|
||||
// anyway.
|
||||
let callback = function onInstall(error, install) {
|
||||
do_check_null(error);
|
||||
do_check_neq(null, install);
|
||||
do_check_eq(sourceURI.spec, install.sourceURI.spec);
|
||||
|
||||
count += 1;
|
||||
|
||||
if (count >= good.length) {
|
||||
run_next_test();
|
||||
}
|
||||
};
|
||||
|
||||
AddonUtils.getInstallFromSearchResult(addon, callback, true);
|
||||
}
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_source_uri_rewrite() {
|
||||
|
|
@ -103,6 +125,8 @@ add_test(function test_source_uri_rewrite() {
|
|||
// This tests for conformance with bug 708134 so server-side metrics aren't
|
||||
// skewed.
|
||||
|
||||
Svc.Prefs.set("addons.ignoreRepositoryChecking", true);
|
||||
|
||||
// We resort to monkeypatching because of the API design.
|
||||
let oldFunction = AddonUtils.__proto__.installAddonFromSearchResult;
|
||||
|
||||
|
|
@ -127,15 +151,12 @@ add_test(function test_source_uri_rewrite() {
|
|||
let server = createAndStartHTTPServer();
|
||||
|
||||
let installCallback = Async.makeSpinningCallback();
|
||||
let installOptions = {
|
||||
id: "rewrite@tests.mozilla.org",
|
||||
requireSecureURI: false,
|
||||
}
|
||||
AddonUtils.installAddons([installOptions], installCallback);
|
||||
AddonUtils.installAddons([{id: "rewrite@tests.mozilla.org"}], installCallback);
|
||||
|
||||
installCallback.wait();
|
||||
do_check_true(installCalled);
|
||||
AddonUtils.__proto__.installAddonFromSearchResult = oldFunction;
|
||||
|
||||
Svc.Prefs.reset("addons.ignoreRepositoryChecking");
|
||||
server.stop(run_next_test);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -13,20 +13,19 @@ Cu.import("resource://services-sync/service.js");
|
|||
Cu.import("resource://services-sync/util.js");
|
||||
Cu.import("resource://testing-common/services/sync/utils.js");
|
||||
|
||||
var prefs = new Preferences();
|
||||
let prefs = new Preferences();
|
||||
prefs.set("extensions.getAddons.get.url",
|
||||
"http://localhost:8888/search/guid:%IDS%");
|
||||
prefs.set("extensions.install.requireSecureOrigin", false);
|
||||
|
||||
loadAddonTestFunctions();
|
||||
startupManager();
|
||||
|
||||
var engineManager = Service.engineManager;
|
||||
let engineManager = Service.engineManager;
|
||||
|
||||
engineManager.register(AddonsEngine);
|
||||
var engine = engineManager.get("addons");
|
||||
var reconciler = engine._reconciler;
|
||||
var tracker = engine._tracker;
|
||||
let engine = engineManager.get("addons");
|
||||
let reconciler = engine._reconciler;
|
||||
let tracker = engine._tracker;
|
||||
|
||||
function advance_test() {
|
||||
reconciler._addons = {};
|
||||
|
|
@ -36,6 +35,8 @@ function advance_test() {
|
|||
reconciler.saveState(null, cb);
|
||||
cb.wait();
|
||||
|
||||
Svc.Prefs.reset("addons.ignoreRepositoryChecking");
|
||||
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
|
|
@ -103,6 +104,7 @@ add_test(function test_get_changed_ids() {
|
|||
tracker.clearChangedIDs();
|
||||
|
||||
_("Ensure reconciler changes are populated.");
|
||||
Svc.Prefs.set("addons.ignoreRepositoryChecking", true);
|
||||
let addon = installAddon("test_bootstrap1_1");
|
||||
tracker.clearChangedIDs(); // Just in case.
|
||||
changes = engine.getChangedIDs();
|
||||
|
|
@ -149,6 +151,9 @@ add_test(function test_disabled_install_semantics() {
|
|||
// This is essentially a test for bug 712542, which snuck into the original
|
||||
// add-on sync drop. It ensures that when an add-on is installed that the
|
||||
// disabled state and incoming syncGUID is preserved, even on the next sync.
|
||||
|
||||
Svc.Prefs.set("addons.ignoreRepositoryChecking", true);
|
||||
|
||||
const USER = "foo";
|
||||
const PASSWORD = "password";
|
||||
const PASSPHRASE = "abcdeabcdeabcdeabcdeabcdea";
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ add_test(function test_install_detection() {
|
|||
|
||||
const KEYS = ["id", "guid", "enabled", "installed", "modified", "type",
|
||||
"scope", "foreignInstall"];
|
||||
for (let key of KEYS) {
|
||||
for each (let key in KEYS) {
|
||||
do_check_true(key in record);
|
||||
do_check_neq(null, record[key]);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,47 +3,25 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
Cu.import("resource://gre/modules/Log.jsm");
|
||||
Cu.import("resource://gre/modules/Preferences.jsm");
|
||||
Cu.import("resource://services-sync/addonutils.js");
|
||||
Cu.import("resource://services-sync/engines/addons.js");
|
||||
Cu.import("resource://services-sync/service.js");
|
||||
Cu.import("resource://services-sync/util.js");
|
||||
Cu.import("resource://testing-common/services/sync/utils.js");
|
||||
Cu.import("resource://gre/modules/FileUtils.jsm");
|
||||
|
||||
const HTTP_PORT = 8888;
|
||||
|
||||
var prefs = new Preferences();
|
||||
let prefs = new Preferences();
|
||||
|
||||
prefs.set("extensions.getAddons.get.url", "http://localhost:8888/search/guid:%IDS%");
|
||||
prefs.set("extensions.install.requireSecureOrigin", false);
|
||||
|
||||
const SYSTEM_ADDON_ID = "system1@tests.mozilla.org";
|
||||
let systemAddonFile;
|
||||
|
||||
// The system add-on must be installed before AddonManager is started.
|
||||
function loadSystemAddon() {
|
||||
let addonFilename = SYSTEM_ADDON_ID + ".xpi";
|
||||
const distroDir = FileUtils.getDir("ProfD", ["sysfeatures", "app0"], true);
|
||||
do_get_file(ExtensionsTestPath("/data/system_addons/system1_1.xpi")).copyTo(distroDir, addonFilename);
|
||||
systemAddonFile = FileUtils.File(distroDir.path);
|
||||
systemAddonFile.append(addonFilename);
|
||||
systemAddonFile.lastModifiedTime = Date.now();
|
||||
// As we're not running in application, we need to setup the features directory
|
||||
// used by system add-ons.
|
||||
registerDirectory("XREAppFeat", distroDir);
|
||||
}
|
||||
|
||||
loadAddonTestFunctions();
|
||||
loadSystemAddon();
|
||||
startupManager();
|
||||
|
||||
Service.engineManager.register(AddonsEngine);
|
||||
var engine = Service.engineManager.get("addons");
|
||||
var tracker = engine._tracker;
|
||||
var store = engine._store;
|
||||
var reconciler = engine._reconciler;
|
||||
let engine = Service.engineManager.get("addons");
|
||||
let tracker = engine._tracker;
|
||||
let store = engine._store;
|
||||
let reconciler = engine._reconciler;
|
||||
|
||||
/**
|
||||
* Create a AddonsRec for this application with the fields specified.
|
||||
|
|
@ -77,16 +55,12 @@ function createAndStartHTTPServer(port) {
|
|||
server.registerFile("/search/guid:missing-xpi%40tests.mozilla.org",
|
||||
do_get_file("missing-xpi-search.xml"));
|
||||
|
||||
server.registerFile("/search/guid:system1%40tests.mozilla.org",
|
||||
do_get_file("systemaddon-search.xml"));
|
||||
server.registerFile("/system.xpi", systemAddonFile);
|
||||
|
||||
server.start(port);
|
||||
|
||||
return server;
|
||||
} catch (ex) {
|
||||
_("Got exception starting HTTP server on port " + port);
|
||||
_("Error: " + Log.exceptionStr(ex));
|
||||
_("Error: " + Utils.exceptionStr(ex));
|
||||
do_throw(ex);
|
||||
}
|
||||
}
|
||||
|
|
@ -94,7 +68,6 @@ function createAndStartHTTPServer(port) {
|
|||
function run_test() {
|
||||
initTestLogging("Trace");
|
||||
Log.repository.getLogger("Sync.Engine.Addons").level = Log.Level.Trace;
|
||||
Log.repository.getLogger("Sync.Tracker.Addons").level = Log.Level.Trace;
|
||||
Log.repository.getLogger("Sync.AddonsRepository").level =
|
||||
Log.Level.Trace;
|
||||
|
||||
|
|
@ -219,6 +192,7 @@ add_test(function test_apply_uninstall() {
|
|||
add_test(function test_addon_syncability() {
|
||||
_("Ensure isAddonSyncable functions properly.");
|
||||
|
||||
Svc.Prefs.set("addons.ignoreRepositoryChecking", true);
|
||||
Svc.Prefs.set("addons.trustedSourceHostnames",
|
||||
"addons.mozilla.org,other.example.com");
|
||||
|
||||
|
|
@ -228,8 +202,8 @@ add_test(function test_addon_syncability() {
|
|||
do_check_true(store.isAddonSyncable(addon));
|
||||
|
||||
let dummy = {};
|
||||
const KEYS = ["id", "syncGUID", "type", "scope", "foreignInstall", "isSyncable"];
|
||||
for (let k of KEYS) {
|
||||
const KEYS = ["id", "syncGUID", "type", "scope", "foreignInstall"];
|
||||
for each (let k in KEYS) {
|
||||
dummy[k] = addon[k];
|
||||
}
|
||||
|
||||
|
|
@ -243,10 +217,6 @@ add_test(function test_addon_syncability() {
|
|||
do_check_false(store.isAddonSyncable(dummy));
|
||||
dummy.scope = addon.scope;
|
||||
|
||||
dummy.isSyncable = false;
|
||||
do_check_false(store.isAddonSyncable(dummy));
|
||||
dummy.isSyncable = addon.isSyncable;
|
||||
|
||||
dummy.foreignInstall = true;
|
||||
do_check_false(store.isAddonSyncable(dummy));
|
||||
dummy.foreignInstall = false;
|
||||
|
|
@ -272,16 +242,16 @@ add_test(function test_addon_syncability() {
|
|||
"https://untrusted.example.com/foo", // non-trusted hostname`
|
||||
];
|
||||
|
||||
for (let uri of trusted) {
|
||||
for each (let uri in trusted) {
|
||||
do_check_true(store.isSourceURITrusted(createURI(uri)));
|
||||
}
|
||||
|
||||
for (let uri of untrusted) {
|
||||
for each (let uri in untrusted) {
|
||||
do_check_false(store.isSourceURITrusted(createURI(uri)));
|
||||
}
|
||||
|
||||
Svc.Prefs.set("addons.trustedSourceHostnames", "");
|
||||
for (let uri of trusted) {
|
||||
for each (let uri in trusted) {
|
||||
do_check_false(store.isSourceURITrusted(createURI(uri)));
|
||||
}
|
||||
|
||||
|
|
@ -296,6 +266,8 @@ add_test(function test_addon_syncability() {
|
|||
add_test(function test_ignore_hotfixes() {
|
||||
_("Ensure that hotfix extensions are ignored.");
|
||||
|
||||
Svc.Prefs.set("addons.ignoreRepositoryChecking", true);
|
||||
|
||||
// A hotfix extension is one that has the id the same as the
|
||||
// extensions.hotfix.id pref.
|
||||
let prefs = new Preferences("extensions.");
|
||||
|
|
@ -304,8 +276,8 @@ add_test(function test_ignore_hotfixes() {
|
|||
do_check_true(store.isAddonSyncable(addon));
|
||||
|
||||
let dummy = {};
|
||||
const KEYS = ["id", "syncGUID", "type", "scope", "foreignInstall", "isSyncable"];
|
||||
for (let k of KEYS) {
|
||||
const KEYS = ["id", "syncGUID", "type", "scope", "foreignInstall"];
|
||||
for each (let k in KEYS) {
|
||||
dummy[k] = addon[k];
|
||||
}
|
||||
|
||||
|
|
@ -327,6 +299,7 @@ add_test(function test_ignore_hotfixes() {
|
|||
|
||||
uninstallAddon(addon);
|
||||
|
||||
Svc.Prefs.reset("addons.ignoreRepositoryChecking");
|
||||
prefs.reset("hotfix.id");
|
||||
|
||||
run_next_test();
|
||||
|
|
@ -336,6 +309,8 @@ add_test(function test_ignore_hotfixes() {
|
|||
add_test(function test_get_all_ids() {
|
||||
_("Ensures that getAllIDs() returns an appropriate set.");
|
||||
|
||||
Svc.Prefs.set("addons.ignoreRepositoryChecking", true);
|
||||
|
||||
_("Installing two addons.");
|
||||
let addon1 = installAddon("test_install1");
|
||||
let addon2 = installAddon("test_bootstrap1_1");
|
||||
|
|
@ -354,6 +329,7 @@ add_test(function test_get_all_ids() {
|
|||
addon1.install.cancel();
|
||||
uninstallAddon(addon2);
|
||||
|
||||
Svc.Prefs.reset("addons.ignoreRepositoryChecking");
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
|
|
@ -379,6 +355,9 @@ add_test(function test_change_item_id() {
|
|||
add_test(function test_create() {
|
||||
_("Ensure creating/installing an add-on from a record works.");
|
||||
|
||||
// Set this so that getInstallFromSearchResult doesn't end up
|
||||
// failing the install due to an insecure source URI scheme.
|
||||
Svc.Prefs.set("addons.ignoreRepositoryChecking", true);
|
||||
let server = createAndStartHTTPServer(HTTP_PORT);
|
||||
|
||||
let addon = installAddon("test_bootstrap1_1");
|
||||
|
|
@ -398,6 +377,7 @@ add_test(function test_create() {
|
|||
|
||||
uninstallAddon(newAddon);
|
||||
|
||||
Svc.Prefs.reset("addons.ignoreRepositoryChecking");
|
||||
server.stop(run_next_test);
|
||||
});
|
||||
|
||||
|
|
@ -432,18 +412,8 @@ add_test(function test_create_bad_install() {
|
|||
let record = createRecordForThisApp(guid, id, true, false);
|
||||
|
||||
let failed = store.applyIncomingBatch([record]);
|
||||
// This addon had no source URI so was skipped - but it's not treated as
|
||||
// failure.
|
||||
// XXX - this test isn't testing what we thought it was. Previously the addon
|
||||
// was not being installed due to requireSecureURL checking *before* we'd
|
||||
// attempted to get the XPI.
|
||||
// With requireSecureURL disabled we do see a download failure, but the addon
|
||||
// *does* get added to |failed|.
|
||||
// FTR: onDownloadFailed() is called with ERROR_NETWORK_FAILURE, so it's going
|
||||
// to be tricky to distinguish a 404 from other transient network errors
|
||||
// where we do want the addon to end up in |failed|.
|
||||
// This is being tracked in bug 1284778.
|
||||
//do_check_eq(0, failed.length);
|
||||
do_check_eq(1, failed.length);
|
||||
do_check_eq(guid, failed[0]);
|
||||
|
||||
let addon = getAddonFromAddonManagerByID(id);
|
||||
do_check_eq(null, addon);
|
||||
|
|
@ -451,56 +421,19 @@ add_test(function test_create_bad_install() {
|
|||
server.stop(run_next_test);
|
||||
});
|
||||
|
||||
add_test(function test_ignore_system() {
|
||||
_("Ensure we ignore system addons");
|
||||
// Our system addon should not appear in getAllIDs
|
||||
engine._refreshReconcilerState();
|
||||
let num = 0;
|
||||
for (let guid in store.getAllIDs()) {
|
||||
num += 1;
|
||||
let addon = reconciler.getAddonStateFromSyncGUID(guid);
|
||||
do_check_neq(addon.id, SYSTEM_ADDON_ID);
|
||||
}
|
||||
do_check_true(num > 1, "should have seen at least one.")
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_incoming_system() {
|
||||
_("Ensure we handle incoming records that refer to a system addon");
|
||||
// eg, loop initially had a normal addon but it was then "promoted" to be a
|
||||
// system addon but wanted to keep the same ID. The server record exists due
|
||||
// to this.
|
||||
|
||||
// before we start, ensure the system addon isn't disabled.
|
||||
do_check_false(getAddonFromAddonManagerByID(SYSTEM_ADDON_ID).userDisabled);
|
||||
|
||||
// Now simulate an incoming record with the same ID as the system addon,
|
||||
// but flagged as disabled - it should not be applied.
|
||||
let server = createAndStartHTTPServer(HTTP_PORT);
|
||||
// We make the incoming record flag the system addon as disabled - it should
|
||||
// be ignored.
|
||||
let guid = Utils.makeGUID();
|
||||
let record = createRecordForThisApp(guid, SYSTEM_ADDON_ID, false, false);
|
||||
|
||||
let failed = store.applyIncomingBatch([record]);
|
||||
do_check_eq(0, failed.length);
|
||||
|
||||
// The system addon should still not be userDisabled.
|
||||
do_check_false(getAddonFromAddonManagerByID(SYSTEM_ADDON_ID).userDisabled);
|
||||
|
||||
server.stop(run_next_test);
|
||||
});
|
||||
|
||||
add_test(function test_wipe() {
|
||||
_("Ensures that wiping causes add-ons to be uninstalled.");
|
||||
|
||||
let addon1 = installAddon("test_bootstrap1_1");
|
||||
|
||||
Svc.Prefs.set("addons.ignoreRepositoryChecking", true);
|
||||
store.wipe();
|
||||
|
||||
let addon = getAddonFromAddonManagerByID(addon1.id);
|
||||
do_check_eq(null, addon);
|
||||
|
||||
Svc.Prefs.reset("addons.ignoreRepositoryChecking");
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
|
|
@ -515,6 +448,7 @@ add_test(function test_wipe_and_install() {
|
|||
let record = createRecordForThisApp(installed.syncGUID, installed.id, true,
|
||||
false);
|
||||
|
||||
Svc.Prefs.set("addons.ignoreRepositoryChecking", true);
|
||||
store.wipe();
|
||||
|
||||
let deleted = getAddonFromAddonManagerByID(installed.id);
|
||||
|
|
@ -528,6 +462,7 @@ add_test(function test_wipe_and_install() {
|
|||
let fetched = getAddonFromAddonManagerByID(record.addonID);
|
||||
do_check_true(!!fetched);
|
||||
|
||||
Svc.Prefs.reset("addons.ignoreRepositoryChecking");
|
||||
server.stop(run_next_test);
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -11,13 +11,14 @@ Cu.import("resource://services-sync/util.js");
|
|||
|
||||
loadAddonTestFunctions();
|
||||
startupManager();
|
||||
Svc.Prefs.set("addons.ignoreRepositoryChecking", true);
|
||||
Svc.Prefs.set("engine.addons", true);
|
||||
|
||||
Service.engineManager.register(AddonsEngine);
|
||||
var engine = Service.engineManager.get("addons");
|
||||
var reconciler = engine._reconciler;
|
||||
var store = engine._store;
|
||||
var tracker = engine._tracker;
|
||||
let engine = Service.engineManager.get("addons");
|
||||
let reconciler = engine._reconciler;
|
||||
let store = engine._store;
|
||||
let tracker = engine._tracker;
|
||||
|
||||
// Don't write out by default.
|
||||
tracker.persistChangedIDs = false;
|
||||
|
|
|
|||
37
services/sync/tests/unit/test_block_sync.js
Normal file
37
services/sync/tests/unit/test_block_sync.js
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
Cu.import("resource://services-sync/main.js");
|
||||
Cu.import("resource://services-sync/util.js");
|
||||
|
||||
// Simple test for block/unblock.
|
||||
add_task(function *() {
|
||||
Assert.ok(!Weave.Service.scheduler.isBlocked, "sync is not blocked.")
|
||||
Assert.ok(!Svc.Prefs.has("scheduler.blocked-until"), "have no blocked pref");
|
||||
Weave.Service.scheduler.blockSync();
|
||||
|
||||
Assert.ok(Weave.Service.scheduler.isBlocked, "sync is blocked.")
|
||||
Assert.ok(Svc.Prefs.has("scheduler.blocked-until"), "have the blocked pref");
|
||||
|
||||
Weave.Service.scheduler.unblockSync();
|
||||
Assert.ok(!Weave.Service.scheduler.isBlocked, "sync is not blocked.")
|
||||
Assert.ok(!Svc.Prefs.has("scheduler.blocked-until"), "have no blocked pref");
|
||||
|
||||
// now check the "until" functionality.
|
||||
let until = Date.now() + 1000;
|
||||
Weave.Service.scheduler.blockSync(until);
|
||||
Assert.ok(Weave.Service.scheduler.isBlocked, "sync is blocked.")
|
||||
Assert.ok(Svc.Prefs.has("scheduler.blocked-until"), "have the blocked pref");
|
||||
|
||||
// wait for 'until' to pass.
|
||||
yield new Promise((resolve, reject) => {
|
||||
CommonUtils.namedTimer(resolve, 1000, {}, "timer");
|
||||
});
|
||||
|
||||
// should have automagically unblocked and removed the pref.
|
||||
Assert.ok(!Weave.Service.scheduler.isBlocked, "sync is not blocked.")
|
||||
Assert.ok(!Svc.Prefs.has("scheduler.blocked-until"), "have no blocked pref");
|
||||
});
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
|
@ -1,644 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
Cu.import("resource://gre/modules/PlacesUtils.jsm");
|
||||
Cu.import("resource://services-common/async.js");
|
||||
Cu.import("resource://gre/modules/Log.jsm");
|
||||
Cu.import("resource://services-sync/engines.js");
|
||||
Cu.import("resource://services-sync/engines/bookmarks.js");
|
||||
Cu.import("resource://services-sync/service.js");
|
||||
Cu.import("resource://services-sync/util.js");
|
||||
Cu.import("resource://testing-common/services/sync/utils.js");
|
||||
Cu.import("resource://services-sync/bookmark_validator.js");
|
||||
|
||||
|
||||
initTestLogging("Trace");
|
||||
|
||||
const bms = PlacesUtils.bookmarks;
|
||||
|
||||
Service.engineManager.register(BookmarksEngine);
|
||||
|
||||
const engine = new BookmarksEngine(Service);
|
||||
const store = engine._store;
|
||||
store._log.level = Log.Level.Trace;
|
||||
engine._log.level = Log.Level.Trace;
|
||||
|
||||
function promiseOneObserver(topic) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let observer = function(subject, topic, data) {
|
||||
Services.obs.removeObserver(observer, topic);
|
||||
resolve({ subject: subject, data: data });
|
||||
}
|
||||
Services.obs.addObserver(observer, topic, false);
|
||||
});
|
||||
}
|
||||
|
||||
function setup() {
|
||||
let server = serverForUsers({"foo": "password"}, {
|
||||
meta: {global: {engines: {bookmarks: {version: engine.version,
|
||||
syncID: engine.syncID}}}},
|
||||
bookmarks: {},
|
||||
});
|
||||
|
||||
generateNewKeys(Service.collectionKeys);
|
||||
|
||||
new SyncTestingInfrastructure(server.server);
|
||||
|
||||
let collection = server.user("foo").collection("bookmarks");
|
||||
|
||||
Svc.Obs.notify("weave:engine:start-tracking"); // We skip usual startup...
|
||||
|
||||
return { server, collection };
|
||||
}
|
||||
|
||||
function* cleanup(server) {
|
||||
Svc.Obs.notify("weave:engine:stop-tracking");
|
||||
Services.prefs.setBoolPref("services.sync-testing.startOverKeepIdentity", true);
|
||||
let promiseStartOver = promiseOneObserver("weave:service:start-over:finish");
|
||||
Service.startOver();
|
||||
yield promiseStartOver;
|
||||
yield new Promise(resolve => server.stop(resolve));
|
||||
yield bms.eraseEverything();
|
||||
}
|
||||
|
||||
function getFolderChildrenIDs(folderId) {
|
||||
let index = 0;
|
||||
let result = [];
|
||||
while (true) {
|
||||
let childId = bms.getIdForItemAt(folderId, index);
|
||||
if (childId == -1) {
|
||||
break;
|
||||
}
|
||||
result.push(childId);
|
||||
index++;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function createFolder(parentId, title) {
|
||||
let id = bms.createFolder(parentId, title, 0);
|
||||
let guid = store.GUIDForId(id);
|
||||
return { id, guid };
|
||||
}
|
||||
|
||||
function createBookmark(parentId, url, title, index = bms.DEFAULT_INDEX) {
|
||||
let uri = Utils.makeURI(url);
|
||||
let id = bms.insertBookmark(parentId, uri, index, title)
|
||||
let guid = store.GUIDForId(id);
|
||||
return { id, guid };
|
||||
}
|
||||
|
||||
function getServerRecord(collection, id) {
|
||||
let wbo = collection.get({ full: true, ids: [id] });
|
||||
// Whew - lots of json strings inside strings.
|
||||
return JSON.parse(JSON.parse(JSON.parse(wbo).payload).ciphertext);
|
||||
}
|
||||
|
||||
function* promiseNoLocalItem(guid) {
|
||||
// Check there's no item with the specified guid.
|
||||
let got = yield bms.fetch({ guid });
|
||||
ok(!got, `No record remains with GUID ${guid}`);
|
||||
// and while we are here ensure the places cache doesn't still have it.
|
||||
yield Assert.rejects(PlacesUtils.promiseItemId(guid));
|
||||
}
|
||||
|
||||
function* validate(collection, expectedFailures = []) {
|
||||
let validator = new BookmarkValidator();
|
||||
let records = collection.payloads();
|
||||
|
||||
let problems = validator.inspectServerRecords(records).problemData;
|
||||
// all non-zero problems.
|
||||
let summary = problems.getSummary().filter(prob => prob.count != 0);
|
||||
|
||||
// split into 2 arrays - expected and unexpected.
|
||||
let isInExpectedFailures = elt => {
|
||||
for (let i = 0; i < expectedFailures.length; i++) {
|
||||
if (elt.name == expectedFailures[i].name && elt.count == expectedFailures[i].count) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
let expected = [];
|
||||
let unexpected = [];
|
||||
for (let elt of summary) {
|
||||
(isInExpectedFailures(elt) ? expected : unexpected).push(elt);
|
||||
}
|
||||
if (unexpected.length || expected.length != expectedFailures.length) {
|
||||
do_print("Validation failed:");
|
||||
do_print(JSON.stringify(summary));
|
||||
// print the entire validator output as it has IDs etc.
|
||||
do_print(JSON.stringify(problems, undefined, 2));
|
||||
// All server records and the entire bookmark tree.
|
||||
do_print("Server records:\n" + JSON.stringify(collection.payloads(), undefined, 2));
|
||||
let tree = yield PlacesUtils.promiseBookmarksTree("", { includeItemIds: true });
|
||||
do_print("Local bookmark tree:\n" + JSON.stringify(tree, undefined, 2));
|
||||
ok(false);
|
||||
}
|
||||
}
|
||||
|
||||
add_task(function* test_dupe_bookmark() {
|
||||
_("Ensure that a bookmark we consider a dupe is handled correctly.");
|
||||
|
||||
let { server, collection } = this.setup();
|
||||
|
||||
try {
|
||||
// The parent folder and one bookmark in it.
|
||||
let {id: folder1_id, guid: folder1_guid } = createFolder(bms.toolbarFolder, "Folder 1");
|
||||
let {id: bmk1_id, guid: bmk1_guid} = createBookmark(folder1_id, "http://getfirefox.com/", "Get Firefox!");
|
||||
|
||||
engine.sync();
|
||||
|
||||
// We've added the bookmark, its parent (folder1) plus "menu", "toolbar", "unfiled", and "mobile".
|
||||
equal(collection.count(), 6);
|
||||
equal(getFolderChildrenIDs(folder1_id).length, 1);
|
||||
|
||||
// Now create a new incoming record that looks alot like a dupe.
|
||||
let newGUID = Utils.makeGUID();
|
||||
let to_apply = {
|
||||
id: newGUID,
|
||||
bmkUri: "http://getfirefox.com/",
|
||||
type: "bookmark",
|
||||
title: "Get Firefox!",
|
||||
parentName: "Folder 1",
|
||||
parentid: folder1_guid,
|
||||
};
|
||||
|
||||
collection.insert(newGUID, encryptPayload(to_apply), Date.now() / 1000 + 10);
|
||||
_("Syncing so new dupe record is processed");
|
||||
engine.lastSync = engine.lastSync - 0.01;
|
||||
engine.sync();
|
||||
|
||||
// We should have logically deleted the dupe record.
|
||||
equal(collection.count(), 7);
|
||||
ok(getServerRecord(collection, bmk1_guid).deleted);
|
||||
// and physically removed from the local store.
|
||||
yield promiseNoLocalItem(bmk1_guid);
|
||||
// Parent should still only have 1 item.
|
||||
equal(getFolderChildrenIDs(folder1_id).length, 1);
|
||||
// The parent record on the server should now reference the new GUID and not the old.
|
||||
let serverRecord = getServerRecord(collection, folder1_guid);
|
||||
ok(!serverRecord.children.includes(bmk1_guid));
|
||||
ok(serverRecord.children.includes(newGUID));
|
||||
|
||||
// and a final sanity check - use the validator
|
||||
yield validate(collection);
|
||||
} finally {
|
||||
yield cleanup(server);
|
||||
}
|
||||
});
|
||||
|
||||
add_task(function* test_dupe_reparented_bookmark() {
|
||||
_("Ensure that a bookmark we consider a dupe from a different parent is handled correctly");
|
||||
|
||||
let { server, collection } = this.setup();
|
||||
|
||||
try {
|
||||
// The parent folder and one bookmark in it.
|
||||
let {id: folder1_id, guid: folder1_guid } = createFolder(bms.toolbarFolder, "Folder 1");
|
||||
let {id: bmk1_id, guid: bmk1_guid} = createBookmark(folder1_id, "http://getfirefox.com/", "Get Firefox!");
|
||||
// Another parent folder *with the same name*
|
||||
let {id: folder2_id, guid: folder2_guid } = createFolder(bms.toolbarFolder, "Folder 1");
|
||||
|
||||
do_print(`folder1_guid=${folder1_guid}, folder2_guid=${folder2_guid}, bmk1_guid=${bmk1_guid}`);
|
||||
|
||||
engine.sync();
|
||||
|
||||
// We've added the bookmark, 2 folders plus "menu", "toolbar", "unfiled", and "mobile".
|
||||
equal(collection.count(), 7);
|
||||
equal(getFolderChildrenIDs(folder1_id).length, 1);
|
||||
equal(getFolderChildrenIDs(folder2_id).length, 0);
|
||||
|
||||
// Now create a new incoming record that looks alot like a dupe of the
|
||||
// item in folder1_guid, but with a record that points to folder2_guid.
|
||||
let newGUID = Utils.makeGUID();
|
||||
let to_apply = {
|
||||
id: newGUID,
|
||||
bmkUri: "http://getfirefox.com/",
|
||||
type: "bookmark",
|
||||
title: "Get Firefox!",
|
||||
parentName: "Folder 1",
|
||||
parentid: folder2_guid,
|
||||
};
|
||||
|
||||
collection.insert(newGUID, encryptPayload(to_apply), Date.now() / 1000 + 10);
|
||||
|
||||
_("Syncing so new dupe record is processed");
|
||||
engine.lastSync = engine.lastSync - 0.01;
|
||||
engine.sync();
|
||||
|
||||
// We should have logically deleted the dupe record.
|
||||
equal(collection.count(), 8);
|
||||
ok(getServerRecord(collection, bmk1_guid).deleted);
|
||||
// and physically removed from the local store.
|
||||
yield promiseNoLocalItem(bmk1_guid);
|
||||
// The original folder no longer has the item
|
||||
equal(getFolderChildrenIDs(folder1_id).length, 0);
|
||||
// But the second dupe folder does.
|
||||
equal(getFolderChildrenIDs(folder2_id).length, 1);
|
||||
|
||||
// The record for folder1 on the server should reference neither old or new GUIDs.
|
||||
let serverRecord1 = getServerRecord(collection, folder1_guid);
|
||||
ok(!serverRecord1.children.includes(bmk1_guid));
|
||||
ok(!serverRecord1.children.includes(newGUID));
|
||||
|
||||
// The record for folder2 on the server should only reference the new new GUID.
|
||||
let serverRecord2 = getServerRecord(collection, folder2_guid);
|
||||
ok(!serverRecord2.children.includes(bmk1_guid));
|
||||
ok(serverRecord2.children.includes(newGUID));
|
||||
|
||||
// and a final sanity check - use the validator
|
||||
yield validate(collection);
|
||||
} finally {
|
||||
yield cleanup(server);
|
||||
}
|
||||
});
|
||||
|
||||
add_task(function* test_dupe_reparented_locally_changed_bookmark() {
|
||||
_("Ensure that a bookmark with local changes we consider a dupe from a different parent is handled correctly");
|
||||
|
||||
let { server, collection } = this.setup();
|
||||
|
||||
try {
|
||||
// The parent folder and one bookmark in it.
|
||||
let {id: folder1_id, guid: folder1_guid } = createFolder(bms.toolbarFolder, "Folder 1");
|
||||
let {id: bmk1_id, guid: bmk1_guid} = createBookmark(folder1_id, "http://getfirefox.com/", "Get Firefox!");
|
||||
// Another parent folder *with the same name*
|
||||
let {id: folder2_id, guid: folder2_guid } = createFolder(bms.toolbarFolder, "Folder 1");
|
||||
|
||||
do_print(`folder1_guid=${folder1_guid}, folder2_guid=${folder2_guid}, bmk1_guid=${bmk1_guid}`);
|
||||
|
||||
engine.sync();
|
||||
|
||||
// We've added the bookmark, 2 folders plus "menu", "toolbar", "unfiled", and "mobile".
|
||||
equal(collection.count(), 7);
|
||||
equal(getFolderChildrenIDs(folder1_id).length, 1);
|
||||
equal(getFolderChildrenIDs(folder2_id).length, 0);
|
||||
|
||||
// Now create a new incoming record that looks alot like a dupe of the
|
||||
// item in folder1_guid, but with a record that points to folder2_guid.
|
||||
let newGUID = Utils.makeGUID();
|
||||
let to_apply = {
|
||||
id: newGUID,
|
||||
bmkUri: "http://getfirefox.com/",
|
||||
type: "bookmark",
|
||||
title: "Get Firefox!",
|
||||
parentName: "Folder 1",
|
||||
parentid: folder2_guid,
|
||||
};
|
||||
|
||||
collection.insert(newGUID, encryptPayload(to_apply), Date.now() / 1000 + 10);
|
||||
|
||||
// Make a change to the bookmark that's a dupe, and set the modification
|
||||
// time further in the future than the incoming record. This will cause
|
||||
// us to issue the infamous "DATA LOSS" warning in the logs but cause us
|
||||
// to *not* apply the incoming record.
|
||||
engine._tracker.addChangedID(bmk1_guid, Date.now() / 1000 + 60);
|
||||
|
||||
_("Syncing so new dupe record is processed");
|
||||
engine.lastSync = engine.lastSync - 0.01;
|
||||
engine.sync();
|
||||
|
||||
// We should have logically deleted the dupe record.
|
||||
equal(collection.count(), 8);
|
||||
ok(getServerRecord(collection, bmk1_guid).deleted);
|
||||
// and physically removed from the local store.
|
||||
yield promiseNoLocalItem(bmk1_guid);
|
||||
// The original folder still longer has the item
|
||||
equal(getFolderChildrenIDs(folder1_id).length, 1);
|
||||
// The second folder does not.
|
||||
equal(getFolderChildrenIDs(folder2_id).length, 0);
|
||||
|
||||
// The record for folder1 on the server should reference only the GUID.
|
||||
let serverRecord1 = getServerRecord(collection, folder1_guid);
|
||||
ok(!serverRecord1.children.includes(bmk1_guid));
|
||||
ok(serverRecord1.children.includes(newGUID));
|
||||
|
||||
// The record for folder2 on the server should reference nothing.
|
||||
let serverRecord2 = getServerRecord(collection, folder2_guid);
|
||||
ok(!serverRecord2.children.includes(bmk1_guid));
|
||||
ok(!serverRecord2.children.includes(newGUID));
|
||||
|
||||
// and a final sanity check - use the validator
|
||||
yield validate(collection);
|
||||
} finally {
|
||||
yield cleanup(server);
|
||||
}
|
||||
});
|
||||
|
||||
add_task(function* test_dupe_reparented_to_earlier_appearing_parent_bookmark() {
|
||||
_("Ensure that a bookmark we consider a dupe from a different parent that " +
|
||||
"appears in the same sync before the dupe item");
|
||||
|
||||
let { server, collection } = this.setup();
|
||||
|
||||
try {
|
||||
// The parent folder and one bookmark in it.
|
||||
let {id: folder1_id, guid: folder1_guid } = createFolder(bms.toolbarFolder, "Folder 1");
|
||||
let {id: bmk1_id, guid: bmk1_guid} = createBookmark(folder1_id, "http://getfirefox.com/", "Get Firefox!");
|
||||
// One more folder we'll use later.
|
||||
let {id: folder2_id, guid: folder2_guid} = createFolder(bms.toolbarFolder, "A second folder");
|
||||
|
||||
do_print(`folder1=${folder1_guid}, bmk1=${bmk1_guid} folder2=${folder2_guid}`);
|
||||
|
||||
engine.sync();
|
||||
|
||||
// We've added the bookmark, 2 folders plus "menu", "toolbar", "unfiled", and "mobile".
|
||||
equal(collection.count(), 7);
|
||||
equal(getFolderChildrenIDs(folder1_id).length, 1);
|
||||
|
||||
let newGUID = Utils.makeGUID();
|
||||
let newParentGUID = Utils.makeGUID();
|
||||
|
||||
// Have the new parent appear before the dupe item.
|
||||
collection.insert(newParentGUID, encryptPayload({
|
||||
id: newParentGUID,
|
||||
type: "folder",
|
||||
title: "Folder 1",
|
||||
parentName: "A second folder",
|
||||
parentid: folder2_guid,
|
||||
children: [newGUID],
|
||||
tags: [],
|
||||
}), Date.now() / 1000 + 10);
|
||||
|
||||
// And also the update to "folder 2" that references the new parent.
|
||||
collection.insert(folder2_guid, encryptPayload({
|
||||
id: folder2_guid,
|
||||
type: "folder",
|
||||
title: "A second folder",
|
||||
parentName: "Bookmarks Toolbar",
|
||||
parentid: "toolbar",
|
||||
children: [newParentGUID],
|
||||
tags: [],
|
||||
}), Date.now() / 1000 + 10);
|
||||
|
||||
// Now create a new incoming record that looks alot like a dupe of the
|
||||
// item in folder1_guid, with a record that points to a parent with the
|
||||
// same name which appeared earlier in this sync.
|
||||
collection.insert(newGUID, encryptPayload({
|
||||
id: newGUID,
|
||||
bmkUri: "http://getfirefox.com/",
|
||||
type: "bookmark",
|
||||
title: "Get Firefox!",
|
||||
parentName: "Folder 1",
|
||||
parentid: newParentGUID,
|
||||
tags: [],
|
||||
}), Date.now() / 1000 + 10);
|
||||
|
||||
|
||||
_("Syncing so new records are processed.");
|
||||
engine.lastSync = engine.lastSync - 0.01;
|
||||
engine.sync();
|
||||
|
||||
// Everything should be parented correctly.
|
||||
equal(getFolderChildrenIDs(folder1_id).length, 0);
|
||||
let newParentID = store.idForGUID(newParentGUID);
|
||||
let newID = store.idForGUID(newGUID);
|
||||
deepEqual(getFolderChildrenIDs(newParentID), [newID]);
|
||||
|
||||
// Make sure the validator thinks everything is hunky-dory.
|
||||
yield validate(collection);
|
||||
} finally {
|
||||
yield cleanup(server);
|
||||
}
|
||||
});
|
||||
|
||||
add_task(function* test_dupe_reparented_to_later_appearing_parent_bookmark() {
|
||||
_("Ensure that a bookmark we consider a dupe from a different parent that " +
|
||||
"doesn't exist locally as we process the child, but does appear in the same sync");
|
||||
|
||||
let { server, collection } = this.setup();
|
||||
|
||||
try {
|
||||
// The parent folder and one bookmark in it.
|
||||
let {id: folder1_id, guid: folder1_guid } = createFolder(bms.toolbarFolder, "Folder 1");
|
||||
let {id: bmk1_id, guid: bmk1_guid} = createBookmark(folder1_id, "http://getfirefox.com/", "Get Firefox!");
|
||||
// One more folder we'll use later.
|
||||
let {id: folder2_id, guid: folder2_guid} = createFolder(bms.toolbarFolder, "A second folder");
|
||||
|
||||
do_print(`folder1=${folder1_guid}, bmk1=${bmk1_guid} folder2=${folder2_guid}`);
|
||||
|
||||
engine.sync();
|
||||
|
||||
// We've added the bookmark, 2 folders plus "menu", "toolbar", "unfiled", and "mobile".
|
||||
equal(collection.count(), 7);
|
||||
equal(getFolderChildrenIDs(folder1_id).length, 1);
|
||||
|
||||
// Now create a new incoming record that looks alot like a dupe of the
|
||||
// item in folder1_guid, but with a record that points to a parent with the
|
||||
// same name, but a non-existing local ID.
|
||||
let newGUID = Utils.makeGUID();
|
||||
let newParentGUID = Utils.makeGUID();
|
||||
|
||||
collection.insert(newGUID, encryptPayload({
|
||||
id: newGUID,
|
||||
bmkUri: "http://getfirefox.com/",
|
||||
type: "bookmark",
|
||||
title: "Get Firefox!",
|
||||
parentName: "Folder 1",
|
||||
parentid: newParentGUID,
|
||||
tags: [],
|
||||
}), Date.now() / 1000 + 10);
|
||||
|
||||
// Now have the parent appear after (so when the record above is processed
|
||||
// this is still unknown.)
|
||||
collection.insert(newParentGUID, encryptPayload({
|
||||
id: newParentGUID,
|
||||
type: "folder",
|
||||
title: "Folder 1",
|
||||
parentName: "A second folder",
|
||||
parentid: folder2_guid,
|
||||
children: [newGUID],
|
||||
tags: [],
|
||||
}), Date.now() / 1000 + 10);
|
||||
// And also the update to "folder 2" that references the new parent.
|
||||
collection.insert(folder2_guid, encryptPayload({
|
||||
id: folder2_guid,
|
||||
type: "folder",
|
||||
title: "A second folder",
|
||||
parentName: "Bookmarks Toolbar",
|
||||
parentid: "toolbar",
|
||||
children: [newParentGUID],
|
||||
tags: [],
|
||||
}), Date.now() / 1000 + 10);
|
||||
|
||||
_("Syncing so out-of-order records are processed.");
|
||||
engine.lastSync = engine.lastSync - 0.01;
|
||||
engine.sync();
|
||||
|
||||
// The intended parent did end up existing, so it should be parented
|
||||
// correctly after de-duplication.
|
||||
equal(getFolderChildrenIDs(folder1_id).length, 0);
|
||||
let newParentID = store.idForGUID(newParentGUID);
|
||||
let newID = store.idForGUID(newGUID);
|
||||
deepEqual(getFolderChildrenIDs(newParentID), [newID]);
|
||||
|
||||
// Make sure the validator thinks everything is hunky-dory.
|
||||
yield validate(collection);
|
||||
} finally {
|
||||
yield cleanup(server);
|
||||
}
|
||||
});
|
||||
|
||||
add_task(function* test_dupe_reparented_to_future_arriving_parent_bookmark() {
|
||||
_("Ensure that a bookmark we consider a dupe from a different parent that " +
|
||||
"doesn't exist locally and doesn't appear in this Sync is handled correctly");
|
||||
|
||||
let { server, collection } = this.setup();
|
||||
|
||||
try {
|
||||
// The parent folder and one bookmark in it.
|
||||
let {id: folder1_id, guid: folder1_guid } = createFolder(bms.toolbarFolder, "Folder 1");
|
||||
let {id: bmk1_id, guid: bmk1_guid} = createBookmark(folder1_id, "http://getfirefox.com/", "Get Firefox!");
|
||||
// One more folder we'll use later.
|
||||
let {id: folder2_id, guid: folder2_guid} = createFolder(bms.toolbarFolder, "A second folder");
|
||||
|
||||
do_print(`folder1=${folder1_guid}, bmk1=${bmk1_guid} folder2=${folder2_guid}`);
|
||||
|
||||
engine.sync();
|
||||
|
||||
// We've added the bookmark, 2 folders plus "menu", "toolbar", "unfiled", and "mobile".
|
||||
equal(collection.count(), 7);
|
||||
equal(getFolderChildrenIDs(folder1_id).length, 1);
|
||||
|
||||
// Now create a new incoming record that looks alot like a dupe of the
|
||||
// item in folder1_guid, but with a record that points to a parent with the
|
||||
// same name, but a non-existing local ID.
|
||||
let newGUID = Utils.makeGUID();
|
||||
let newParentGUID = Utils.makeGUID();
|
||||
|
||||
collection.insert(newGUID, encryptPayload({
|
||||
id: newGUID,
|
||||
bmkUri: "http://getfirefox.com/",
|
||||
type: "bookmark",
|
||||
title: "Get Firefox!",
|
||||
parentName: "Folder 1",
|
||||
parentid: newParentGUID,
|
||||
tags: [],
|
||||
}), Date.now() / 1000 + 10);
|
||||
|
||||
_("Syncing so new dupe record is processed");
|
||||
engine.lastSync = engine.lastSync - 0.01;
|
||||
engine.sync();
|
||||
|
||||
// We should have logically deleted the dupe record.
|
||||
equal(collection.count(), 8);
|
||||
ok(getServerRecord(collection, bmk1_guid).deleted);
|
||||
// and physically removed from the local store.
|
||||
yield promiseNoLocalItem(bmk1_guid);
|
||||
// The intended parent doesn't exist, so it remains in the original folder
|
||||
equal(getFolderChildrenIDs(folder1_id).length, 1);
|
||||
|
||||
// The record for folder1 on the server should reference the new GUID.
|
||||
let serverRecord1 = getServerRecord(collection, folder1_guid);
|
||||
ok(!serverRecord1.children.includes(bmk1_guid));
|
||||
ok(serverRecord1.children.includes(newGUID));
|
||||
|
||||
// As the incoming parent is missing the item should have been annotated
|
||||
// with that missing parent.
|
||||
equal(PlacesUtils.annotations.getItemAnnotation(store.idForGUID(newGUID), "sync/parent"),
|
||||
newParentGUID);
|
||||
|
||||
// Check the validator. Sadly, this is known to cause a mismatch between
|
||||
// the server and client views of the tree.
|
||||
let expected = [
|
||||
// We haven't fixed the incoming record that referenced the missing parent.
|
||||
{ name: "orphans", count: 1 },
|
||||
];
|
||||
yield validate(collection, expected);
|
||||
|
||||
// Now have the parent magically appear in a later sync - but
|
||||
// it appears as being in a different parent from our existing "Folder 1",
|
||||
// so the folder itself isn't duped.
|
||||
collection.insert(newParentGUID, encryptPayload({
|
||||
id: newParentGUID,
|
||||
type: "folder",
|
||||
title: "Folder 1",
|
||||
parentName: "A second folder",
|
||||
parentid: folder2_guid,
|
||||
children: [newGUID],
|
||||
tags: [],
|
||||
}), Date.now() / 1000 + 10);
|
||||
// We also queue an update to "folder 2" that references the new parent.
|
||||
collection.insert(folder2_guid, encryptPayload({
|
||||
id: folder2_guid,
|
||||
type: "folder",
|
||||
title: "A second folder",
|
||||
parentName: "Bookmarks Toolbar",
|
||||
parentid: "toolbar",
|
||||
children: [newParentGUID],
|
||||
tags: [],
|
||||
}), Date.now() / 1000 + 10);
|
||||
|
||||
_("Syncing so missing parent appears");
|
||||
engine.lastSync = engine.lastSync - 0.01;
|
||||
engine.sync();
|
||||
|
||||
// The intended parent now does exist, so it should have been reparented.
|
||||
equal(getFolderChildrenIDs(folder1_id).length, 0);
|
||||
let newParentID = store.idForGUID(newParentGUID);
|
||||
let newID = store.idForGUID(newGUID);
|
||||
deepEqual(getFolderChildrenIDs(newParentID), [newID]);
|
||||
|
||||
// validation now has different errors :(
|
||||
expected = [
|
||||
// The validator reports multipleParents because:
|
||||
// * The incoming record newParentGUID still (and correctly) references
|
||||
// newGUID as a child.
|
||||
// * Our original Folder1 was updated to include newGUID when it
|
||||
// originally de-deuped and couldn't find the parent.
|
||||
// * When the parent *did* eventually arrive we used the parent annotation
|
||||
// to correctly reparent - but that reparenting process does not change
|
||||
// the server record.
|
||||
// Hence, newGUID is a child of both those server records :(
|
||||
{ name: "multipleParents", count: 1 },
|
||||
];
|
||||
yield validate(collection, expected);
|
||||
|
||||
} finally {
|
||||
yield cleanup(server);
|
||||
}
|
||||
});
|
||||
|
||||
add_task(function* test_dupe_empty_folder() {
|
||||
_("Ensure that an empty folder we consider a dupe is handled correctly.");
|
||||
// Empty folders aren't particularly interesting in practice (as that seems
|
||||
// an edge-case) but duping folders with items is broken - bug 1293163.
|
||||
let { server, collection } = this.setup();
|
||||
|
||||
try {
|
||||
// The folder we will end up duping away.
|
||||
let {id: folder1_id, guid: folder1_guid } = createFolder(bms.toolbarFolder, "Folder 1");
|
||||
|
||||
engine.sync();
|
||||
|
||||
// We've added 1 folder, "menu", "toolbar", "unfiled", and "mobile".
|
||||
equal(collection.count(), 5);
|
||||
|
||||
// Now create new incoming records that looks alot like a dupe of "Folder 1".
|
||||
let newFolderGUID = Utils.makeGUID();
|
||||
collection.insert(newFolderGUID, encryptPayload({
|
||||
id: newFolderGUID,
|
||||
type: "folder",
|
||||
title: "Folder 1",
|
||||
parentName: "Bookmarks Toolbar",
|
||||
parentid: "toolbar",
|
||||
children: [],
|
||||
}), Date.now() / 1000 + 10);
|
||||
|
||||
_("Syncing so new dupe records are processed");
|
||||
engine.lastSync = engine.lastSync - 0.01;
|
||||
engine.sync();
|
||||
|
||||
yield validate(collection);
|
||||
|
||||
// Collection now has one additional record - the logically deleted dupe.
|
||||
equal(collection.count(), 6);
|
||||
// original folder should be logically deleted.
|
||||
ok(getServerRecord(collection, folder1_guid).deleted);
|
||||
yield promiseNoLocalItem(folder1_guid);
|
||||
} finally {
|
||||
yield cleanup(server);
|
||||
}
|
||||
});
|
||||
// XXX - TODO - folders with children. Bug 1293163
|
||||
|
|
@ -2,10 +2,9 @@
|
|||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
Cu.import("resource://gre/modules/PlacesUtils.jsm");
|
||||
Cu.import("resource://gre/modules/PlacesSyncUtils.jsm");
|
||||
Cu.import("resource://gre/modules/BookmarkJSONUtils.jsm");
|
||||
Cu.import("resource://services-common/async.js");
|
||||
Cu.import("resource://gre/modules/Log.jsm");
|
||||
Cu.import("resource://services-sync/constants.js");
|
||||
Cu.import("resource://services-sync/engines.js");
|
||||
Cu.import("resource://services-sync/engines/bookmarks.js");
|
||||
Cu.import("resource://services-sync/service.js");
|
||||
|
|
@ -13,168 +12,9 @@ Cu.import("resource://services-sync/util.js");
|
|||
Cu.import("resource://testing-common/services/sync/utils.js");
|
||||
Cu.import("resource://gre/modules/Promise.jsm");
|
||||
|
||||
initTestLogging("Trace");
|
||||
|
||||
Service.engineManager.register(BookmarksEngine);
|
||||
|
||||
function* assertChildGuids(folderGuid, expectedChildGuids, message) {
|
||||
let tree = yield PlacesUtils.promiseBookmarksTree(folderGuid);
|
||||
let childGuids = tree.children.map(child => child.guid);
|
||||
deepEqual(childGuids, expectedChildGuids, message);
|
||||
}
|
||||
|
||||
add_task(function* test_change_during_sync() {
|
||||
_("Ensure that we track changes made during a sync.");
|
||||
|
||||
let engine = new BookmarksEngine(Service);
|
||||
let store = engine._store;
|
||||
let tracker = engine._tracker;
|
||||
let server = serverForFoo(engine);
|
||||
new SyncTestingInfrastructure(server.server);
|
||||
|
||||
let collection = server.user("foo").collection("bookmarks");
|
||||
|
||||
let bz_id = PlacesUtils.bookmarks.insertBookmark(
|
||||
PlacesUtils.bookmarksMenuFolderId, Utils.makeURI("https://bugzilla.mozilla.org/"),
|
||||
PlacesUtils.bookmarks.DEFAULT_INDEX, "Bugzilla");
|
||||
let bz_guid = yield PlacesUtils.promiseItemGuid(bz_id);
|
||||
_(`Bugzilla GUID: ${bz_guid}`);
|
||||
|
||||
Svc.Obs.notify("weave:engine:start-tracking");
|
||||
|
||||
try {
|
||||
let folder1_id = PlacesUtils.bookmarks.createFolder(
|
||||
PlacesUtils.bookmarks.toolbarFolder, "Folder 1", 0);
|
||||
let folder1_guid = store.GUIDForId(folder1_id);
|
||||
_(`Folder GUID: ${folder1_guid}`);
|
||||
|
||||
let bmk1_id = PlacesUtils.bookmarks.insertBookmark(
|
||||
folder1_id, Utils.makeURI("http://getthunderbird.com/"),
|
||||
PlacesUtils.bookmarks.DEFAULT_INDEX, "Get Thunderbird!");
|
||||
let bmk1_guid = store.GUIDForId(bmk1_id);
|
||||
_(`Thunderbird GUID: ${bmk1_guid}`);
|
||||
|
||||
// Sync is synchronous, so, to simulate a bookmark change made during a
|
||||
// sync, we create a server record that adds a bookmark as a side effect.
|
||||
let bmk2_guid = "get-firefox1"; // New child of Folder 1, created remotely.
|
||||
let bmk3_id = -1; // New child of Folder 1, created locally during sync.
|
||||
let folder2_guid = "folder2-1111"; // New folder, created remotely.
|
||||
let tagQuery_guid = "tag-query111"; // New tag query child of Folder 2, created remotely.
|
||||
let bmk4_guid = "example-org1"; // New tagged child of Folder 2, created remotely.
|
||||
{
|
||||
// An existing record changed on the server that should not trigger
|
||||
// another sync when applied.
|
||||
let bzBmk = new Bookmark("bookmarks", bz_guid);
|
||||
bzBmk.bmkUri = "https://bugzilla.mozilla.org/";
|
||||
bzBmk.description = "New description";
|
||||
bzBmk.title = "Bugzilla";
|
||||
bzBmk.tags = ["new", "tags"];
|
||||
bzBmk.parentName = "Bookmarks Toolbar";
|
||||
bzBmk.parentid = "toolbar";
|
||||
collection.insert(bz_guid, encryptPayload(bzBmk.cleartext));
|
||||
|
||||
let remoteFolder = new BookmarkFolder("bookmarks", folder2_guid);
|
||||
remoteFolder.title = "Folder 2";
|
||||
remoteFolder.children = [bmk4_guid, tagQuery_guid];
|
||||
remoteFolder.parentName = "Bookmarks Menu";
|
||||
remoteFolder.parentid = "menu";
|
||||
collection.insert(folder2_guid, encryptPayload(remoteFolder.cleartext));
|
||||
|
||||
let localFxBmk = new Bookmark("bookmarks", bmk2_guid);
|
||||
localFxBmk.bmkUri = "http://getfirefox.com/";
|
||||
localFxBmk.description = "Firefox is awesome.";
|
||||
localFxBmk.title = "Get Firefox!";
|
||||
localFxBmk.tags = ["firefox", "awesome", "browser"];
|
||||
localFxBmk.keyword = "awesome";
|
||||
localFxBmk.loadInSidebar = false;
|
||||
localFxBmk.parentName = "Folder 1";
|
||||
localFxBmk.parentid = folder1_guid;
|
||||
let remoteFxBmk = collection.insert(bmk2_guid, encryptPayload(localFxBmk.cleartext));
|
||||
remoteFxBmk.get = function get() {
|
||||
_("Inserting bookmark into local store");
|
||||
bmk3_id = PlacesUtils.bookmarks.insertBookmark(
|
||||
folder1_id, Utils.makeURI("https://mozilla.org/"),
|
||||
PlacesUtils.bookmarks.DEFAULT_INDEX, "Mozilla");
|
||||
|
||||
return ServerWBO.prototype.get.apply(this, arguments);
|
||||
};
|
||||
|
||||
// A tag query referencing a nonexistent tag folder, which we should
|
||||
// create locally when applying the record.
|
||||
let localTagQuery = new BookmarkQuery("bookmarks", tagQuery_guid);
|
||||
localTagQuery.bmkUri = "place:type=7&folder=999";
|
||||
localTagQuery.title = "Taggy tags";
|
||||
localTagQuery.folderName = "taggy";
|
||||
localTagQuery.parentName = "Folder 2";
|
||||
localTagQuery.parentid = folder2_guid;
|
||||
collection.insert(tagQuery_guid, encryptPayload(localTagQuery.cleartext));
|
||||
|
||||
// A bookmark that should appear in the results for the tag query.
|
||||
let localTaggedBmk = new Bookmark("bookmarks", bmk4_guid);
|
||||
localTaggedBmk.bmkUri = "https://example.org";
|
||||
localTaggedBmk.title = "Tagged bookmark";
|
||||
localTaggedBmk.tags = ["taggy"];
|
||||
localTaggedBmk.parentName = "Folder 2";
|
||||
localTaggedBmk.parentid = folder2_guid;
|
||||
collection.insert(bmk4_guid, encryptPayload(localTaggedBmk.cleartext));
|
||||
}
|
||||
|
||||
yield* assertChildGuids(folder1_guid, [bmk1_guid], "Folder should have 1 child before first sync");
|
||||
|
||||
_("Perform first sync");
|
||||
{
|
||||
let changes = engine.pullNewChanges();
|
||||
deepEqual(changes.ids().sort(), [folder1_guid, bmk1_guid, "toolbar"].sort(),
|
||||
"Should track bookmark and folder created before first sync");
|
||||
yield sync_engine_and_validate_telem(engine, false);
|
||||
}
|
||||
|
||||
let bmk2_id = store.idForGUID(bmk2_guid);
|
||||
let bmk3_guid = store.GUIDForId(bmk3_id);
|
||||
_(`Mozilla GUID: ${bmk3_guid}`);
|
||||
{
|
||||
equal(store.GUIDForId(bmk2_id), bmk2_guid,
|
||||
"Remote bookmark should be applied during first sync");
|
||||
ok(bmk3_id > -1,
|
||||
"Bookmark created during first sync should exist locally");
|
||||
ok(!collection.wbo(bmk3_guid),
|
||||
"Bookmark created during first sync shouldn't be uploaded yet");
|
||||
|
||||
yield* assertChildGuids(folder1_guid, [bmk1_guid, bmk3_guid, bmk2_guid],
|
||||
"Folder 1 should have 3 children after first sync");
|
||||
yield* assertChildGuids(folder2_guid, [bmk4_guid, tagQuery_guid],
|
||||
"Folder 2 should have 2 children after first sync");
|
||||
let taggedURIs = PlacesUtils.tagging.getURIsForTag("taggy");
|
||||
equal(taggedURIs.length, 1, "Should have 1 tagged URI");
|
||||
equal(taggedURIs[0].spec, "https://example.org/",
|
||||
"Synced tagged bookmark should appear in tagged URI list");
|
||||
}
|
||||
|
||||
_("Perform second sync");
|
||||
{
|
||||
let changes = engine.pullNewChanges();
|
||||
deepEqual(changes.ids().sort(), [bmk3_guid, folder1_guid].sort(),
|
||||
"Should track bookmark added during last sync and its parent");
|
||||
yield sync_engine_and_validate_telem(engine, false);
|
||||
|
||||
ok(collection.wbo(bmk3_guid),
|
||||
"Bookmark created during first sync should be uploaded during second sync");
|
||||
|
||||
yield* assertChildGuids(folder1_guid, [bmk1_guid, bmk3_guid, bmk2_guid],
|
||||
"Folder 1 should have same children after second sync");
|
||||
yield* assertChildGuids(folder2_guid, [bmk4_guid, tagQuery_guid],
|
||||
"Folder 2 should have same children after second sync");
|
||||
}
|
||||
} finally {
|
||||
store.wipe();
|
||||
Svc.Prefs.resetBranch("");
|
||||
Service.recordManager.clearCache();
|
||||
yield new Promise(resolve => server.stop(resolve));
|
||||
Svc.Obs.notify("weave:engine:stop-tracking");
|
||||
}
|
||||
});
|
||||
|
||||
add_task(function* bad_record_allIDs() {
|
||||
add_test(function bad_record_allIDs() {
|
||||
let server = new SyncServer();
|
||||
server.start();
|
||||
let syncTesting = new SyncTestingInfrastructure(server.server);
|
||||
|
|
@ -192,6 +32,9 @@ add_task(function* bad_record_allIDs() {
|
|||
_("Record is " + badRecordID);
|
||||
_("Type: " + PlacesUtils.bookmarks.getItemType(badRecordID));
|
||||
|
||||
_("Fetching children.");
|
||||
store._getChildren("toolbar", {});
|
||||
|
||||
_("Fetching all IDs.");
|
||||
let all = store.getAllIDs();
|
||||
|
||||
|
|
@ -201,7 +44,49 @@ add_task(function* bad_record_allIDs() {
|
|||
|
||||
_("Clean up.");
|
||||
PlacesUtils.bookmarks.removeItem(badRecordID);
|
||||
yield new Promise(r => server.stop(r));
|
||||
server.stop(run_next_test);
|
||||
});
|
||||
|
||||
add_test(function test_ID_caching() {
|
||||
let server = new SyncServer();
|
||||
server.start();
|
||||
let syncTesting = new SyncTestingInfrastructure(server.server);
|
||||
|
||||
_("Ensure that Places IDs are not cached.");
|
||||
let engine = new BookmarksEngine(Service);
|
||||
let store = engine._store;
|
||||
_("All IDs: " + JSON.stringify(store.getAllIDs()));
|
||||
|
||||
let mobileID = store.idForGUID("mobile");
|
||||
_("Change the GUID for that item, and drop the mobile anno.");
|
||||
store._setGUID(mobileID, "abcdefghijkl");
|
||||
PlacesUtils.annotations.removeItemAnnotation(mobileID, "mobile/bookmarksRoot");
|
||||
|
||||
let err;
|
||||
let newMobileID;
|
||||
|
||||
// With noCreate, we don't find an entry.
|
||||
try {
|
||||
newMobileID = store.idForGUID("mobile", true);
|
||||
_("New mobile ID: " + newMobileID);
|
||||
} catch (ex) {
|
||||
err = ex;
|
||||
_("Error: " + Utils.exceptionStr(err));
|
||||
}
|
||||
|
||||
do_check_true(!err);
|
||||
|
||||
// With !noCreate, lookup works, and it's different.
|
||||
newMobileID = store.idForGUID("mobile", false);
|
||||
_("New mobile ID: " + newMobileID);
|
||||
do_check_true(!!newMobileID);
|
||||
do_check_neq(newMobileID, mobileID);
|
||||
|
||||
// And it's repeatable, even with creation enabled.
|
||||
do_check_eq(newMobileID, store.idForGUID("mobile", false));
|
||||
|
||||
do_check_eq(store.GUIDForId(mobileID), "abcdefghijkl");
|
||||
server.stop(run_next_test);
|
||||
});
|
||||
|
||||
function serverForFoo(engine) {
|
||||
|
|
@ -212,7 +97,7 @@ function serverForFoo(engine) {
|
|||
});
|
||||
}
|
||||
|
||||
add_task(function* test_processIncoming_error_orderChildren() {
|
||||
add_test(function test_processIncoming_error_orderChildren() {
|
||||
_("Ensure that _orderChildren() is called even when _processIncoming() throws an error.");
|
||||
|
||||
let engine = new BookmarksEngine(Service);
|
||||
|
|
@ -259,11 +144,11 @@ add_task(function* test_processIncoming_error_orderChildren() {
|
|||
|
||||
let error;
|
||||
try {
|
||||
yield sync_engine_and_validate_telem(engine, true)
|
||||
engine.sync();
|
||||
} catch(ex) {
|
||||
error = ex;
|
||||
}
|
||||
ok(!!error);
|
||||
do_check_true(!!error);
|
||||
|
||||
// Verify that the bookmark order has been applied.
|
||||
let new_children = store.createRecord(folder1_guid).children;
|
||||
|
|
@ -278,11 +163,11 @@ add_task(function* test_processIncoming_error_orderChildren() {
|
|||
store.wipe();
|
||||
Svc.Prefs.resetBranch("");
|
||||
Service.recordManager.clearCache();
|
||||
yield new Promise(resolve => server.stop(resolve));
|
||||
server.stop(run_next_test);
|
||||
}
|
||||
});
|
||||
|
||||
add_task(function* test_restorePromptsReupload() {
|
||||
add_task(function test_restorePromptsReupload() {
|
||||
_("Ensure that restoring from a backup will reupload all records.");
|
||||
let engine = new BookmarksEngine(Service);
|
||||
let store = engine._store;
|
||||
|
|
@ -319,7 +204,8 @@ add_task(function* test_restorePromptsReupload() {
|
|||
backupFile.append("t_b_e_" + Date.now() + ".json");
|
||||
|
||||
_("Backing up to file " + backupFile.path);
|
||||
yield BookmarkJSONUtils.exportToFile(backupFile.path);
|
||||
backupFile.create(Ci.nsILocalFile.NORMAL_FILE_TYPE, 0600);
|
||||
yield BookmarkJSONUtils.exportToFile(backupFile);
|
||||
|
||||
_("Create a different record and sync.");
|
||||
let bmk2_id = PlacesUtils.bookmarks.insertBookmark(
|
||||
|
|
@ -331,17 +217,17 @@ add_task(function* test_restorePromptsReupload() {
|
|||
|
||||
let error;
|
||||
try {
|
||||
yield sync_engine_and_validate_telem(engine, false);
|
||||
engine.sync();
|
||||
} catch(ex) {
|
||||
error = ex;
|
||||
_("Got error: " + Log.exceptionStr(ex));
|
||||
_("Got error: " + Utils.exceptionStr(ex));
|
||||
}
|
||||
do_check_true(!error);
|
||||
|
||||
_("Verify that there's only one bookmark on the server, and it's Thunderbird.");
|
||||
// Of course, there's also the Bookmarks Toolbar and Bookmarks Menu...
|
||||
let wbos = collection.keys(function (id) {
|
||||
return ["menu", "toolbar", "mobile", "unfiled", folder1_guid].indexOf(id) == -1;
|
||||
return ["menu", "toolbar", "mobile", folder1_guid].indexOf(id) == -1;
|
||||
});
|
||||
do_check_eq(wbos.length, 1);
|
||||
do_check_eq(wbos[0], bmk2_guid);
|
||||
|
|
@ -371,14 +257,14 @@ add_task(function* test_restorePromptsReupload() {
|
|||
do_check_true(found);
|
||||
|
||||
_("Have the correct number of IDs locally, too.");
|
||||
do_check_eq(count, ["menu", "toolbar", "mobile", "unfiled", folder1_id, bmk1_id].length);
|
||||
do_check_eq(count, ["menu", "toolbar", folder1_id, bmk1_id].length);
|
||||
|
||||
_("Sync again. This'll wipe bookmarks from the server.");
|
||||
try {
|
||||
yield sync_engine_and_validate_telem(engine, false);
|
||||
engine.sync();
|
||||
} catch(ex) {
|
||||
error = ex;
|
||||
_("Got error: " + Log.exceptionStr(ex));
|
||||
_("Got error: " + Utils.exceptionStr(ex));
|
||||
}
|
||||
do_check_true(!error);
|
||||
|
||||
|
|
@ -391,9 +277,7 @@ add_task(function* test_restorePromptsReupload() {
|
|||
let folderWBOs = payloads.filter(function (wbo) {
|
||||
return ((wbo.type == "folder") &&
|
||||
(wbo.id != "menu") &&
|
||||
(wbo.id != "toolbar") &&
|
||||
(wbo.id != "unfiled") &&
|
||||
(wbo.id != "mobile"));
|
||||
(wbo.id != "toolbar"));
|
||||
});
|
||||
|
||||
do_check_eq(bookmarkWBOs.length, 1);
|
||||
|
|
@ -420,12 +304,10 @@ function FakeRecord(constructor, r) {
|
|||
for (let x in r) {
|
||||
this[x] = r[x];
|
||||
}
|
||||
// Borrow the constructor's conversion functions.
|
||||
this.toSyncBookmark = constructor.prototype.toSyncBookmark;
|
||||
}
|
||||
|
||||
// Bug 632287.
|
||||
add_task(function* test_mismatched_types() {
|
||||
add_test(function test_mismatched_types() {
|
||||
_("Ensure that handling a record that changes type causes deletion " +
|
||||
"then re-adding.");
|
||||
|
||||
|
|
@ -437,7 +319,6 @@ add_task(function* test_mismatched_types() {
|
|||
"description":null,
|
||||
"parentid": "toolbar"
|
||||
};
|
||||
oldRecord.cleartext = oldRecord;
|
||||
|
||||
let newRecord = {
|
||||
"id": "l1nZZXfB8nC7",
|
||||
|
|
@ -453,7 +334,6 @@ add_task(function* test_mismatched_types() {
|
|||
"oT74WwV8_j4P", "IztsItWVSo3-"],
|
||||
"parentid": "toolbar"
|
||||
};
|
||||
newRecord.cleartext = newRecord;
|
||||
|
||||
let engine = new BookmarksEngine(Service);
|
||||
let store = engine._store;
|
||||
|
|
@ -466,8 +346,8 @@ add_task(function* test_mismatched_types() {
|
|||
let bms = PlacesUtils.bookmarks;
|
||||
let oldR = new FakeRecord(BookmarkFolder, oldRecord);
|
||||
let newR = new FakeRecord(Livemark, newRecord);
|
||||
oldR.parentid = PlacesUtils.bookmarks.toolbarGuid;
|
||||
newR.parentid = PlacesUtils.bookmarks.toolbarGuid;
|
||||
oldR._parent = PlacesUtils.bookmarks.toolbarFolder;
|
||||
newR._parent = PlacesUtils.bookmarks.toolbarFolder;
|
||||
|
||||
store.applyIncoming(oldR);
|
||||
_("Applied old. It's a folder.");
|
||||
|
|
@ -490,11 +370,11 @@ add_task(function* test_mismatched_types() {
|
|||
store.wipe();
|
||||
Svc.Prefs.resetBranch("");
|
||||
Service.recordManager.clearCache();
|
||||
yield new Promise(r => server.stop(r));
|
||||
server.stop(run_next_test);
|
||||
}
|
||||
});
|
||||
|
||||
add_task(function* test_bookmark_guidMap_fail() {
|
||||
add_test(function test_bookmark_guidMap_fail() {
|
||||
_("Ensure that failures building the GUID map cause early death.");
|
||||
|
||||
let engine = new BookmarksEngine(Service);
|
||||
|
|
@ -514,9 +394,7 @@ add_task(function* test_bookmark_guidMap_fail() {
|
|||
engine.lastSync = 1; // So we don't back up.
|
||||
|
||||
// Make building the GUID map fail.
|
||||
|
||||
let pbt = PlacesUtils.promiseBookmarksTree;
|
||||
PlacesUtils.promiseBookmarksTree = function() { return Promise.reject("Nooo"); };
|
||||
store.getAllIDs = function () { throw "Nooo"; };
|
||||
|
||||
// Ensure that we throw when accessing _guidMap.
|
||||
engine._syncStartup();
|
||||
|
|
@ -542,11 +420,26 @@ add_task(function* test_bookmark_guidMap_fail() {
|
|||
}
|
||||
do_check_eq(err, "Nooo");
|
||||
|
||||
PlacesUtils.promiseBookmarksTree = pbt;
|
||||
yield new Promise(r => server.stop(r));
|
||||
server.stop(run_next_test);
|
||||
});
|
||||
|
||||
add_task(function* test_bookmark_tag_but_no_uri() {
|
||||
add_test(function test_bookmark_is_taggable() {
|
||||
let engine = new BookmarksEngine(Service);
|
||||
let store = engine._store;
|
||||
|
||||
do_check_true(store.isTaggable("bookmark"));
|
||||
do_check_true(store.isTaggable("microsummary"));
|
||||
do_check_true(store.isTaggable("query"));
|
||||
do_check_false(store.isTaggable("folder"));
|
||||
do_check_false(store.isTaggable("livemark"));
|
||||
do_check_false(store.isTaggable(null));
|
||||
do_check_false(store.isTaggable(undefined));
|
||||
do_check_false(store.isTaggable(""));
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_bookmark_tag_but_no_uri() {
|
||||
_("Ensure that a bookmark record with tags, but no URI, doesn't throw an exception.");
|
||||
|
||||
let engine = new BookmarksEngine(Service);
|
||||
|
|
@ -555,43 +448,30 @@ add_task(function* test_bookmark_tag_but_no_uri() {
|
|||
// We're simply checking that no exception is thrown, so
|
||||
// no actual checks in this test.
|
||||
|
||||
yield PlacesSyncUtils.bookmarks.insert({
|
||||
kind: PlacesSyncUtils.bookmarks.KINDS.BOOKMARK,
|
||||
syncId: Utils.makeGUID(),
|
||||
parentSyncId: "toolbar",
|
||||
url: "http://example.com",
|
||||
tags: ["foo"],
|
||||
});
|
||||
yield PlacesSyncUtils.bookmarks.insert({
|
||||
kind: PlacesSyncUtils.bookmarks.KINDS.BOOKMARK,
|
||||
syncId: Utils.makeGUID(),
|
||||
parentSyncId: "toolbar",
|
||||
url: "http://example.org",
|
||||
tags: null,
|
||||
});
|
||||
yield PlacesSyncUtils.bookmarks.insert({
|
||||
kind: PlacesSyncUtils.bookmarks.KINDS.BOOKMARK,
|
||||
syncId: Utils.makeGUID(),
|
||||
url: "about:fake",
|
||||
parentSyncId: "toolbar",
|
||||
tags: null,
|
||||
});
|
||||
store._tagURI(null, ["foo"]);
|
||||
store._tagURI(null, null);
|
||||
store._tagURI(Utils.makeURI("about:fake"), null);
|
||||
|
||||
let record = new FakeRecord(BookmarkFolder, {
|
||||
parentid: "toolbar",
|
||||
let record = {
|
||||
_parent: PlacesUtils.bookmarks.toolbarFolder,
|
||||
id: Utils.makeGUID(),
|
||||
description: "",
|
||||
tags: ["foo"],
|
||||
title: "Taggy tag",
|
||||
type: "folder"
|
||||
});
|
||||
};
|
||||
|
||||
// Because update() walks the cleartext.
|
||||
record.cleartext = record;
|
||||
|
||||
store.create(record);
|
||||
record.tags = ["bar"];
|
||||
store.update(record);
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_task(function* test_misreconciled_root() {
|
||||
add_test(function test_misreconciled_root() {
|
||||
_("Ensure that we don't reconcile an arbitrary record with a root.");
|
||||
|
||||
let engine = new BookmarksEngine(Service);
|
||||
|
|
@ -636,9 +516,6 @@ add_task(function* test_misreconciled_root() {
|
|||
|
||||
_("Applying record.");
|
||||
engine._processIncoming({
|
||||
getBatched() {
|
||||
return this.get();
|
||||
},
|
||||
get: function () {
|
||||
this.recordHandler(encrypted);
|
||||
return {success: true}
|
||||
|
|
@ -655,7 +532,7 @@ add_task(function* test_misreconciled_root() {
|
|||
do_check_eq(parentGUIDBefore, parentGUIDAfter);
|
||||
do_check_eq(parentIDBefore, parentIDAfter);
|
||||
|
||||
yield new Promise(r => server.stop(r));
|
||||
server.stop(run_next_test);
|
||||
});
|
||||
|
||||
function run_test() {
|
||||
|
|
|
|||
|
|
@ -1,63 +0,0 @@
|
|||
Cu.import("resource://gre/modules/PlacesUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Log.jsm");
|
||||
Cu.import("resource://gre/modules/Task.jsm");
|
||||
Cu.import("resource://services-sync/engines.js");
|
||||
Cu.import("resource://services-sync/engines/bookmarks.js");
|
||||
Cu.import("resource://services-sync/service.js");
|
||||
Cu.import("resource://services-sync/util.js");
|
||||
|
||||
Service.engineManager.register(BookmarksEngine);
|
||||
|
||||
var engine = Service.engineManager.get("bookmarks");
|
||||
var store = engine._store;
|
||||
var tracker = engine._tracker;
|
||||
|
||||
add_task(function* test_ignore_invalid_uri() {
|
||||
_("Ensure that we don't die with invalid bookmarks.");
|
||||
|
||||
// First create a valid bookmark.
|
||||
let bmid = PlacesUtils.bookmarks.insertBookmark(PlacesUtils.unfiledBookmarksFolderId,
|
||||
Services.io.newURI("http://example.com/", null, null),
|
||||
PlacesUtils.bookmarks.DEFAULT_INDEX,
|
||||
"the title");
|
||||
|
||||
// Now update moz_places with an invalid url.
|
||||
yield PlacesUtils.withConnectionWrapper("test_ignore_invalid_uri", Task.async(function* (db) {
|
||||
yield db.execute(
|
||||
`UPDATE moz_places SET url = :url, url_hash = hash(:url)
|
||||
WHERE id = (SELECT b.fk FROM moz_bookmarks b
|
||||
WHERE b.id = :id LIMIT 1)`,
|
||||
{ id: bmid, url: "<invalid url>" });
|
||||
}));
|
||||
|
||||
// Ensure that this doesn't throw even though the DB is now in a bad state (a
|
||||
// bookmark has an illegal url).
|
||||
engine._buildGUIDMap();
|
||||
});
|
||||
|
||||
add_task(function* test_ignore_missing_uri() {
|
||||
_("Ensure that we don't die with a bookmark referencing an invalid bookmark id.");
|
||||
|
||||
// First create a valid bookmark.
|
||||
let bmid = PlacesUtils.bookmarks.insertBookmark(PlacesUtils.unfiledBookmarksFolderId,
|
||||
Services.io.newURI("http://example.com/", null, null),
|
||||
PlacesUtils.bookmarks.DEFAULT_INDEX,
|
||||
"the title");
|
||||
|
||||
// Now update moz_bookmarks to reference a non-existing places ID
|
||||
yield PlacesUtils.withConnectionWrapper("test_ignore_missing_uri", Task.async(function* (db) {
|
||||
yield db.execute(
|
||||
`UPDATE moz_bookmarks SET fk = 999999
|
||||
WHERE id = :id`
|
||||
, { id: bmid });
|
||||
}));
|
||||
|
||||
// Ensure that this doesn't throw even though the DB is now in a bad state (a
|
||||
// bookmark has an illegal url).
|
||||
engine._buildGUIDMap();
|
||||
});
|
||||
|
||||
function run_test() {
|
||||
initTestLogging('Trace');
|
||||
run_next_test();
|
||||
}
|
||||
|
|
@ -85,12 +85,12 @@ function run_test() {
|
|||
do_check_eq(PlacesUtils.bookmarks.getKeywordForBookmark(id), null);
|
||||
|
||||
do_check_throws(
|
||||
() => PlacesUtils.annotations.getItemAnnotation(id, GENERATORURI_ANNO),
|
||||
function () PlacesUtils.annotations.getItemAnnotation(id, GENERATORURI_ANNO),
|
||||
Cr.NS_ERROR_NOT_AVAILABLE
|
||||
);
|
||||
|
||||
do_check_throws(
|
||||
() => PlacesUtils.annotations.getItemAnnotation(id, STATICTITLE_ANNO),
|
||||
function () PlacesUtils.annotations.getItemAnnotation(id, STATICTITLE_ANNO),
|
||||
Cr.NS_ERROR_NOT_AVAILABLE
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -12,11 +12,11 @@ Cu.import("resource://testing-common/services/common/utils.js");
|
|||
|
||||
const DESCRIPTION_ANNO = "bookmarkProperties/description";
|
||||
|
||||
var engine = Service.engineManager.get("bookmarks");
|
||||
var store = engine._store;
|
||||
let engine = Service.engineManager.get("bookmarks");
|
||||
let store = engine._store;
|
||||
|
||||
// Record borrowed from Bug 631361.
|
||||
var record631361 = {
|
||||
let record631361 = {
|
||||
id: "M5bwUKK8hPyF",
|
||||
index: 150,
|
||||
modified: 1296768176.49,
|
||||
|
|
@ -103,11 +103,20 @@ add_test(function test_livemark_descriptions() {
|
|||
add_test(function test_livemark_invalid() {
|
||||
_("Livemarks considered invalid by nsLivemarkService are skipped.");
|
||||
|
||||
_("Parent is 0, which is invalid. Will be set to unfiled.");
|
||||
let noParentRec = makeLivemark(record631361.payload, true);
|
||||
noParentRec._parent = 0;
|
||||
store.create(noParentRec);
|
||||
let recID = store.idForGUID(noParentRec.id, true);
|
||||
do_check_true(recID > 0);
|
||||
do_check_eq(PlacesUtils.bookmarks.getFolderIdForItem(recID), PlacesUtils.bookmarks.unfiledBookmarksFolder);
|
||||
|
||||
_("Parent is unknown. Will be set to unfiled.");
|
||||
let lateParentRec = makeLivemark(record631361.payload, true);
|
||||
let parentGUID = Utils.makeGUID();
|
||||
lateParentRec.parentid = parentGUID;
|
||||
do_check_eq(-1, store.idForGUID(parentGUID));
|
||||
lateParentRec._parent = store.idForGUID(parentGUID); // Usually done by applyIncoming.
|
||||
do_check_eq(-1, lateParentRec._parent);
|
||||
|
||||
store.create(lateParentRec);
|
||||
recID = store.idForGUID(lateParentRec.id, true);
|
||||
|
|
@ -124,7 +133,7 @@ add_test(function test_livemark_invalid() {
|
|||
|
||||
_("Parent is a Livemark. Will be skipped.");
|
||||
let lmParentRec = makeLivemark(record631361.payload, true);
|
||||
lmParentRec.parentid = store.GUIDForId(recID);
|
||||
lmParentRec._parent = recID;
|
||||
store.create(lmParentRec);
|
||||
// No exception, but no creation occurs.
|
||||
do_check_eq(-1, store.idForGUID(lmParentRec.id, true));
|
||||
|
|
|
|||
|
|
@ -2,61 +2,53 @@
|
|||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
_("Making sure after processing incoming bookmarks, they show up in the right order");
|
||||
Cu.import("resource://gre/modules/PlacesUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Task.jsm");
|
||||
Cu.import("resource://gre/modules/PlacesUtils.jsm", this);
|
||||
Cu.import("resource://services-sync/engines/bookmarks.js");
|
||||
Cu.import("resource://services-sync/service.js");
|
||||
Cu.import("resource://services-sync/util.js");
|
||||
|
||||
var check = Task.async(function* (expected, message) {
|
||||
let root = yield PlacesUtils.promiseBookmarksTree();
|
||||
function getBookmarks(folderId) {
|
||||
let bookmarks = [];
|
||||
|
||||
let bookmarks = (function mapTree(children) {
|
||||
return children.map(child => {
|
||||
let result = {
|
||||
guid: child.guid,
|
||||
index: child.index,
|
||||
};
|
||||
if (child.children) {
|
||||
result.children = mapTree(child.children);
|
||||
}
|
||||
if (child.annos) {
|
||||
let orphanAnno = child.annos.find(
|
||||
anno => anno.name == "sync/parent");
|
||||
if (orphanAnno) {
|
||||
result.requestedParent = orphanAnno.value;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
});
|
||||
}(root.children));
|
||||
let pos = 0;
|
||||
while (true) {
|
||||
let itemId = PlacesUtils.bookmarks.getIdForItemAt(folderId, pos);
|
||||
_("Got itemId", itemId, "under", folderId, "at", pos);
|
||||
if (itemId == -1)
|
||||
break;
|
||||
|
||||
switch (PlacesUtils.bookmarks.getItemType(itemId)) {
|
||||
case PlacesUtils.bookmarks.TYPE_BOOKMARK:
|
||||
bookmarks.push(PlacesUtils.bookmarks.getItemTitle(itemId));
|
||||
break;
|
||||
case PlacesUtils.bookmarks.TYPE_FOLDER:
|
||||
bookmarks.push(getBookmarks(itemId));
|
||||
break;
|
||||
default:
|
||||
_("Unsupported item type..");
|
||||
}
|
||||
|
||||
pos++;
|
||||
}
|
||||
|
||||
return bookmarks;
|
||||
}
|
||||
|
||||
function check(expected) {
|
||||
let bookmarks = getBookmarks(PlacesUtils.bookmarks.unfiledBookmarksFolder);
|
||||
|
||||
_("Checking if the bookmark structure is", JSON.stringify(expected));
|
||||
_("Got bookmarks:", JSON.stringify(bookmarks));
|
||||
deepEqual(bookmarks, expected);
|
||||
});
|
||||
do_check_true(Utils.deepEquals(bookmarks, expected));
|
||||
}
|
||||
|
||||
add_task(function* test_bookmark_order() {
|
||||
function run_test() {
|
||||
let store = new BookmarksEngine(Service)._store;
|
||||
initTestLogging("Trace");
|
||||
|
||||
_("Starting with a clean slate of no bookmarks");
|
||||
store.wipe();
|
||||
yield check([{
|
||||
guid: PlacesUtils.bookmarks.menuGuid,
|
||||
index: 0,
|
||||
}, {
|
||||
guid: PlacesUtils.bookmarks.toolbarGuid,
|
||||
index: 1,
|
||||
}, {
|
||||
// Index 2 is the tags root. (Root indices depend on the order of the
|
||||
// `CreateRoot` calls in `Database::CreateBookmarkRoots`).
|
||||
guid: PlacesUtils.bookmarks.unfiledGuid,
|
||||
index: 3,
|
||||
}, {
|
||||
guid: PlacesUtils.bookmarks.mobileGuid,
|
||||
index: 4,
|
||||
}], "clean slate");
|
||||
check([]);
|
||||
|
||||
function bookmark(name, parent) {
|
||||
let bookmark = new Bookmark("http://weave.server/my-bookmark");
|
||||
|
|
@ -83,447 +75,64 @@ add_task(function* test_bookmark_order() {
|
|||
store._orderChildren();
|
||||
delete store._childrenToOrder;
|
||||
}
|
||||
let id10 = "10_aaaaaaaaa";
|
||||
|
||||
_("basic add first bookmark");
|
||||
apply(bookmark(id10, ""));
|
||||
yield check([{
|
||||
guid: PlacesUtils.bookmarks.menuGuid,
|
||||
index: 0,
|
||||
}, {
|
||||
guid: PlacesUtils.bookmarks.toolbarGuid,
|
||||
index: 1,
|
||||
}, {
|
||||
guid: PlacesUtils.bookmarks.unfiledGuid,
|
||||
index: 3,
|
||||
children: [{
|
||||
guid: id10,
|
||||
index: 0,
|
||||
}],
|
||||
}, {
|
||||
guid: PlacesUtils.bookmarks.mobileGuid,
|
||||
index: 4,
|
||||
}], "basic add first bookmark");
|
||||
let id20 = "20_aaaaaaaaa";
|
||||
apply(bookmark("10", ""));
|
||||
check(["10"]);
|
||||
|
||||
_("basic append behind 10");
|
||||
apply(bookmark(id20, ""));
|
||||
yield check([{
|
||||
guid: PlacesUtils.bookmarks.menuGuid,
|
||||
index: 0,
|
||||
}, {
|
||||
guid: PlacesUtils.bookmarks.toolbarGuid,
|
||||
index: 1,
|
||||
}, {
|
||||
guid: PlacesUtils.bookmarks.unfiledGuid,
|
||||
index: 3,
|
||||
children: [{
|
||||
guid: id10,
|
||||
index: 0,
|
||||
}, {
|
||||
guid: id20,
|
||||
index: 1,
|
||||
}],
|
||||
}, {
|
||||
guid: PlacesUtils.bookmarks.mobileGuid,
|
||||
index: 4,
|
||||
}], "basic append behind 10");
|
||||
apply(bookmark("20", ""));
|
||||
check(["10", "20"]);
|
||||
|
||||
let id31 = "31_aaaaaaaaa";
|
||||
let id30 = "f30_aaaaaaaa";
|
||||
_("basic create in folder");
|
||||
apply(bookmark(id31, id30));
|
||||
let f30 = folder(id30, "", [id31]);
|
||||
apply(bookmark("31", "f30"));
|
||||
let f30 = folder("f30", "", ["31"]);
|
||||
apply(f30);
|
||||
yield check([{
|
||||
guid: PlacesUtils.bookmarks.menuGuid,
|
||||
index: 0,
|
||||
}, {
|
||||
guid: PlacesUtils.bookmarks.toolbarGuid,
|
||||
index: 1,
|
||||
}, {
|
||||
guid: PlacesUtils.bookmarks.unfiledGuid,
|
||||
index: 3,
|
||||
children: [{
|
||||
guid: id10,
|
||||
index: 0,
|
||||
}, {
|
||||
guid: id20,
|
||||
index: 1,
|
||||
}, {
|
||||
guid: id30,
|
||||
index: 2,
|
||||
children: [{
|
||||
guid: id31,
|
||||
index: 0,
|
||||
}],
|
||||
}],
|
||||
}, {
|
||||
guid: PlacesUtils.bookmarks.mobileGuid,
|
||||
index: 4,
|
||||
}], "basic create in folder");
|
||||
check(["10", "20", ["31"]]);
|
||||
|
||||
let id41 = "41_aaaaaaaaa";
|
||||
let id40 = "f40_aaaaaaaa";
|
||||
_("insert missing parent -> append to unfiled");
|
||||
apply(bookmark(id41, id40));
|
||||
yield check([{
|
||||
guid: PlacesUtils.bookmarks.menuGuid,
|
||||
index: 0,
|
||||
}, {
|
||||
guid: PlacesUtils.bookmarks.toolbarGuid,
|
||||
index: 1,
|
||||
}, {
|
||||
guid: PlacesUtils.bookmarks.unfiledGuid,
|
||||
index: 3,
|
||||
children: [{
|
||||
guid: id10,
|
||||
index: 0,
|
||||
}, {
|
||||
guid: id20,
|
||||
index: 1,
|
||||
}, {
|
||||
guid: id30,
|
||||
index: 2,
|
||||
children: [{
|
||||
guid: id31,
|
||||
index: 0,
|
||||
}],
|
||||
}, {
|
||||
guid: id41,
|
||||
index: 3,
|
||||
requestedParent: id40,
|
||||
}],
|
||||
}, {
|
||||
guid: PlacesUtils.bookmarks.mobileGuid,
|
||||
index: 4,
|
||||
}], "insert missing parent -> append to unfiled");
|
||||
|
||||
let id42 = "42_aaaaaaaaa";
|
||||
apply(bookmark("41", "f40"));
|
||||
check(["10", "20", ["31"], "41"]);
|
||||
|
||||
_("insert another missing parent -> append");
|
||||
apply(bookmark(id42, id40));
|
||||
yield check([{
|
||||
guid: PlacesUtils.bookmarks.menuGuid,
|
||||
index: 0,
|
||||
}, {
|
||||
guid: PlacesUtils.bookmarks.toolbarGuid,
|
||||
index: 1,
|
||||
}, {
|
||||
guid: PlacesUtils.bookmarks.unfiledGuid,
|
||||
index: 3,
|
||||
children: [{
|
||||
guid: id10,
|
||||
index: 0,
|
||||
}, {
|
||||
guid: id20,
|
||||
index: 1,
|
||||
}, {
|
||||
guid: id30,
|
||||
index: 2,
|
||||
children: [{
|
||||
guid: id31,
|
||||
index: 0,
|
||||
}],
|
||||
}, {
|
||||
guid: id41,
|
||||
index: 3,
|
||||
requestedParent: id40,
|
||||
}, {
|
||||
guid: id42,
|
||||
index: 4,
|
||||
requestedParent: id40,
|
||||
}],
|
||||
}, {
|
||||
guid: PlacesUtils.bookmarks.mobileGuid,
|
||||
index: 4,
|
||||
}], "insert another missing parent -> append");
|
||||
apply(bookmark("42", "f40"));
|
||||
check(["10", "20", ["31"], "41", "42"]);
|
||||
|
||||
_("insert folder -> move children and followers");
|
||||
let f40 = folder(id40, "", [id41, id42]);
|
||||
let f40 = folder("f40", "", ["41", "42"]);
|
||||
apply(f40);
|
||||
yield check([{
|
||||
guid: PlacesUtils.bookmarks.menuGuid,
|
||||
index: 0,
|
||||
}, {
|
||||
guid: PlacesUtils.bookmarks.toolbarGuid,
|
||||
index: 1,
|
||||
}, {
|
||||
guid: PlacesUtils.bookmarks.unfiledGuid,
|
||||
index: 3,
|
||||
children: [{
|
||||
guid: id10,
|
||||
index: 0,
|
||||
}, {
|
||||
guid: id20,
|
||||
index: 1,
|
||||
}, {
|
||||
guid: id30,
|
||||
index: 2,
|
||||
children: [{
|
||||
guid: id31,
|
||||
index: 0,
|
||||
}],
|
||||
}, {
|
||||
guid: id40,
|
||||
index: 3,
|
||||
children: [{
|
||||
guid: id41,
|
||||
index: 0,
|
||||
}, {
|
||||
guid: id42,
|
||||
index: 1,
|
||||
}]
|
||||
}],
|
||||
}, {
|
||||
guid: PlacesUtils.bookmarks.mobileGuid,
|
||||
index: 4,
|
||||
}], "insert folder -> move children and followers");
|
||||
check(["10", "20", ["31"], ["41", "42"]]);
|
||||
|
||||
_("Moving 41 behind 42 -> update f40");
|
||||
f40.children = [id42, id41];
|
||||
f40.children = ["42", "41"];
|
||||
apply(f40);
|
||||
yield check([{
|
||||
guid: PlacesUtils.bookmarks.menuGuid,
|
||||
index: 0,
|
||||
}, {
|
||||
guid: PlacesUtils.bookmarks.toolbarGuid,
|
||||
index: 1,
|
||||
}, {
|
||||
guid: PlacesUtils.bookmarks.unfiledGuid,
|
||||
index: 3,
|
||||
children: [{
|
||||
guid: id10,
|
||||
index: 0,
|
||||
}, {
|
||||
guid: id20,
|
||||
index: 1,
|
||||
}, {
|
||||
guid: id30,
|
||||
index: 2,
|
||||
children: [{
|
||||
guid: id31,
|
||||
index: 0,
|
||||
}],
|
||||
}, {
|
||||
guid: id40,
|
||||
index: 3,
|
||||
children: [{
|
||||
guid: id42,
|
||||
index: 0,
|
||||
}, {
|
||||
guid: id41,
|
||||
index: 1,
|
||||
}]
|
||||
}],
|
||||
}, {
|
||||
guid: PlacesUtils.bookmarks.mobileGuid,
|
||||
index: 4,
|
||||
}], "Moving 41 behind 42 -> update f40");
|
||||
check(["10", "20", ["31"], ["42", "41"]]);
|
||||
|
||||
_("Moving 10 back to front -> update 10, 20");
|
||||
f40.children = [id41, id42];
|
||||
f40.children = ["41", "42"];
|
||||
apply(f40);
|
||||
yield check([{
|
||||
guid: PlacesUtils.bookmarks.menuGuid,
|
||||
index: 0,
|
||||
}, {
|
||||
guid: PlacesUtils.bookmarks.toolbarGuid,
|
||||
index: 1,
|
||||
}, {
|
||||
guid: PlacesUtils.bookmarks.unfiledGuid,
|
||||
index: 3,
|
||||
children: [{
|
||||
guid: id10,
|
||||
index: 0,
|
||||
}, {
|
||||
guid: id20,
|
||||
index: 1,
|
||||
}, {
|
||||
guid: id30,
|
||||
index: 2,
|
||||
children: [{
|
||||
guid: id31,
|
||||
index: 0,
|
||||
}],
|
||||
}, {
|
||||
guid: id40,
|
||||
index: 3,
|
||||
children: [{
|
||||
guid: id41,
|
||||
index: 0,
|
||||
}, {
|
||||
guid: id42,
|
||||
index: 1,
|
||||
}]
|
||||
}],
|
||||
}, {
|
||||
guid: PlacesUtils.bookmarks.mobileGuid,
|
||||
index: 4,
|
||||
}], "Moving 10 back to front -> update 10, 20");
|
||||
check(["10", "20", ["31"], ["41", "42"]]);
|
||||
|
||||
_("Moving 20 behind 42 in f40 -> update 50");
|
||||
apply(bookmark(id20, id40));
|
||||
yield check([{
|
||||
guid: PlacesUtils.bookmarks.menuGuid,
|
||||
index: 0,
|
||||
}, {
|
||||
guid: PlacesUtils.bookmarks.toolbarGuid,
|
||||
index: 1,
|
||||
}, {
|
||||
guid: PlacesUtils.bookmarks.unfiledGuid,
|
||||
index: 3,
|
||||
children: [{
|
||||
guid: id10,
|
||||
index: 0,
|
||||
}, {
|
||||
guid: id30,
|
||||
index: 1,
|
||||
children: [{
|
||||
guid: id31,
|
||||
index: 0,
|
||||
}],
|
||||
}, {
|
||||
guid: id40,
|
||||
index: 2,
|
||||
children: [{
|
||||
guid: id41,
|
||||
index: 0,
|
||||
}, {
|
||||
guid: id42,
|
||||
index: 1,
|
||||
}, {
|
||||
guid: id20,
|
||||
index: 2,
|
||||
}]
|
||||
}],
|
||||
}, {
|
||||
guid: PlacesUtils.bookmarks.mobileGuid,
|
||||
index: 4,
|
||||
}], "Moving 20 behind 42 in f40 -> update 50");
|
||||
apply(bookmark("20", "f40"));
|
||||
check(["10", ["31"], ["41", "42", "20"]]);
|
||||
|
||||
_("Moving 10 in front of 31 in f30 -> update 10, f30");
|
||||
apply(bookmark(id10, id30));
|
||||
f30.children = [id10, id31];
|
||||
apply(bookmark("10", "f30"));
|
||||
f30.children = ["10", "31"];
|
||||
apply(f30);
|
||||
yield check([{
|
||||
guid: PlacesUtils.bookmarks.menuGuid,
|
||||
index: 0,
|
||||
}, {
|
||||
guid: PlacesUtils.bookmarks.toolbarGuid,
|
||||
index: 1,
|
||||
}, {
|
||||
guid: PlacesUtils.bookmarks.unfiledGuid,
|
||||
index: 3,
|
||||
children: [{
|
||||
guid: id30,
|
||||
index: 0,
|
||||
children: [{
|
||||
guid: id10,
|
||||
index: 0,
|
||||
}, {
|
||||
guid: id31,
|
||||
index: 1,
|
||||
}],
|
||||
}, {
|
||||
guid: id40,
|
||||
index: 1,
|
||||
children: [{
|
||||
guid: id41,
|
||||
index: 0,
|
||||
}, {
|
||||
guid: id42,
|
||||
index: 1,
|
||||
}, {
|
||||
guid: id20,
|
||||
index: 2,
|
||||
}]
|
||||
}],
|
||||
}, {
|
||||
guid: PlacesUtils.bookmarks.mobileGuid,
|
||||
index: 4,
|
||||
}], "Moving 10 in front of 31 in f30 -> update 10, f30");
|
||||
check([["10", "31"], ["41", "42", "20"]]);
|
||||
|
||||
_("Moving 20 from f40 to f30 -> update 20, f30");
|
||||
apply(bookmark(id20, id30));
|
||||
f30.children = [id10, id20, id31];
|
||||
apply(bookmark("20", "f30"));
|
||||
f30.children = ["10", "20", "31"];
|
||||
apply(f30);
|
||||
yield check([{
|
||||
guid: PlacesUtils.bookmarks.menuGuid,
|
||||
index: 0,
|
||||
}, {
|
||||
guid: PlacesUtils.bookmarks.toolbarGuid,
|
||||
index: 1,
|
||||
}, {
|
||||
guid: PlacesUtils.bookmarks.unfiledGuid,
|
||||
index: 3,
|
||||
children: [{
|
||||
guid: id30,
|
||||
index: 0,
|
||||
children: [{
|
||||
guid: id10,
|
||||
index: 0,
|
||||
}, {
|
||||
guid: id20,
|
||||
index: 1,
|
||||
}, {
|
||||
guid: id31,
|
||||
index: 2,
|
||||
}],
|
||||
}, {
|
||||
guid: id40,
|
||||
index: 1,
|
||||
children: [{
|
||||
guid: id41,
|
||||
index: 0,
|
||||
}, {
|
||||
guid: id42,
|
||||
index: 1,
|
||||
}]
|
||||
}],
|
||||
}, {
|
||||
guid: PlacesUtils.bookmarks.mobileGuid,
|
||||
index: 4,
|
||||
}], "Moving 20 from f40 to f30 -> update 20, f30");
|
||||
check([["10", "20", "31"], ["41", "42"]]);
|
||||
|
||||
_("Move 20 back to front -> update 20, f30");
|
||||
apply(bookmark(id20, ""));
|
||||
f30.children = [id10, id31];
|
||||
apply(bookmark("20", ""));
|
||||
f30.children = ["10", "31"];
|
||||
apply(f30);
|
||||
yield check([{
|
||||
guid: PlacesUtils.bookmarks.menuGuid,
|
||||
index: 0,
|
||||
}, {
|
||||
guid: PlacesUtils.bookmarks.toolbarGuid,
|
||||
index: 1,
|
||||
}, {
|
||||
guid: PlacesUtils.bookmarks.unfiledGuid,
|
||||
index: 3,
|
||||
children: [{
|
||||
guid: id30,
|
||||
index: 0,
|
||||
children: [{
|
||||
guid: id10,
|
||||
index: 0,
|
||||
}, {
|
||||
guid: id31,
|
||||
index: 1,
|
||||
}],
|
||||
}, {
|
||||
guid: id40,
|
||||
index: 1,
|
||||
children: [{
|
||||
guid: id41,
|
||||
index: 0,
|
||||
}, {
|
||||
guid: id42,
|
||||
index: 1,
|
||||
}],
|
||||
}, {
|
||||
guid: id20,
|
||||
index: 2,
|
||||
}],
|
||||
}, {
|
||||
guid: PlacesUtils.bookmarks.mobileGuid,
|
||||
index: 4,
|
||||
}], "Move 20 back to front -> update 20, f30");
|
||||
check([["10", "31"], ["41", "42"], "20"]);
|
||||
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,54 +7,45 @@ Cu.import("resource://services-sync/engines/bookmarks.js");
|
|||
Cu.import("resource://services-sync/service.js");
|
||||
Cu.import("resource://services-sync/util.js");
|
||||
|
||||
var engine = new BookmarksEngine(Service);
|
||||
var store = engine._store;
|
||||
|
||||
function makeTagRecord(id, uri) {
|
||||
let tagRecord = new BookmarkQuery("bookmarks", id);
|
||||
tagRecord.queryId = "MagicTags";
|
||||
tagRecord.parentName = "Bookmarks Toolbar";
|
||||
tagRecord.bmkUri = uri;
|
||||
tagRecord.title = "tagtag";
|
||||
tagRecord.folderName = "bar";
|
||||
tagRecord.parentid = PlacesUtils.bookmarks.toolbarGuid;
|
||||
return tagRecord;
|
||||
}
|
||||
let engine = new BookmarksEngine(Service);
|
||||
let store = engine._store;
|
||||
|
||||
function run_test() {
|
||||
initTestLogging("Trace");
|
||||
Log.repository.getLogger("Sync.Engine.Bookmarks").level = Log.Level.Trace;
|
||||
Log.repository.getLogger("Sync.Store.Bookmarks").level = Log.Level.Trace;
|
||||
|
||||
let tagRecord = new BookmarkQuery("bookmarks", "abcdefabcdef");
|
||||
let uri = "place:folder=499&type=7&queryType=1";
|
||||
let tagRecord = makeTagRecord("abcdefabcdef", uri);
|
||||
tagRecord.queryId = "MagicTags";
|
||||
tagRecord.parentName = "Bookmarks Toolbar";
|
||||
tagRecord.bmkUri = uri;
|
||||
tagRecord.title = "tagtag";
|
||||
tagRecord.folderName = "bar";
|
||||
|
||||
_("Type: " + tagRecord.type);
|
||||
_("Folder name: " + tagRecord.folderName);
|
||||
store.applyIncoming(tagRecord);
|
||||
store.preprocessTagQuery(tagRecord);
|
||||
|
||||
let tags = PlacesUtils.getFolderContents(PlacesUtils.tagsFolderId).root;
|
||||
_("Verify that the URI has been rewritten.");
|
||||
do_check_neq(tagRecord.bmkUri, uri);
|
||||
|
||||
let tags = store._getNode(PlacesUtils.tagsFolderId);
|
||||
tags.containerOpen = true;
|
||||
let tagID;
|
||||
try {
|
||||
for (let i = 0; i < tags.childCount; ++i) {
|
||||
let child = tags.getChild(i);
|
||||
if (child.title == "bar") {
|
||||
tagID = child.itemId;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
tags.containerOpen = false;
|
||||
for (let i = 0; i < tags.childCount; ++i) {
|
||||
let child = tags.getChild(i);
|
||||
if (child.title == "bar")
|
||||
tagID = child.itemId;
|
||||
}
|
||||
tags.containerOpen = false;
|
||||
|
||||
_("Tag ID: " + tagID);
|
||||
let insertedRecord = store.createRecord("abcdefabcdef", "bookmarks");
|
||||
do_check_eq(insertedRecord.bmkUri, uri.replace("499", tagID));
|
||||
do_check_eq(tagRecord.bmkUri, uri.replace("499", tagID));
|
||||
|
||||
_("... but not if the type is wrong.");
|
||||
let wrongTypeURI = "place:folder=499&type=2&queryType=1";
|
||||
let wrongTypeRecord = makeTagRecord("fedcbafedcba", wrongTypeURI);
|
||||
store.applyIncoming(wrongTypeRecord);
|
||||
|
||||
insertedRecord = store.createRecord("fedcbafedcba", "bookmarks");
|
||||
do_check_eq(insertedRecord.bmkUri, wrongTypeURI);
|
||||
tagRecord.bmkUri = wrongTypeURI;
|
||||
store.preprocessTagQuery(tagRecord);
|
||||
do_check_eq(tagRecord.bmkUri, wrongTypeURI);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,8 +16,8 @@ var IOService = Cc["@mozilla.org/network/io-service;1"]
|
|||
|
||||
|
||||
Service.engineManager.register(BookmarksEngine);
|
||||
var engine = Service.engineManager.get("bookmarks");
|
||||
var store = engine._store;
|
||||
let engine = Service.engineManager.get("bookmarks");
|
||||
let store = engine._store;
|
||||
|
||||
// Clean up after other tests. Only necessary in XULRunner.
|
||||
store.wipe();
|
||||
|
|
@ -57,7 +57,7 @@ function serverForFoo(engine) {
|
|||
|
||||
// Verify that Places smart bookmarks have their annotation uploaded and
|
||||
// handled locally.
|
||||
add_task(function *test_annotation_uploaded() {
|
||||
add_test(function test_annotation_uploaded() {
|
||||
let server = serverForFoo(engine);
|
||||
new SyncTestingInfrastructure(server.server);
|
||||
|
||||
|
|
@ -110,9 +110,9 @@ add_task(function *test_annotation_uploaded() {
|
|||
let collection = server.user("foo").collection("bookmarks");
|
||||
|
||||
try {
|
||||
yield sync_engine_and_validate_telem(engine, false);
|
||||
engine.sync();
|
||||
let wbos = collection.keys(function (id) {
|
||||
return ["menu", "toolbar", "mobile", "unfiled"].indexOf(id) == -1;
|
||||
return ["menu", "toolbar", "mobile"].indexOf(id) == -1;
|
||||
});
|
||||
do_check_eq(wbos.length, 1);
|
||||
|
||||
|
|
@ -141,7 +141,7 @@ add_task(function *test_annotation_uploaded() {
|
|||
do_check_eq(smartBookmarkCount(), startCount);
|
||||
|
||||
_("Sync. Verify that the downloaded record carries the annotation.");
|
||||
yield sync_engine_and_validate_telem(engine, false);
|
||||
engine.sync();
|
||||
|
||||
_("Verify that the Places DB now has an annotated bookmark.");
|
||||
_("Our count has increased again.");
|
||||
|
|
|
|||
|
|
@ -11,17 +11,17 @@ const PARENT_ANNO = "sync/parent";
|
|||
|
||||
Service.engineManager.register(BookmarksEngine);
|
||||
|
||||
var engine = Service.engineManager.get("bookmarks");
|
||||
var store = engine._store;
|
||||
var tracker = engine._tracker;
|
||||
let engine = Service.engineManager.get("bookmarks");
|
||||
let store = engine._store;
|
||||
let tracker = engine._tracker;
|
||||
|
||||
// Don't write some persistence files asynchronously.
|
||||
tracker.persistChangedIDs = false;
|
||||
|
||||
var fxuri = Utils.makeURI("http://getfirefox.com/");
|
||||
var tburi = Utils.makeURI("http://getthunderbird.com/");
|
||||
let fxuri = Utils.makeURI("http://getfirefox.com/");
|
||||
let tburi = Utils.makeURI("http://getthunderbird.com/");
|
||||
|
||||
add_task(function* test_ignore_specials() {
|
||||
add_test(function test_ignore_specials() {
|
||||
_("Ensure that we can't delete bookmark roots.");
|
||||
|
||||
// Belt...
|
||||
|
|
@ -30,7 +30,6 @@ add_task(function* test_ignore_specials() {
|
|||
do_check_neq(null, store.idForGUID("toolbar"));
|
||||
|
||||
store.applyIncoming(record);
|
||||
yield store.deletePending();
|
||||
|
||||
// Ensure that the toolbar exists.
|
||||
do_check_neq(null, store.idForGUID("toolbar"));
|
||||
|
|
@ -40,11 +39,11 @@ add_task(function* test_ignore_specials() {
|
|||
|
||||
// Braces...
|
||||
store.remove(record);
|
||||
yield store.deletePending();
|
||||
do_check_neq(null, store.idForGUID("toolbar"));
|
||||
engine._buildGUIDMap();
|
||||
|
||||
store.wipe();
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_bookmark_create() {
|
||||
|
|
@ -81,8 +80,8 @@ add_test(function test_bookmark_create() {
|
|||
_("Have the store create a new record object. Verify that it has the same data.");
|
||||
let newrecord = store.createRecord(fxrecord.id);
|
||||
do_check_true(newrecord instanceof Bookmark);
|
||||
for (let property of ["type", "bmkUri", "description", "title",
|
||||
"keyword", "parentName", "parentid"]) {
|
||||
for each (let property in ["type", "bmkUri", "description", "title",
|
||||
"keyword", "parentName", "parentid"]) {
|
||||
do_check_eq(newrecord[property], fxrecord[property]);
|
||||
}
|
||||
do_check_true(Utils.deepEquals(newrecord.tags.sort(),
|
||||
|
|
@ -167,7 +166,7 @@ add_test(function test_bookmark_createRecord() {
|
|||
|
||||
_("Verify that the record is created accordingly.");
|
||||
let record = store.createRecord(bmk1_guid);
|
||||
do_check_eq(record.title, "");
|
||||
do_check_eq(record.title, null);
|
||||
do_check_eq(record.description, null);
|
||||
do_check_eq(record.keyword, null);
|
||||
|
||||
|
|
@ -198,7 +197,7 @@ add_test(function test_folder_create() {
|
|||
_("Have the store create a new record object. Verify that it has the same data.");
|
||||
let newrecord = store.createRecord(folder.id);
|
||||
do_check_true(newrecord instanceof BookmarkFolder);
|
||||
for (let property of ["title", "parentName", "parentid"])
|
||||
for each (let property in ["title", "parentName", "parentid"])
|
||||
do_check_eq(newrecord[property], folder[property]);
|
||||
|
||||
_("Folders have high sort index to ensure they're synced first.");
|
||||
|
|
@ -244,7 +243,7 @@ add_test(function test_folder_createRecord() {
|
|||
}
|
||||
});
|
||||
|
||||
add_task(function* test_deleted() {
|
||||
add_test(function test_deleted() {
|
||||
try {
|
||||
_("Create a bookmark that will be deleted.");
|
||||
let bmk1_id = PlacesUtils.bookmarks.insertBookmark(
|
||||
|
|
@ -256,7 +255,7 @@ add_task(function* test_deleted() {
|
|||
let record = new PlacesItem("bookmarks", bmk1_guid);
|
||||
record.deleted = true;
|
||||
store.applyIncoming(record);
|
||||
yield store.deletePending();
|
||||
|
||||
_("Ensure it has been deleted.");
|
||||
let error;
|
||||
try {
|
||||
|
|
@ -272,6 +271,7 @@ add_task(function* test_deleted() {
|
|||
} finally {
|
||||
_("Clean up.");
|
||||
store.wipe();
|
||||
run_next_test();
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -428,106 +428,6 @@ add_test(function test_empty_query_doesnt_die() {
|
|||
run_next_test();
|
||||
});
|
||||
|
||||
function assertDeleted(id) {
|
||||
let error;
|
||||
try {
|
||||
PlacesUtils.bookmarks.getItemType(id);
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
||||
equal(error.result, Cr.NS_ERROR_ILLEGAL_VALUE)
|
||||
}
|
||||
|
||||
add_task(function* test_delete_buffering() {
|
||||
store.wipe();
|
||||
try {
|
||||
_("Create a folder with two bookmarks.");
|
||||
let folder = new BookmarkFolder("bookmarks", "testfolder-1");
|
||||
folder.parentName = "Bookmarks Toolbar";
|
||||
folder.parentid = "toolbar";
|
||||
folder.title = "Test Folder";
|
||||
store.applyIncoming(folder);
|
||||
|
||||
|
||||
let fxRecord = new Bookmark("bookmarks", "get-firefox1");
|
||||
fxRecord.bmkUri = fxuri.spec;
|
||||
fxRecord.title = "Get Firefox!";
|
||||
fxRecord.parentName = "Test Folder";
|
||||
fxRecord.parentid = "testfolder-1";
|
||||
|
||||
let tbRecord = new Bookmark("bookmarks", "get-tndrbrd1");
|
||||
tbRecord.bmkUri = tburi.spec;
|
||||
tbRecord.title = "Get Thunderbird!";
|
||||
tbRecord.parentName = "Test Folder";
|
||||
tbRecord.parentid = "testfolder-1";
|
||||
|
||||
store.applyIncoming(fxRecord);
|
||||
store.applyIncoming(tbRecord);
|
||||
|
||||
let folderId = store.idForGUID(folder.id);
|
||||
let fxRecordId = store.idForGUID(fxRecord.id);
|
||||
let tbRecordId = store.idForGUID(tbRecord.id);
|
||||
|
||||
_("Check everything was created correctly.");
|
||||
|
||||
equal(PlacesUtils.bookmarks.getItemType(fxRecordId),
|
||||
PlacesUtils.bookmarks.TYPE_BOOKMARK);
|
||||
equal(PlacesUtils.bookmarks.getItemType(tbRecordId),
|
||||
PlacesUtils.bookmarks.TYPE_BOOKMARK);
|
||||
equal(PlacesUtils.bookmarks.getItemType(folderId),
|
||||
PlacesUtils.bookmarks.TYPE_FOLDER);
|
||||
|
||||
equal(PlacesUtils.bookmarks.getFolderIdForItem(fxRecordId), folderId);
|
||||
equal(PlacesUtils.bookmarks.getFolderIdForItem(tbRecordId), folderId);
|
||||
equal(PlacesUtils.bookmarks.getFolderIdForItem(folderId),
|
||||
PlacesUtils.bookmarks.toolbarFolder);
|
||||
|
||||
_("Delete the folder and one bookmark.");
|
||||
|
||||
let deleteFolder = new PlacesItem("bookmarks", "testfolder-1");
|
||||
deleteFolder.deleted = true;
|
||||
|
||||
let deleteFxRecord = new PlacesItem("bookmarks", "get-firefox1");
|
||||
deleteFxRecord.deleted = true;
|
||||
|
||||
store.applyIncoming(deleteFolder);
|
||||
store.applyIncoming(deleteFxRecord);
|
||||
|
||||
_("Check that we haven't deleted them yet, but that the deletions are queued");
|
||||
// these will throw if we've deleted them
|
||||
equal(PlacesUtils.bookmarks.getItemType(fxRecordId),
|
||||
PlacesUtils.bookmarks.TYPE_BOOKMARK);
|
||||
|
||||
equal(PlacesUtils.bookmarks.getItemType(folderId),
|
||||
PlacesUtils.bookmarks.TYPE_FOLDER);
|
||||
|
||||
equal(PlacesUtils.bookmarks.getFolderIdForItem(fxRecordId), folderId);
|
||||
|
||||
ok(store._foldersToDelete.has(folder.id));
|
||||
ok(store._atomsToDelete.has(fxRecord.id));
|
||||
ok(!store._atomsToDelete.has(tbRecord.id));
|
||||
|
||||
_("Process pending deletions and ensure that the right things are deleted.");
|
||||
let updatedGuids = yield store.deletePending();
|
||||
|
||||
deepEqual(updatedGuids.sort(), ["get-tndrbrd1", "toolbar"]);
|
||||
|
||||
assertDeleted(fxRecordId);
|
||||
assertDeleted(folderId);
|
||||
|
||||
ok(!store._foldersToDelete.has(folder.id));
|
||||
ok(!store._atomsToDelete.has(fxRecord.id));
|
||||
|
||||
equal(PlacesUtils.bookmarks.getFolderIdForItem(tbRecordId),
|
||||
PlacesUtils.bookmarks.toolbarFolder);
|
||||
|
||||
} finally {
|
||||
_("Clean up.");
|
||||
store.wipe();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
function run_test() {
|
||||
initTestLogging('Trace');
|
||||
run_next_test();
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,347 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
Components.utils.import("resource://services-sync/bookmark_validator.js");
|
||||
Components.utils.import("resource://services-sync/util.js");
|
||||
|
||||
function inspectServerRecords(data) {
|
||||
return new BookmarkValidator().inspectServerRecords(data);
|
||||
}
|
||||
|
||||
add_test(function test_isr_rootOnServer() {
|
||||
let c = inspectServerRecords([{
|
||||
id: 'places',
|
||||
type: 'folder',
|
||||
children: [],
|
||||
}]);
|
||||
ok(c.problemData.rootOnServer);
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_isr_empty() {
|
||||
let c = inspectServerRecords([]);
|
||||
ok(!c.problemData.rootOnServer);
|
||||
notEqual(c.root, null);
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_isr_cycles() {
|
||||
let c = inspectServerRecords([
|
||||
{id: 'C', type: 'folder', children: ['A', 'B'], parentid: 'places'},
|
||||
{id: 'A', type: 'folder', children: ['B'], parentid: 'B'},
|
||||
{id: 'B', type: 'folder', children: ['A'], parentid: 'A'},
|
||||
]).problemData;
|
||||
|
||||
equal(c.cycles.length, 1);
|
||||
ok(c.cycles[0].indexOf('A') >= 0);
|
||||
ok(c.cycles[0].indexOf('B') >= 0);
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_isr_orphansMultiParents() {
|
||||
let c = inspectServerRecords([
|
||||
{ id: 'A', type: 'bookmark', parentid: 'D' },
|
||||
{ id: 'B', type: 'folder', parentid: 'places', children: ['A']},
|
||||
{ id: 'C', type: 'folder', parentid: 'places', children: ['A']},
|
||||
|
||||
]).problemData;
|
||||
deepEqual(c.orphans, [{ id: "A", parent: "D" }]);
|
||||
equal(c.multipleParents.length, 1)
|
||||
ok(c.multipleParents[0].parents.indexOf('B') >= 0);
|
||||
ok(c.multipleParents[0].parents.indexOf('C') >= 0);
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_isr_orphansMultiParents2() {
|
||||
let c = inspectServerRecords([
|
||||
{ id: 'A', type: 'bookmark', parentid: 'D' },
|
||||
{ id: 'B', type: 'folder', parentid: 'places', children: ['A']},
|
||||
]).problemData;
|
||||
equal(c.orphans.length, 1);
|
||||
equal(c.orphans[0].id, 'A');
|
||||
equal(c.multipleParents.length, 0);
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_isr_deletedParents() {
|
||||
let c = inspectServerRecords([
|
||||
{ id: 'A', type: 'bookmark', parentid: 'B' },
|
||||
{ id: 'B', type: 'folder', parentid: 'places', children: ['A']},
|
||||
{ id: 'B', type: 'item', deleted: true},
|
||||
]).problemData;
|
||||
deepEqual(c.deletedParents, ['A'])
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_isr_badChildren() {
|
||||
let c = inspectServerRecords([
|
||||
{ id: 'A', type: 'bookmark', parentid: 'places', children: ['B', 'C'] },
|
||||
{ id: 'C', type: 'bookmark', parentid: 'A' }
|
||||
]).problemData;
|
||||
deepEqual(c.childrenOnNonFolder, ['A'])
|
||||
deepEqual(c.missingChildren, [{parent: 'A', child: 'B'}]);
|
||||
deepEqual(c.parentNotFolder, ['C']);
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
|
||||
add_test(function test_isr_parentChildMismatches() {
|
||||
let c = inspectServerRecords([
|
||||
{ id: 'A', type: 'folder', parentid: 'places', children: [] },
|
||||
{ id: 'B', type: 'bookmark', parentid: 'A' }
|
||||
]).problemData;
|
||||
deepEqual(c.parentChildMismatches, [{parent: 'A', child: 'B'}]);
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_isr_duplicatesAndMissingIDs() {
|
||||
let c = inspectServerRecords([
|
||||
{id: 'A', type: 'folder', parentid: 'places', children: []},
|
||||
{id: 'A', type: 'folder', parentid: 'places', children: []},
|
||||
{type: 'folder', parentid: 'places', children: []}
|
||||
]).problemData;
|
||||
equal(c.missingIDs, 1);
|
||||
deepEqual(c.duplicates, ['A']);
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_isr_duplicateChildren() {
|
||||
let c = inspectServerRecords([
|
||||
{id: 'A', type: 'folder', parentid: 'places', children: ['B', 'B']},
|
||||
{id: 'B', type: 'bookmark', parentid: 'A'},
|
||||
]).problemData;
|
||||
deepEqual(c.duplicateChildren, ['A']);
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
// Each compareServerWithClient test mutates these, so we can't just keep them
|
||||
// global
|
||||
function getDummyServerAndClient() {
|
||||
let server = [
|
||||
{
|
||||
id: 'menu',
|
||||
parentid: 'places',
|
||||
type: 'folder',
|
||||
parentName: '',
|
||||
title: 'foo',
|
||||
children: ['bbbbbbbbbbbb', 'cccccccccccc']
|
||||
},
|
||||
{
|
||||
id: 'bbbbbbbbbbbb',
|
||||
type: 'bookmark',
|
||||
parentid: 'menu',
|
||||
parentName: 'foo',
|
||||
title: 'bar',
|
||||
bmkUri: 'http://baz.com'
|
||||
},
|
||||
{
|
||||
id: 'cccccccccccc',
|
||||
parentid: 'menu',
|
||||
parentName: 'foo',
|
||||
title: '',
|
||||
type: 'query',
|
||||
bmkUri: 'place:type=6&sort=14&maxResults=10'
|
||||
}
|
||||
];
|
||||
|
||||
let client = {
|
||||
"guid": "root________",
|
||||
"title": "",
|
||||
"id": 1,
|
||||
"type": "text/x-moz-place-container",
|
||||
"children": [
|
||||
{
|
||||
"guid": "menu________",
|
||||
"title": "foo",
|
||||
"id": 1000,
|
||||
"type": "text/x-moz-place-container",
|
||||
"children": [
|
||||
{
|
||||
"guid": "bbbbbbbbbbbb",
|
||||
"title": "bar",
|
||||
"id": 1001,
|
||||
"type": "text/x-moz-place",
|
||||
"uri": "http://baz.com"
|
||||
},
|
||||
{
|
||||
"guid": "cccccccccccc",
|
||||
"title": "",
|
||||
"id": 1002,
|
||||
"annos": [{
|
||||
"name": "Places/SmartBookmark",
|
||||
"flags": 0,
|
||||
"expires": 4,
|
||||
"value": "RecentTags"
|
||||
}],
|
||||
"type": "text/x-moz-place",
|
||||
"uri": "place:type=6&sort=14&maxResults=10"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
return {server, client};
|
||||
}
|
||||
|
||||
|
||||
add_test(function test_cswc_valid() {
|
||||
let {server, client} = getDummyServerAndClient();
|
||||
|
||||
let c = new BookmarkValidator().compareServerWithClient(server, client).problemData;
|
||||
equal(c.clientMissing.length, 0);
|
||||
equal(c.serverMissing.length, 0);
|
||||
equal(c.differences.length, 0);
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_cswc_serverMissing() {
|
||||
let {server, client} = getDummyServerAndClient();
|
||||
// remove c
|
||||
server.pop();
|
||||
server[0].children.pop();
|
||||
|
||||
let c = new BookmarkValidator().compareServerWithClient(server, client).problemData;
|
||||
deepEqual(c.serverMissing, ['cccccccccccc']);
|
||||
equal(c.clientMissing.length, 0);
|
||||
deepEqual(c.structuralDifferences, [{id: 'menu', differences: ['childGUIDs']}]);
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_cswc_clientMissing() {
|
||||
let {server, client} = getDummyServerAndClient();
|
||||
client.children[0].children.pop();
|
||||
|
||||
let c = new BookmarkValidator().compareServerWithClient(server, client).problemData;
|
||||
deepEqual(c.clientMissing, ['cccccccccccc']);
|
||||
equal(c.serverMissing.length, 0);
|
||||
deepEqual(c.structuralDifferences, [{id: 'menu', differences: ['childGUIDs']}]);
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_cswc_differences() {
|
||||
{
|
||||
let {server, client} = getDummyServerAndClient();
|
||||
client.children[0].children[0].title = 'asdf';
|
||||
let c = new BookmarkValidator().compareServerWithClient(server, client).problemData;
|
||||
equal(c.clientMissing.length, 0);
|
||||
equal(c.serverMissing.length, 0);
|
||||
deepEqual(c.differences, [{id: 'bbbbbbbbbbbb', differences: ['title']}]);
|
||||
}
|
||||
|
||||
{
|
||||
let {server, client} = getDummyServerAndClient();
|
||||
server[2].type = 'bookmark';
|
||||
let c = new BookmarkValidator().compareServerWithClient(server, client).problemData;
|
||||
equal(c.clientMissing.length, 0);
|
||||
equal(c.serverMissing.length, 0);
|
||||
deepEqual(c.differences, [{id: 'cccccccccccc', differences: ['type']}]);
|
||||
}
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_cswc_serverUnexpected() {
|
||||
let {server, client} = getDummyServerAndClient();
|
||||
client.children.push({
|
||||
"guid": "dddddddddddd",
|
||||
"title": "",
|
||||
"id": 2000,
|
||||
"annos": [{
|
||||
"name": "places/excludeFromBackup",
|
||||
"flags": 0,
|
||||
"expires": 4,
|
||||
"value": 1
|
||||
}, {
|
||||
"name": "PlacesOrganizer/OrganizerFolder",
|
||||
"flags": 0,
|
||||
"expires": 4,
|
||||
"value": 7
|
||||
}],
|
||||
"type": "text/x-moz-place-container",
|
||||
"children": [{
|
||||
"guid": "eeeeeeeeeeee",
|
||||
"title": "History",
|
||||
"annos": [{
|
||||
"name": "places/excludeFromBackup",
|
||||
"flags": 0,
|
||||
"expires": 4,
|
||||
"value": 1
|
||||
}, {
|
||||
"name": "PlacesOrganizer/OrganizerQuery",
|
||||
"flags": 0,
|
||||
"expires": 4,
|
||||
"value": "History"
|
||||
}],
|
||||
"type": "text/x-moz-place",
|
||||
"uri": "place:type=3&sort=4"
|
||||
}]
|
||||
});
|
||||
server.push({
|
||||
id: 'dddddddddddd',
|
||||
parentid: 'places',
|
||||
parentName: '',
|
||||
title: '',
|
||||
type: 'folder',
|
||||
children: ['eeeeeeeeeeee']
|
||||
}, {
|
||||
id: 'eeeeeeeeeeee',
|
||||
parentid: 'dddddddddddd',
|
||||
parentName: '',
|
||||
title: 'History',
|
||||
type: 'query',
|
||||
bmkUri: 'place:type=3&sort=4'
|
||||
});
|
||||
|
||||
let c = new BookmarkValidator().compareServerWithClient(server, client).problemData;
|
||||
equal(c.clientMissing.length, 0);
|
||||
equal(c.serverMissing.length, 0);
|
||||
equal(c.serverUnexpected.length, 2);
|
||||
deepEqual(c.serverUnexpected, ["dddddddddddd", "eeeeeeeeeeee"]);
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
function validationPing(server, client, duration) {
|
||||
return wait_for_ping(function() {
|
||||
// fake this entirely
|
||||
Svc.Obs.notify("weave:service:sync:start");
|
||||
Svc.Obs.notify("weave:engine:sync:start", null, "bookmarks");
|
||||
Svc.Obs.notify("weave:engine:sync:finish", null, "bookmarks");
|
||||
let validator = new BookmarkValidator();
|
||||
let data = {
|
||||
// We fake duration and version just so that we can verify they're passed through.
|
||||
duration,
|
||||
version: validator.version,
|
||||
recordCount: server.length,
|
||||
problems: validator.compareServerWithClient(server, client).problemData,
|
||||
};
|
||||
Svc.Obs.notify("weave:engine:validate:finish", data, "bookmarks");
|
||||
Svc.Obs.notify("weave:service:sync:finish");
|
||||
}, true); // Allow "failing" pings, since having validation info indicates failure.
|
||||
}
|
||||
|
||||
add_task(function *test_telemetry_integration() {
|
||||
let {server, client} = getDummyServerAndClient();
|
||||
// remove "c"
|
||||
server.pop();
|
||||
server[0].children.pop();
|
||||
const duration = 50;
|
||||
let ping = yield validationPing(server, client, duration);
|
||||
ok(ping.engines);
|
||||
let bme = ping.engines.find(e => e.name === "bookmarks");
|
||||
ok(bme);
|
||||
ok(bme.validation);
|
||||
ok(bme.validation.problems)
|
||||
equal(bme.validation.checked, server.length);
|
||||
equal(bme.validation.took, duration);
|
||||
bme.validation.problems.sort((a, b) => String.localeCompare(a.name, b.name));
|
||||
equal(bme.validation.version, new BookmarkValidator().version);
|
||||
deepEqual(bme.validation.problems, [
|
||||
{ name: "badClientRoots", count: 3 },
|
||||
{ name: "sdiff:childGUIDs", count: 1 },
|
||||
{ name: "serverMissing", count: 1 },
|
||||
{ name: "structuralDifferences", count: 1 },
|
||||
]);
|
||||
});
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
|
@ -16,14 +16,13 @@ Cu.import("resource://gre/modules/FxAccountsCommon.js");
|
|||
Cu.import("resource://services-sync/service.js");
|
||||
Cu.import("resource://services-sync/status.js");
|
||||
Cu.import("resource://services-sync/constants.js");
|
||||
Cu.import("resource://services-common/tokenserverclient.js");
|
||||
|
||||
const SECOND_MS = 1000;
|
||||
const MINUTE_MS = SECOND_MS * 60;
|
||||
const HOUR_MS = MINUTE_MS * 60;
|
||||
|
||||
var identityConfig = makeIdentityConfig();
|
||||
var browseridManager = new BrowserIDManager();
|
||||
let identityConfig = makeIdentityConfig();
|
||||
let browseridManager = new BrowserIDManager();
|
||||
configureFxAccountIdentity(browseridManager, identityConfig);
|
||||
|
||||
/**
|
||||
|
|
@ -32,14 +31,11 @@ configureFxAccountIdentity(browseridManager, identityConfig);
|
|||
* headers. We will use this to test clock skew compensation in these headers
|
||||
* below.
|
||||
*/
|
||||
var MockFxAccountsClient = function() {
|
||||
let MockFxAccountsClient = function() {
|
||||
FxAccountsClient.apply(this);
|
||||
};
|
||||
MockFxAccountsClient.prototype = {
|
||||
__proto__: FxAccountsClient.prototype,
|
||||
accountStatus() {
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
__proto__: FxAccountsClient.prototype
|
||||
};
|
||||
|
||||
function MockFxAccounts() {
|
||||
|
|
@ -77,7 +73,7 @@ add_test(function test_initial_state() {
|
|||
}
|
||||
);
|
||||
|
||||
add_task(function* test_initialializeWithCurrentIdentity() {
|
||||
add_task(function test_initialializeWithCurrentIdentity() {
|
||||
_("Verify start after initializeWithCurrentIdentity");
|
||||
browseridManager.initializeWithCurrentIdentity();
|
||||
yield browseridManager.whenReadyToAuthenticate.promise;
|
||||
|
|
@ -87,57 +83,7 @@ add_task(function* test_initialializeWithCurrentIdentity() {
|
|||
}
|
||||
);
|
||||
|
||||
add_task(function* test_initialializeWithAuthErrorAndDeletedAccount() {
|
||||
_("Verify sync unpair after initializeWithCurrentIdentity with auth error + account deleted");
|
||||
|
||||
var identityConfig = makeIdentityConfig();
|
||||
var browseridManager = new BrowserIDManager();
|
||||
|
||||
// Use the real `_getAssertion` method that calls
|
||||
// `mockFxAClient.signCertificate`.
|
||||
let fxaInternal = makeFxAccountsInternalMock(identityConfig);
|
||||
delete fxaInternal._getAssertion;
|
||||
|
||||
configureFxAccountIdentity(browseridManager, identityConfig, fxaInternal);
|
||||
browseridManager._fxaService.internal.initialize();
|
||||
|
||||
let signCertificateCalled = false;
|
||||
let accountStatusCalled = false;
|
||||
|
||||
let MockFxAccountsClient = function() {
|
||||
FxAccountsClient.apply(this);
|
||||
};
|
||||
MockFxAccountsClient.prototype = {
|
||||
__proto__: FxAccountsClient.prototype,
|
||||
signCertificate() {
|
||||
signCertificateCalled = true;
|
||||
return Promise.reject({
|
||||
code: 401,
|
||||
errno: ERRNO_INVALID_AUTH_TOKEN,
|
||||
});
|
||||
},
|
||||
accountStatus() {
|
||||
accountStatusCalled = true;
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
};
|
||||
|
||||
let mockFxAClient = new MockFxAccountsClient();
|
||||
browseridManager._fxaService.internal._fxAccountsClient = mockFxAClient;
|
||||
|
||||
yield browseridManager.initializeWithCurrentIdentity();
|
||||
yield Assert.rejects(browseridManager.whenReadyToAuthenticate.promise,
|
||||
"should reject due to an auth error");
|
||||
|
||||
do_check_true(signCertificateCalled);
|
||||
do_check_true(accountStatusCalled);
|
||||
do_check_false(browseridManager.account);
|
||||
do_check_false(browseridManager._token);
|
||||
do_check_false(browseridManager.hasValidToken());
|
||||
do_check_false(browseridManager.account);
|
||||
});
|
||||
|
||||
add_task(function* test_initialializeWithNoKeys() {
|
||||
add_task(function test_initialializeWithNoKeys() {
|
||||
_("Verify start after initializeWithCurrentIdentity without kA, kB or keyFetchToken");
|
||||
let identityConfig = makeIdentityConfig();
|
||||
delete identityConfig.fxaccount.user.kA;
|
||||
|
|
@ -306,7 +252,7 @@ add_test(function test_RESTResourceAuthenticatorSkew() {
|
|||
run_next_test();
|
||||
});
|
||||
|
||||
add_task(function* test_ensureLoggedIn() {
|
||||
add_task(function test_ensureLoggedIn() {
|
||||
configureFxAccountIdentity(browseridManager);
|
||||
yield browseridManager.initializeWithCurrentIdentity();
|
||||
yield browseridManager.whenReadyToAuthenticate.promise;
|
||||
|
|
@ -318,8 +264,8 @@ add_task(function* test_ensureLoggedIn() {
|
|||
|
||||
// arrange for no logged in user.
|
||||
let fxa = browseridManager._fxaService
|
||||
let signedInUser = fxa.internal.currentAccountState.storageManager.accountData;
|
||||
fxa.internal.currentAccountState.storageManager.accountData = null;
|
||||
let signedInUser = fxa.internal.currentAccountState.signedInUser;
|
||||
fxa.internal.currentAccountState.signedInUser = null;
|
||||
browseridManager.initializeWithCurrentIdentity();
|
||||
Assert.ok(!browseridManager._shouldHaveSyncKeyBundle,
|
||||
"_shouldHaveSyncKeyBundle should be false so we know we are testing what we think we are.");
|
||||
|
|
@ -327,8 +273,7 @@ add_task(function* test_ensureLoggedIn() {
|
|||
yield Assert.rejects(browseridManager.ensureLoggedIn(), "expecting rejection due to no user");
|
||||
Assert.ok(browseridManager._shouldHaveSyncKeyBundle,
|
||||
"_shouldHaveSyncKeyBundle should always be true after ensureLogin completes.");
|
||||
// Restore the logged in user to what it was.
|
||||
fxa.internal.currentAccountState.storageManager.accountData = signedInUser;
|
||||
fxa.internal.currentAccountState.signedInUser = signedInUser;
|
||||
Status.login = LOGIN_FAILED_LOGIN_REJECTED;
|
||||
yield Assert.rejects(browseridManager.ensureLoggedIn(),
|
||||
"LOGIN_FAILED_LOGIN_REJECTED should have caused immediate rejection");
|
||||
|
|
@ -404,7 +349,7 @@ add_test(function test_computeXClientStateHeader() {
|
|||
run_next_test();
|
||||
});
|
||||
|
||||
add_task(function* test_getTokenErrors() {
|
||||
add_task(function test_getTokenErrors() {
|
||||
_("BrowserIDManager correctly handles various failures to get a token.");
|
||||
|
||||
_("Arrange for a 401 - Sync should reflect an auth error.");
|
||||
|
|
@ -437,75 +382,7 @@ add_task(function* test_getTokenErrors() {
|
|||
Assert.equal(Status.login, LOGIN_FAILED_NETWORK_ERROR, "login state is LOGIN_FAILED_NETWORK_ERROR");
|
||||
});
|
||||
|
||||
add_task(function* test_refreshCertificateOn401() {
|
||||
_("BrowserIDManager refreshes the FXA certificate after a 401.");
|
||||
var identityConfig = makeIdentityConfig();
|
||||
var browseridManager = new BrowserIDManager();
|
||||
// Use the real `_getAssertion` method that calls
|
||||
// `mockFxAClient.signCertificate`.
|
||||
let fxaInternal = makeFxAccountsInternalMock(identityConfig);
|
||||
delete fxaInternal._getAssertion;
|
||||
configureFxAccountIdentity(browseridManager, identityConfig, fxaInternal);
|
||||
browseridManager._fxaService.internal.initialize();
|
||||
|
||||
let getCertCount = 0;
|
||||
|
||||
let MockFxAccountsClient = function() {
|
||||
FxAccountsClient.apply(this);
|
||||
};
|
||||
MockFxAccountsClient.prototype = {
|
||||
__proto__: FxAccountsClient.prototype,
|
||||
signCertificate() {
|
||||
++getCertCount;
|
||||
}
|
||||
};
|
||||
|
||||
let mockFxAClient = new MockFxAccountsClient();
|
||||
browseridManager._fxaService.internal._fxAccountsClient = mockFxAClient;
|
||||
|
||||
let didReturn401 = false;
|
||||
let didReturn200 = false;
|
||||
let mockTSC = mockTokenServer(() => {
|
||||
if (getCertCount <= 1) {
|
||||
didReturn401 = true;
|
||||
return {
|
||||
status: 401,
|
||||
headers: {"content-type": "application/json"},
|
||||
body: JSON.stringify({}),
|
||||
};
|
||||
} else {
|
||||
didReturn200 = true;
|
||||
return {
|
||||
status: 200,
|
||||
headers: {"content-type": "application/json"},
|
||||
body: JSON.stringify({
|
||||
id: "id",
|
||||
key: "key",
|
||||
api_endpoint: "http://example.com/",
|
||||
uid: "uid",
|
||||
duration: 300,
|
||||
})
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
browseridManager._tokenServerClient = mockTSC;
|
||||
|
||||
yield browseridManager.initializeWithCurrentIdentity();
|
||||
yield browseridManager.whenReadyToAuthenticate.promise;
|
||||
|
||||
do_check_eq(getCertCount, 2);
|
||||
do_check_true(didReturn401);
|
||||
do_check_true(didReturn200);
|
||||
do_check_true(browseridManager.account);
|
||||
do_check_true(browseridManager._token);
|
||||
do_check_true(browseridManager.hasValidToken());
|
||||
do_check_true(browseridManager.account);
|
||||
});
|
||||
|
||||
|
||||
|
||||
add_task(function* test_getTokenErrorWithRetry() {
|
||||
add_task(function test_getTokenErrorWithRetry() {
|
||||
_("tokenserver sends an observer notification on various backoff headers.");
|
||||
|
||||
// Set Sync's backoffInterval to zero - after we simulated the backoff header
|
||||
|
|
@ -547,7 +424,7 @@ add_task(function* test_getTokenErrorWithRetry() {
|
|||
Assert.ok(Status.backoffInterval >= 200000);
|
||||
});
|
||||
|
||||
add_task(function* test_getKeysErrorWithBackoff() {
|
||||
add_task(function test_getKeysErrorWithBackoff() {
|
||||
_("Auth server (via hawk) sends an observer notification on backoff headers.");
|
||||
|
||||
// Set Sync's backoffInterval to zero - after we simulated the backoff header
|
||||
|
|
@ -581,7 +458,7 @@ add_task(function* test_getKeysErrorWithBackoff() {
|
|||
Assert.ok(Status.backoffInterval >= 100000);
|
||||
});
|
||||
|
||||
add_task(function* test_getKeysErrorWithRetry() {
|
||||
add_task(function test_getKeysErrorWithRetry() {
|
||||
_("Auth server (via hawk) sends an observer notification on retry headers.");
|
||||
|
||||
// Set Sync's backoffInterval to zero - after we simulated the backoff header
|
||||
|
|
@ -615,7 +492,7 @@ add_task(function* test_getKeysErrorWithRetry() {
|
|||
Assert.ok(Status.backoffInterval >= 100000);
|
||||
});
|
||||
|
||||
add_task(function* test_getHAWKErrors() {
|
||||
add_task(function test_getHAWKErrors() {
|
||||
_("BrowserIDManager correctly handles various HAWK failures.");
|
||||
|
||||
_("Arrange for a 401 - Sync should reflect an auth error.");
|
||||
|
|
@ -648,7 +525,7 @@ add_task(function* test_getHAWKErrors() {
|
|||
Assert.equal(Status.login, LOGIN_FAILED_NETWORK_ERROR, "login state is LOGIN_FAILED_NETWORK_ERROR");
|
||||
});
|
||||
|
||||
add_task(function* test_getGetKeysFailing401() {
|
||||
add_task(function test_getGetKeysFailing401() {
|
||||
_("BrowserIDManager correctly handles 401 responses fetching keys.");
|
||||
|
||||
_("Arrange for a 401 - Sync should reflect an auth error.");
|
||||
|
|
@ -669,7 +546,7 @@ add_task(function* test_getGetKeysFailing401() {
|
|||
Assert.equal(Status.login, LOGIN_FAILED_LOGIN_REJECTED, "login was rejected");
|
||||
});
|
||||
|
||||
add_task(function* test_getGetKeysFailing503() {
|
||||
add_task(function test_getGetKeysFailing503() {
|
||||
_("BrowserIDManager correctly handles 5XX responses fetching keys.");
|
||||
|
||||
_("Arrange for a 503 - Sync should reflect a network error.");
|
||||
|
|
@ -690,7 +567,7 @@ add_task(function* test_getGetKeysFailing503() {
|
|||
Assert.equal(Status.login, LOGIN_FAILED_NETWORK_ERROR, "state reflects network error");
|
||||
});
|
||||
|
||||
add_task(function* test_getKeysMissing() {
|
||||
add_task(function test_getKeysMissing() {
|
||||
_("BrowserIDManager correctly handles getKeys succeeding but not returning keys.");
|
||||
|
||||
let browseridManager = new BrowserIDManager();
|
||||
|
|
@ -708,17 +585,7 @@ add_task(function* test_getKeysMissing() {
|
|||
fetchAndUnwrapKeys: function () {
|
||||
return Promise.resolve({});
|
||||
},
|
||||
fxAccountsClient: new MockFxAccountsClient(),
|
||||
newAccountState(credentials) {
|
||||
// We only expect this to be called with null indicating the (mock)
|
||||
// storage should be read.
|
||||
if (credentials) {
|
||||
throw new Error("Not expecting to have credentials passed");
|
||||
}
|
||||
let storageManager = new MockFxaStorageManager();
|
||||
storageManager.initialize(identityConfig.fxaccount.user);
|
||||
return new AccountState(storageManager);
|
||||
},
|
||||
fxAccountsClient: new MockFxAccountsClient()
|
||||
});
|
||||
|
||||
// Add a mock to the currentAccountState object.
|
||||
|
|
@ -730,6 +597,9 @@ add_task(function* test_getKeysMissing() {
|
|||
return Promise.resolve(this.cert.cert);
|
||||
};
|
||||
|
||||
// Ensure the new FxAccounts mock has a signed-in user.
|
||||
fxa.internal.currentAccountState.signedInUser = browseridManager._fxaService.internal.currentAccountState.signedInUser;
|
||||
|
||||
browseridManager._fxaService = fxa;
|
||||
|
||||
yield browseridManager.initializeWithCurrentIdentity();
|
||||
|
|
@ -744,41 +614,6 @@ add_task(function* test_getKeysMissing() {
|
|||
Assert.ok(ex.message.indexOf("missing kA or kB") >= 0);
|
||||
});
|
||||
|
||||
add_task(function* test_signedInUserMissing() {
|
||||
_("BrowserIDManager detects getSignedInUser returning incomplete account data");
|
||||
|
||||
let browseridManager = new BrowserIDManager();
|
||||
let config = makeIdentityConfig();
|
||||
// Delete stored keys and the key fetch token.
|
||||
delete identityConfig.fxaccount.user.kA;
|
||||
delete identityConfig.fxaccount.user.kB;
|
||||
delete identityConfig.fxaccount.user.keyFetchToken;
|
||||
|
||||
configureFxAccountIdentity(browseridManager, identityConfig);
|
||||
|
||||
let fxa = new FxAccounts({
|
||||
fetchAndUnwrapKeys: function () {
|
||||
return Promise.resolve({});
|
||||
},
|
||||
fxAccountsClient: new MockFxAccountsClient(),
|
||||
newAccountState(credentials) {
|
||||
// We only expect this to be called with null indicating the (mock)
|
||||
// storage should be read.
|
||||
if (credentials) {
|
||||
throw new Error("Not expecting to have credentials passed");
|
||||
}
|
||||
let storageManager = new MockFxaStorageManager();
|
||||
storageManager.initialize(identityConfig.fxaccount.user);
|
||||
return new AccountState(storageManager);
|
||||
},
|
||||
});
|
||||
|
||||
browseridManager._fxaService = fxa;
|
||||
|
||||
let status = yield browseridManager.unlockAndVerifyAuthState();
|
||||
Assert.equal(status, LOGIN_FAILED_LOGIN_REJECTED);
|
||||
});
|
||||
|
||||
// End of tests
|
||||
// Utility functions follow
|
||||
|
||||
|
|
@ -803,17 +638,7 @@ function* initializeIdentityWithHAWKResponseFactory(config, cbGetResponse) {
|
|||
callback.call(this);
|
||||
},
|
||||
get: function(callback) {
|
||||
// Skip /status requests (browserid_identity checks if the account still
|
||||
// exists after an auth error)
|
||||
if (this._uri.startsWith("http://mockedserver:9999/account/status")) {
|
||||
this.response = {
|
||||
status: 200,
|
||||
headers: {"content-type": "application/json"},
|
||||
body: JSON.stringify({exists: true}),
|
||||
};
|
||||
} else {
|
||||
this.response = cbGetResponse("get", null, this._uri, this._credentials, this._extra);
|
||||
}
|
||||
this.response = cbGetResponse("get", null, this._uri, this._credentials, this._extra);
|
||||
callback.call(this);
|
||||
}
|
||||
}
|
||||
|
|
@ -833,18 +658,11 @@ function* initializeIdentityWithHAWKResponseFactory(config, cbGetResponse) {
|
|||
fxaClient.hawk = new MockedHawkClient();
|
||||
let internal = {
|
||||
fxAccountsClient: fxaClient,
|
||||
newAccountState(credentials) {
|
||||
// We only expect this to be called with null indicating the (mock)
|
||||
// storage should be read.
|
||||
if (credentials) {
|
||||
throw new Error("Not expecting to have credentials passed");
|
||||
}
|
||||
let storageManager = new MockFxaStorageManager();
|
||||
storageManager.initialize(config.fxaccount.user);
|
||||
return new AccountState(storageManager);
|
||||
},
|
||||
}
|
||||
let fxa = new FxAccounts(internal);
|
||||
fxa.internal.currentAccountState.signedInUser = {
|
||||
accountData: config.fxaccount.user,
|
||||
};
|
||||
|
||||
browseridManager._fxaService = fxa;
|
||||
browseridManager._signedInUser = null;
|
||||
|
|
@ -862,29 +680,3 @@ function getTimestampDelta(hawkAuthHeader, now=Date.now()) {
|
|||
return Math.abs(getTimestamp(hawkAuthHeader) - now);
|
||||
}
|
||||
|
||||
function mockTokenServer(func) {
|
||||
let requestLog = Log.repository.getLogger("testing.mock-rest");
|
||||
if (!requestLog.appenders.length) { // might as well see what it says :)
|
||||
requestLog.addAppender(new Log.DumpAppender());
|
||||
requestLog.level = Log.Level.Trace;
|
||||
}
|
||||
function MockRESTRequest(url) {};
|
||||
MockRESTRequest.prototype = {
|
||||
_log: requestLog,
|
||||
setHeader: function() {},
|
||||
get: function(callback) {
|
||||
this.response = func();
|
||||
callback.call(this);
|
||||
}
|
||||
}
|
||||
// The mocked TokenServer client which will get the response.
|
||||
function MockTSC() { }
|
||||
MockTSC.prototype = new TokenServerClient();
|
||||
MockTSC.prototype.constructor = MockTSC;
|
||||
MockTSC.prototype.newRESTRequest = function(url) {
|
||||
return new MockRESTRequest(url);
|
||||
}
|
||||
// Arrange for the same observerPrefix as browserid_identity uses.
|
||||
MockTSC.prototype.observerPrefix = "weave:service";
|
||||
return new MockTSC();
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,195 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
Cu.import("resource://services-sync/record.js");
|
||||
Cu.import("resource://services-sync/service.js");
|
||||
|
||||
function run_test() {
|
||||
initTestLogging("Trace");
|
||||
Log.repository.getLogger("Sync.Collection").level = Log.Level.Trace;
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
function recordRange(lim, offset, total) {
|
||||
let res = [];
|
||||
for (let i = offset; i < Math.min(lim + offset, total); ++i) {
|
||||
res.push(JSON.stringify({ id: String(i), payload: "test:" + i }));
|
||||
}
|
||||
return res.join("\n") + "\n";
|
||||
}
|
||||
|
||||
function get_test_collection_info({ totalRecords, batchSize, lastModified,
|
||||
throwAfter = Infinity,
|
||||
interruptedAfter = Infinity }) {
|
||||
let coll = new Collection("http://example.com/test/", WBORecord, Service);
|
||||
coll.full = true;
|
||||
let requests = [];
|
||||
let responses = [];
|
||||
let sawRecord = false;
|
||||
coll.get = function() {
|
||||
ok(!sawRecord); // make sure we call record handler after all requests.
|
||||
let limit = +this.limit;
|
||||
let offset = 0;
|
||||
if (this.offset) {
|
||||
equal(this.offset.slice(0, 6), "foobar");
|
||||
offset = +this.offset.slice(6);
|
||||
}
|
||||
requests.push({
|
||||
limit,
|
||||
offset,
|
||||
spec: this.spec,
|
||||
headers: Object.assign({}, this.headers)
|
||||
});
|
||||
if (--throwAfter === 0) {
|
||||
throw "Some Network Error";
|
||||
}
|
||||
let body = recordRange(limit, offset, totalRecords);
|
||||
this._onProgress.call({ _data: body });
|
||||
let response = {
|
||||
body,
|
||||
success: true,
|
||||
status: 200,
|
||||
headers: {}
|
||||
};
|
||||
if (--interruptedAfter === 0) {
|
||||
response.success = false;
|
||||
response.status = 412;
|
||||
response.body = "";
|
||||
} else if (offset + limit < totalRecords) {
|
||||
// Ensure we're treating this as an opaque string, since the docs say
|
||||
// it might not be numeric.
|
||||
response.headers["x-weave-next-offset"] = "foobar" + (offset + batchSize);
|
||||
}
|
||||
response.headers["x-last-modified"] = lastModified;
|
||||
responses.push(response);
|
||||
return response;
|
||||
};
|
||||
|
||||
let records = [];
|
||||
coll.recordHandler = function(record) {
|
||||
sawRecord = true;
|
||||
// ensure records are coming in in the right order
|
||||
equal(record.id, String(records.length));
|
||||
equal(record.payload, "test:" + records.length);
|
||||
records.push(record);
|
||||
};
|
||||
return { records, responses, requests, coll };
|
||||
}
|
||||
|
||||
add_test(function test_success() {
|
||||
const totalRecords = 11;
|
||||
const batchSize = 2;
|
||||
const lastModified = "111111";
|
||||
let { records, responses, requests, coll } = get_test_collection_info({
|
||||
totalRecords,
|
||||
batchSize,
|
||||
lastModified,
|
||||
});
|
||||
let response = coll.getBatched(batchSize);
|
||||
|
||||
equal(requests.length, Math.ceil(totalRecords / batchSize));
|
||||
|
||||
// records are mostly checked in recordHandler, we just care about the length
|
||||
equal(records.length, totalRecords);
|
||||
|
||||
// ensure we're returning the last response
|
||||
equal(responses[responses.length - 1], response);
|
||||
|
||||
// check first separately since its a bit of a special case
|
||||
ok(!requests[0].headers["x-if-unmodified-since"]);
|
||||
ok(!requests[0].offset);
|
||||
equal(requests[0].limit, batchSize);
|
||||
let expectedOffset = 2;
|
||||
for (let i = 1; i < requests.length; ++i) {
|
||||
let req = requests[i];
|
||||
equal(req.headers["x-if-unmodified-since"], lastModified);
|
||||
equal(req.limit, batchSize);
|
||||
if (i !== requests.length - 1) {
|
||||
equal(req.offset, expectedOffset);
|
||||
}
|
||||
|
||||
expectedOffset += batchSize;
|
||||
}
|
||||
|
||||
// ensure we cleaned up anything that would break further
|
||||
// use of this collection.
|
||||
ok(!coll._headers["x-if-unmodified-since"]);
|
||||
ok(!coll.offset);
|
||||
ok(!coll.limit || (coll.limit == Infinity));
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_total_limit() {
|
||||
_("getBatched respects the (initial) value of the limit property");
|
||||
const totalRecords = 100;
|
||||
const recordLimit = 11;
|
||||
const batchSize = 2;
|
||||
const lastModified = "111111";
|
||||
let { records, responses, requests, coll } = get_test_collection_info({
|
||||
totalRecords,
|
||||
batchSize,
|
||||
lastModified,
|
||||
});
|
||||
coll.limit = recordLimit;
|
||||
let response = coll.getBatched(batchSize);
|
||||
|
||||
equal(requests.length, Math.ceil(recordLimit / batchSize));
|
||||
equal(records.length, recordLimit);
|
||||
|
||||
for (let i = 0; i < requests.length; ++i) {
|
||||
let req = requests[i];
|
||||
if (i !== requests.length - 1) {
|
||||
equal(req.limit, batchSize);
|
||||
} else {
|
||||
equal(req.limit, recordLimit % batchSize);
|
||||
}
|
||||
}
|
||||
|
||||
equal(coll._limit, recordLimit);
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_412() {
|
||||
_("We shouldn't record records if we get a 412 in the middle of a batch");
|
||||
const totalRecords = 11;
|
||||
const batchSize = 2;
|
||||
const lastModified = "111111";
|
||||
let { records, responses, requests, coll } = get_test_collection_info({
|
||||
totalRecords,
|
||||
batchSize,
|
||||
lastModified,
|
||||
interruptedAfter: 3
|
||||
});
|
||||
let response = coll.getBatched(batchSize);
|
||||
|
||||
equal(requests.length, 3);
|
||||
equal(records.length, 0); // record handler shouldn't be called for anything
|
||||
|
||||
// ensure we're returning the last response
|
||||
equal(responses[responses.length - 1], response);
|
||||
|
||||
ok(!response.success);
|
||||
equal(response.status, 412);
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_get_throws() {
|
||||
_("We shouldn't record records if get() throws for some reason");
|
||||
const totalRecords = 11;
|
||||
const batchSize = 2;
|
||||
const lastModified = "111111";
|
||||
let { records, responses, requests, coll } = get_test_collection_info({
|
||||
totalRecords,
|
||||
batchSize,
|
||||
lastModified,
|
||||
throwAfter: 3
|
||||
});
|
||||
|
||||
throws(() => coll.getBatched(batchSize), "Some Network Error");
|
||||
|
||||
equal(requests.length, 3);
|
||||
equal(records.length, 0);
|
||||
run_next_test();
|
||||
});
|
||||
|
|
@ -6,7 +6,7 @@ Cu.import("resource://services-sync/service.js");
|
|||
Cu.import("resource://services-sync/util.js");
|
||||
Cu.import("resource://testing-common/services/sync/utils.js");
|
||||
|
||||
add_identity_test(this, function* test_missing_crypto_collection() {
|
||||
add_identity_test(this, function test_missing_crypto_collection() {
|
||||
let johnHelper = track_collections_helper();
|
||||
let johnU = johnHelper.with_updated_collection;
|
||||
let johnColls = johnHelper.collections;
|
||||
|
|
@ -33,10 +33,7 @@ add_identity_test(this, function* test_missing_crypto_collection() {
|
|||
};
|
||||
let collections = ["clients", "bookmarks", "forms", "history",
|
||||
"passwords", "prefs", "tabs"];
|
||||
// Disable addon sync because AddonManager won't be initialized here.
|
||||
Service.engineManager.unregister("addons");
|
||||
|
||||
for (let coll of collections) {
|
||||
for each (let coll in collections) {
|
||||
handlers["/1.1/johndoe/storage/" + coll] =
|
||||
johnU(coll, new ServerCollection({}, true).handler());
|
||||
}
|
||||
|
|
@ -53,7 +50,7 @@ add_identity_test(this, function* test_missing_crypto_collection() {
|
|||
};
|
||||
|
||||
_("Startup, no meta/global: freshStart called once.");
|
||||
yield sync_and_validate_telem();
|
||||
Service.sync();
|
||||
do_check_eq(fresh, 1);
|
||||
fresh = 0;
|
||||
|
||||
|
|
@ -63,12 +60,12 @@ add_identity_test(this, function* test_missing_crypto_collection() {
|
|||
|
||||
_("Simulate a bad info/collections.");
|
||||
delete johnColls.crypto;
|
||||
yield sync_and_validate_telem();
|
||||
Service.sync();
|
||||
do_check_eq(fresh, 1);
|
||||
fresh = 0;
|
||||
|
||||
_("Regular sync: no need to freshStart.");
|
||||
yield sync_and_validate_telem();
|
||||
Service.sync();
|
||||
do_check_eq(fresh, 0);
|
||||
|
||||
} finally {
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ Cu.import("resource://services-sync/util.js");
|
|||
Cu.import("resource://testing-common/services/sync/utils.js");
|
||||
Cu.import("resource://gre/modules/Promise.jsm");
|
||||
|
||||
add_task(function* test_locally_changed_keys() {
|
||||
add_task(function test_locally_changed_keys() {
|
||||
let passphrase = "abcdeabcdeabcdeabcdeabcdea";
|
||||
|
||||
let hmacErrorCount = 0;
|
||||
|
|
@ -51,7 +51,7 @@ add_task(function* test_locally_changed_keys() {
|
|||
}]}]};
|
||||
delete Svc.Session;
|
||||
Svc.Session = {
|
||||
getBrowserState: () => JSON.stringify(myTabs)
|
||||
getBrowserState: function () JSON.stringify(myTabs)
|
||||
};
|
||||
|
||||
setBasicCredentials("johndoe", "password", passphrase);
|
||||
|
|
@ -59,7 +59,6 @@ add_task(function* test_locally_changed_keys() {
|
|||
Service.clusterURL = server.baseURI;
|
||||
|
||||
Service.engineManager.register(HistoryEngine);
|
||||
Service.engineManager.unregister("addons");
|
||||
|
||||
function corrupt_local_keys() {
|
||||
Service.collectionKeys._default.keyPair = [Svc.Crypto.generateRandomKey(),
|
||||
|
|
@ -87,7 +86,7 @@ add_task(function* test_locally_changed_keys() {
|
|||
do_check_true(Service.isLoggedIn);
|
||||
|
||||
// Sync should upload records.
|
||||
yield sync_and_validate_telem();
|
||||
Service.sync();
|
||||
|
||||
// Tabs exist.
|
||||
_("Tabs modified: " + johndoe.modified("tabs"));
|
||||
|
|
@ -140,9 +139,7 @@ add_task(function* test_locally_changed_keys() {
|
|||
|
||||
_("HMAC error count: " + hmacErrorCount);
|
||||
// Now syncing should succeed, after one HMAC error.
|
||||
let ping = yield wait_for_ping(() => Service.sync(), true);
|
||||
equal(ping.engines.find(e => e.name == "history").incoming.applied, 5);
|
||||
|
||||
Service.sync();
|
||||
do_check_eq(hmacErrorCount, 1);
|
||||
_("Keys now: " + Service.collectionKeys.keyForCollection("history").keyPair);
|
||||
|
||||
|
|
@ -186,9 +183,7 @@ add_task(function* test_locally_changed_keys() {
|
|||
Service.lastHMACEvent = 0;
|
||||
|
||||
_("Syncing...");
|
||||
ping = yield sync_and_validate_telem(true);
|
||||
|
||||
do_check_eq(ping.engines.find(e => e.name == "history").incoming.failed, 5);
|
||||
Service.sync();
|
||||
_("Keys now: " + Service.collectionKeys.keyForCollection("history").keyPair);
|
||||
_("Server keys have been updated, and we skipped over 5 more HMAC errors without adjusting history.");
|
||||
do_check_true(johndoe.modified("crypto") > old_key_time);
|
||||
|
|
@ -209,7 +204,6 @@ add_task(function* test_locally_changed_keys() {
|
|||
function run_test() {
|
||||
let logger = Log.repository.rootLogger;
|
||||
Log.repository.rootLogger.addAppender(new Log.DumpAppender());
|
||||
validate_all_future_pings();
|
||||
|
||||
ensureLegacyIdentityManager();
|
||||
|
||||
|
|
|
|||
|
|
@ -25,8 +25,8 @@ SteamTracker.prototype = {
|
|||
__proto__: Tracker.prototype
|
||||
};
|
||||
|
||||
function SteamEngine(name, service) {
|
||||
Engine.call(this, name, service);
|
||||
function SteamEngine(service) {
|
||||
Engine.call(this, "Steam", service);
|
||||
this.wasReset = false;
|
||||
this.wasSynced = false;
|
||||
}
|
||||
|
|
@ -44,7 +44,7 @@ SteamEngine.prototype = {
|
|||
}
|
||||
};
|
||||
|
||||
var engineObserver = {
|
||||
let engineObserver = {
|
||||
topics: [],
|
||||
|
||||
observe: function(subject, topic, data) {
|
||||
|
|
@ -69,7 +69,7 @@ function run_test() {
|
|||
|
||||
add_test(function test_members() {
|
||||
_("Engine object members");
|
||||
let engine = new SteamEngine("Steam", Service);
|
||||
let engine = new SteamEngine(Service);
|
||||
do_check_eq(engine.Name, "Steam");
|
||||
do_check_eq(engine.prefName, "steam");
|
||||
do_check_true(engine._store instanceof SteamStore);
|
||||
|
|
@ -79,7 +79,7 @@ add_test(function test_members() {
|
|||
|
||||
add_test(function test_score() {
|
||||
_("Engine.score corresponds to tracker.score and is readonly");
|
||||
let engine = new SteamEngine("Steam", Service);
|
||||
let engine = new SteamEngine(Service);
|
||||
do_check_eq(engine.score, 0);
|
||||
engine._tracker.score += 5;
|
||||
do_check_eq(engine.score, 5);
|
||||
|
|
@ -97,7 +97,7 @@ add_test(function test_score() {
|
|||
|
||||
add_test(function test_resetClient() {
|
||||
_("Engine.resetClient calls _resetClient");
|
||||
let engine = new SteamEngine("Steam", Service);
|
||||
let engine = new SteamEngine(Service);
|
||||
do_check_false(engine.wasReset);
|
||||
|
||||
engine.resetClient();
|
||||
|
|
@ -112,7 +112,7 @@ add_test(function test_resetClient() {
|
|||
|
||||
add_test(function test_invalidChangedIDs() {
|
||||
_("Test that invalid changed IDs on disk don't end up live.");
|
||||
let engine = new SteamEngine("Steam", Service);
|
||||
let engine = new SteamEngine(Service);
|
||||
let tracker = engine._tracker;
|
||||
tracker.changedIDs = 5;
|
||||
tracker.saveChangedIDs(function onSaved() {
|
||||
|
|
@ -127,7 +127,7 @@ add_test(function test_invalidChangedIDs() {
|
|||
|
||||
add_test(function test_wipeClient() {
|
||||
_("Engine.wipeClient calls resetClient, wipes store, clears changed IDs");
|
||||
let engine = new SteamEngine("Steam", Service);
|
||||
let engine = new SteamEngine(Service);
|
||||
do_check_false(engine.wasReset);
|
||||
do_check_false(engine._store.wasWiped);
|
||||
do_check_true(engine._tracker.addChangedID("a-changed-id"));
|
||||
|
|
@ -150,7 +150,7 @@ add_test(function test_wipeClient() {
|
|||
|
||||
add_test(function test_enabled() {
|
||||
_("Engine.enabled corresponds to preference");
|
||||
let engine = new SteamEngine("Steam", Service);
|
||||
let engine = new SteamEngine(Service);
|
||||
try {
|
||||
do_check_false(engine.enabled);
|
||||
Svc.Prefs.set("engine.steam", true);
|
||||
|
|
@ -165,18 +165,16 @@ add_test(function test_enabled() {
|
|||
});
|
||||
|
||||
add_test(function test_sync() {
|
||||
let engine = new SteamEngine("Steam", Service);
|
||||
let engine = new SteamEngine(Service);
|
||||
try {
|
||||
_("Engine.sync doesn't call _sync if it's not enabled");
|
||||
do_check_false(engine.enabled);
|
||||
do_check_false(engine.wasSynced);
|
||||
engine.sync();
|
||||
|
||||
do_check_false(engine.wasSynced);
|
||||
|
||||
_("Engine.sync calls _sync if it's enabled");
|
||||
engine.enabled = true;
|
||||
|
||||
engine.sync();
|
||||
do_check_true(engine.wasSynced);
|
||||
do_check_eq(engineObserver.topics[0], "weave:engine:sync:start");
|
||||
|
|
@ -191,7 +189,7 @@ add_test(function test_sync() {
|
|||
|
||||
add_test(function test_disabled_no_track() {
|
||||
_("When an engine is disabled, its tracker is not tracking.");
|
||||
let engine = new SteamEngine("Steam", Service);
|
||||
let engine = new SteamEngine(Service);
|
||||
let tracker = engine._tracker;
|
||||
do_check_eq(engine, tracker.engine);
|
||||
|
||||
|
|
|
|||
1893
services/sync/tests/unit/test_errorhandler.js
Normal file
1893
services/sync/tests/unit/test_errorhandler.js
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -1,913 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
Cu.import("resource://services-sync/engines/clients.js");
|
||||
Cu.import("resource://services-sync/constants.js");
|
||||
Cu.import("resource://services-sync/engines.js");
|
||||
Cu.import("resource://services-sync/keys.js");
|
||||
Cu.import("resource://services-sync/policies.js");
|
||||
Cu.import("resource://services-sync/service.js");
|
||||
Cu.import("resource://services-sync/status.js");
|
||||
Cu.import("resource://services-sync/util.js");
|
||||
Cu.import("resource://testing-common/services/sync/utils.js");
|
||||
Cu.import("resource://gre/modules/FileUtils.jsm");
|
||||
|
||||
var fakeServer = new SyncServer();
|
||||
fakeServer.start();
|
||||
|
||||
do_register_cleanup(function() {
|
||||
return new Promise(resolve => {
|
||||
fakeServer.stop(resolve);
|
||||
});
|
||||
});
|
||||
|
||||
var fakeServerUrl = "http://localhost:" + fakeServer.port;
|
||||
|
||||
const logsdir = FileUtils.getDir("ProfD", ["weave", "logs"], true);
|
||||
|
||||
const PROLONGED_ERROR_DURATION =
|
||||
(Svc.Prefs.get('errorhandler.networkFailureReportTimeout') * 2) * 1000;
|
||||
|
||||
const NON_PROLONGED_ERROR_DURATION =
|
||||
(Svc.Prefs.get('errorhandler.networkFailureReportTimeout') / 2) * 1000;
|
||||
|
||||
Service.engineManager.clear();
|
||||
|
||||
function setLastSync(lastSyncValue) {
|
||||
Svc.Prefs.set("lastSync", (new Date(Date.now() - lastSyncValue)).toString());
|
||||
}
|
||||
|
||||
var engineManager = Service.engineManager;
|
||||
engineManager.register(EHTestsCommon.CatapultEngine);
|
||||
|
||||
// This relies on Service/ErrorHandler being a singleton. Fixing this will take
|
||||
// a lot of work.
|
||||
var errorHandler = Service.errorHandler;
|
||||
|
||||
function run_test() {
|
||||
initTestLogging("Trace");
|
||||
|
||||
Log.repository.getLogger("Sync.Service").level = Log.Level.Trace;
|
||||
Log.repository.getLogger("Sync.SyncScheduler").level = Log.Level.Trace;
|
||||
Log.repository.getLogger("Sync.ErrorHandler").level = Log.Level.Trace;
|
||||
|
||||
ensureLegacyIdentityManager();
|
||||
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
|
||||
function clean() {
|
||||
Service.startOver();
|
||||
Status.resetSync();
|
||||
Status.resetBackoff();
|
||||
errorHandler.didReportProlongedError = false;
|
||||
}
|
||||
|
||||
add_identity_test(this, function* test_401_logout() {
|
||||
let server = EHTestsCommon.sync_httpd_setup();
|
||||
yield EHTestsCommon.setUp(server);
|
||||
|
||||
// By calling sync, we ensure we're logged in.
|
||||
yield sync_and_validate_telem();
|
||||
do_check_eq(Status.sync, SYNC_SUCCEEDED);
|
||||
do_check_true(Service.isLoggedIn);
|
||||
|
||||
let deferred = Promise.defer();
|
||||
Svc.Obs.add("weave:service:sync:error", onSyncError);
|
||||
function onSyncError() {
|
||||
_("Got weave:service:sync:error in first sync.");
|
||||
Svc.Obs.remove("weave:service:sync:error", onSyncError);
|
||||
|
||||
// Wait for the automatic next sync.
|
||||
function onLoginError() {
|
||||
_("Got weave:service:login:error in second sync.");
|
||||
Svc.Obs.remove("weave:service:login:error", onLoginError);
|
||||
|
||||
let expected = isConfiguredWithLegacyIdentity() ?
|
||||
LOGIN_FAILED_LOGIN_REJECTED : LOGIN_FAILED_NETWORK_ERROR;
|
||||
|
||||
do_check_eq(Status.login, expected);
|
||||
do_check_false(Service.isLoggedIn);
|
||||
|
||||
// Clean up.
|
||||
Utils.nextTick(function () {
|
||||
Service.startOver();
|
||||
server.stop(deferred.resolve);
|
||||
});
|
||||
}
|
||||
Svc.Obs.add("weave:service:login:error", onLoginError);
|
||||
}
|
||||
|
||||
// Make sync fail due to login rejected.
|
||||
yield configureIdentity({username: "janedoe"});
|
||||
Service._updateCachedURLs();
|
||||
|
||||
_("Starting first sync.");
|
||||
let ping = yield sync_and_validate_telem(true);
|
||||
deepEqual(ping.failureReason, { name: "httperror", code: 401 });
|
||||
_("First sync done.");
|
||||
yield deferred.promise;
|
||||
});
|
||||
|
||||
add_identity_test(this, function* test_credentials_changed_logout() {
|
||||
let server = EHTestsCommon.sync_httpd_setup();
|
||||
yield EHTestsCommon.setUp(server);
|
||||
|
||||
// By calling sync, we ensure we're logged in.
|
||||
yield sync_and_validate_telem();
|
||||
do_check_eq(Status.sync, SYNC_SUCCEEDED);
|
||||
do_check_true(Service.isLoggedIn);
|
||||
|
||||
EHTestsCommon.generateCredentialsChangedFailure();
|
||||
|
||||
let ping = yield sync_and_validate_telem(true);
|
||||
equal(ping.status.sync, CREDENTIALS_CHANGED);
|
||||
deepEqual(ping.failureReason, {
|
||||
name: "unexpectederror",
|
||||
error: "Error: Aborting sync, remote setup failed"
|
||||
});
|
||||
|
||||
do_check_eq(Status.sync, CREDENTIALS_CHANGED);
|
||||
do_check_false(Service.isLoggedIn);
|
||||
|
||||
// Clean up.
|
||||
Service.startOver();
|
||||
let deferred = Promise.defer();
|
||||
server.stop(deferred.resolve);
|
||||
yield deferred.promise;
|
||||
});
|
||||
|
||||
add_identity_test(this, function test_no_lastSync_pref() {
|
||||
// Test reported error.
|
||||
Status.resetSync();
|
||||
errorHandler.dontIgnoreErrors = true;
|
||||
Status.sync = CREDENTIALS_CHANGED;
|
||||
do_check_true(errorHandler.shouldReportError());
|
||||
|
||||
// Test unreported error.
|
||||
Status.resetSync();
|
||||
errorHandler.dontIgnoreErrors = true;
|
||||
Status.login = LOGIN_FAILED_NETWORK_ERROR;
|
||||
do_check_true(errorHandler.shouldReportError());
|
||||
|
||||
});
|
||||
|
||||
add_identity_test(this, function test_shouldReportError() {
|
||||
Status.login = MASTER_PASSWORD_LOCKED;
|
||||
do_check_false(errorHandler.shouldReportError());
|
||||
|
||||
// Give ourselves a clusterURL so that the temporary 401 no-error situation
|
||||
// doesn't come into play.
|
||||
Service.serverURL = fakeServerUrl;
|
||||
Service.clusterURL = fakeServerUrl;
|
||||
|
||||
// Test dontIgnoreErrors, non-network, non-prolonged, login error reported
|
||||
Status.resetSync();
|
||||
setLastSync(NON_PROLONGED_ERROR_DURATION);
|
||||
errorHandler.dontIgnoreErrors = true;
|
||||
Status.login = LOGIN_FAILED_NO_PASSWORD;
|
||||
do_check_true(errorHandler.shouldReportError());
|
||||
|
||||
// Test dontIgnoreErrors, non-network, non-prolonged, sync error reported
|
||||
Status.resetSync();
|
||||
setLastSync(NON_PROLONGED_ERROR_DURATION);
|
||||
errorHandler.dontIgnoreErrors = true;
|
||||
Status.sync = CREDENTIALS_CHANGED;
|
||||
do_check_true(errorHandler.shouldReportError());
|
||||
|
||||
// Test dontIgnoreErrors, non-network, prolonged, login error reported
|
||||
Status.resetSync();
|
||||
setLastSync(PROLONGED_ERROR_DURATION);
|
||||
errorHandler.dontIgnoreErrors = true;
|
||||
Status.login = LOGIN_FAILED_NO_PASSWORD;
|
||||
do_check_true(errorHandler.shouldReportError());
|
||||
|
||||
// Test dontIgnoreErrors, non-network, prolonged, sync error reported
|
||||
Status.resetSync();
|
||||
setLastSync(PROLONGED_ERROR_DURATION);
|
||||
errorHandler.dontIgnoreErrors = true;
|
||||
Status.sync = CREDENTIALS_CHANGED;
|
||||
do_check_true(errorHandler.shouldReportError());
|
||||
|
||||
// Test dontIgnoreErrors, network, non-prolonged, login error reported
|
||||
Status.resetSync();
|
||||
setLastSync(NON_PROLONGED_ERROR_DURATION);
|
||||
errorHandler.dontIgnoreErrors = true;
|
||||
Status.login = LOGIN_FAILED_NETWORK_ERROR;
|
||||
do_check_true(errorHandler.shouldReportError());
|
||||
|
||||
// Test dontIgnoreErrors, network, non-prolonged, sync error reported
|
||||
Status.resetSync();
|
||||
setLastSync(NON_PROLONGED_ERROR_DURATION);
|
||||
errorHandler.dontIgnoreErrors = true;
|
||||
Status.sync = LOGIN_FAILED_NETWORK_ERROR;
|
||||
do_check_true(errorHandler.shouldReportError());
|
||||
|
||||
// Test dontIgnoreErrors, network, prolonged, login error reported
|
||||
Status.resetSync();
|
||||
setLastSync(PROLONGED_ERROR_DURATION);
|
||||
errorHandler.dontIgnoreErrors = true;
|
||||
Status.login = LOGIN_FAILED_NETWORK_ERROR;
|
||||
do_check_true(errorHandler.shouldReportError());
|
||||
|
||||
// Test dontIgnoreErrors, network, prolonged, sync error reported
|
||||
Status.resetSync();
|
||||
setLastSync(PROLONGED_ERROR_DURATION);
|
||||
errorHandler.dontIgnoreErrors = true;
|
||||
Status.sync = LOGIN_FAILED_NETWORK_ERROR;
|
||||
do_check_true(errorHandler.shouldReportError());
|
||||
|
||||
// Test non-network, prolonged, login error reported
|
||||
do_check_false(errorHandler.didReportProlongedError);
|
||||
Status.resetSync();
|
||||
setLastSync(PROLONGED_ERROR_DURATION);
|
||||
errorHandler.dontIgnoreErrors = false;
|
||||
Status.login = LOGIN_FAILED_NO_PASSWORD;
|
||||
do_check_true(errorHandler.shouldReportError());
|
||||
do_check_true(errorHandler.didReportProlongedError);
|
||||
|
||||
// Second time with prolonged error and without resetting
|
||||
// didReportProlongedError, sync error should not be reported.
|
||||
Status.resetSync();
|
||||
setLastSync(PROLONGED_ERROR_DURATION);
|
||||
errorHandler.dontIgnoreErrors = false;
|
||||
Status.login = LOGIN_FAILED_NO_PASSWORD;
|
||||
do_check_false(errorHandler.shouldReportError());
|
||||
do_check_true(errorHandler.didReportProlongedError);
|
||||
|
||||
// Test non-network, prolonged, sync error reported
|
||||
Status.resetSync();
|
||||
setLastSync(PROLONGED_ERROR_DURATION);
|
||||
errorHandler.dontIgnoreErrors = false;
|
||||
errorHandler.didReportProlongedError = false;
|
||||
Status.sync = CREDENTIALS_CHANGED;
|
||||
do_check_true(errorHandler.shouldReportError());
|
||||
do_check_true(errorHandler.didReportProlongedError);
|
||||
errorHandler.didReportProlongedError = false;
|
||||
|
||||
// Test network, prolonged, login error reported
|
||||
Status.resetSync();
|
||||
setLastSync(PROLONGED_ERROR_DURATION);
|
||||
errorHandler.dontIgnoreErrors = false;
|
||||
Status.login = LOGIN_FAILED_NETWORK_ERROR;
|
||||
do_check_true(errorHandler.shouldReportError());
|
||||
do_check_true(errorHandler.didReportProlongedError);
|
||||
errorHandler.didReportProlongedError = false;
|
||||
|
||||
// Test network, prolonged, sync error reported
|
||||
Status.resetSync();
|
||||
setLastSync(PROLONGED_ERROR_DURATION);
|
||||
errorHandler.dontIgnoreErrors = false;
|
||||
Status.sync = LOGIN_FAILED_NETWORK_ERROR;
|
||||
do_check_true(errorHandler.shouldReportError());
|
||||
do_check_true(errorHandler.didReportProlongedError);
|
||||
errorHandler.didReportProlongedError = false;
|
||||
|
||||
// Test non-network, non-prolonged, login error reported
|
||||
Status.resetSync();
|
||||
setLastSync(NON_PROLONGED_ERROR_DURATION);
|
||||
errorHandler.dontIgnoreErrors = false;
|
||||
Status.login = LOGIN_FAILED_NO_PASSWORD;
|
||||
do_check_true(errorHandler.shouldReportError());
|
||||
do_check_false(errorHandler.didReportProlongedError);
|
||||
|
||||
// Test non-network, non-prolonged, sync error reported
|
||||
Status.resetSync();
|
||||
setLastSync(NON_PROLONGED_ERROR_DURATION);
|
||||
errorHandler.dontIgnoreErrors = false;
|
||||
Status.sync = CREDENTIALS_CHANGED;
|
||||
do_check_true(errorHandler.shouldReportError());
|
||||
do_check_false(errorHandler.didReportProlongedError);
|
||||
|
||||
// Test network, non-prolonged, login error reported
|
||||
Status.resetSync();
|
||||
setLastSync(NON_PROLONGED_ERROR_DURATION);
|
||||
errorHandler.dontIgnoreErrors = false;
|
||||
Status.login = LOGIN_FAILED_NETWORK_ERROR;
|
||||
do_check_false(errorHandler.shouldReportError());
|
||||
do_check_false(errorHandler.didReportProlongedError);
|
||||
|
||||
// Test network, non-prolonged, sync error reported
|
||||
Status.resetSync();
|
||||
setLastSync(NON_PROLONGED_ERROR_DURATION);
|
||||
errorHandler.dontIgnoreErrors = false;
|
||||
Status.sync = LOGIN_FAILED_NETWORK_ERROR;
|
||||
do_check_false(errorHandler.shouldReportError());
|
||||
do_check_false(errorHandler.didReportProlongedError);
|
||||
|
||||
// Test server maintenance, sync errors are not reported
|
||||
Status.resetSync();
|
||||
setLastSync(NON_PROLONGED_ERROR_DURATION);
|
||||
errorHandler.dontIgnoreErrors = false;
|
||||
Status.sync = SERVER_MAINTENANCE;
|
||||
do_check_false(errorHandler.shouldReportError());
|
||||
do_check_false(errorHandler.didReportProlongedError);
|
||||
|
||||
// Test server maintenance, login errors are not reported
|
||||
Status.resetSync();
|
||||
setLastSync(NON_PROLONGED_ERROR_DURATION);
|
||||
errorHandler.dontIgnoreErrors = false;
|
||||
Status.login = SERVER_MAINTENANCE;
|
||||
do_check_false(errorHandler.shouldReportError());
|
||||
do_check_false(errorHandler.didReportProlongedError);
|
||||
|
||||
// Test prolonged, server maintenance, sync errors are reported
|
||||
Status.resetSync();
|
||||
setLastSync(PROLONGED_ERROR_DURATION);
|
||||
errorHandler.dontIgnoreErrors = false;
|
||||
Status.sync = SERVER_MAINTENANCE;
|
||||
do_check_true(errorHandler.shouldReportError());
|
||||
do_check_true(errorHandler.didReportProlongedError);
|
||||
errorHandler.didReportProlongedError = false;
|
||||
|
||||
// Test prolonged, server maintenance, login errors are reported
|
||||
Status.resetSync();
|
||||
setLastSync(PROLONGED_ERROR_DURATION);
|
||||
errorHandler.dontIgnoreErrors = false;
|
||||
Status.login = SERVER_MAINTENANCE;
|
||||
do_check_true(errorHandler.shouldReportError());
|
||||
do_check_true(errorHandler.didReportProlongedError);
|
||||
errorHandler.didReportProlongedError = false;
|
||||
|
||||
// Test dontIgnoreErrors, server maintenance, sync errors are reported
|
||||
Status.resetSync();
|
||||
setLastSync(NON_PROLONGED_ERROR_DURATION);
|
||||
errorHandler.dontIgnoreErrors = true;
|
||||
Status.sync = SERVER_MAINTENANCE;
|
||||
do_check_true(errorHandler.shouldReportError());
|
||||
// dontIgnoreErrors means we don't set didReportProlongedError
|
||||
do_check_false(errorHandler.didReportProlongedError);
|
||||
|
||||
// Test dontIgnoreErrors, server maintenance, login errors are reported
|
||||
Status.resetSync();
|
||||
setLastSync(NON_PROLONGED_ERROR_DURATION);
|
||||
errorHandler.dontIgnoreErrors = true;
|
||||
Status.login = SERVER_MAINTENANCE;
|
||||
do_check_true(errorHandler.shouldReportError());
|
||||
do_check_false(errorHandler.didReportProlongedError);
|
||||
|
||||
// Test dontIgnoreErrors, prolonged, server maintenance,
|
||||
// sync errors are reported
|
||||
Status.resetSync();
|
||||
setLastSync(PROLONGED_ERROR_DURATION);
|
||||
errorHandler.dontIgnoreErrors = true;
|
||||
Status.sync = SERVER_MAINTENANCE;
|
||||
do_check_true(errorHandler.shouldReportError());
|
||||
do_check_false(errorHandler.didReportProlongedError);
|
||||
|
||||
// Test dontIgnoreErrors, prolonged, server maintenance,
|
||||
// login errors are reported
|
||||
Status.resetSync();
|
||||
setLastSync(PROLONGED_ERROR_DURATION);
|
||||
errorHandler.dontIgnoreErrors = true;
|
||||
Status.login = SERVER_MAINTENANCE;
|
||||
do_check_true(errorHandler.shouldReportError());
|
||||
do_check_false(errorHandler.didReportProlongedError);
|
||||
});
|
||||
|
||||
add_identity_test(this, function* test_shouldReportError_master_password() {
|
||||
_("Test error ignored due to locked master password");
|
||||
let server = EHTestsCommon.sync_httpd_setup();
|
||||
yield EHTestsCommon.setUp(server);
|
||||
|
||||
// Monkey patch Service.verifyLogin to imitate
|
||||
// master password being locked.
|
||||
Service._verifyLogin = Service.verifyLogin;
|
||||
Service.verifyLogin = function () {
|
||||
Status.login = MASTER_PASSWORD_LOCKED;
|
||||
return false;
|
||||
};
|
||||
|
||||
setLastSync(NON_PROLONGED_ERROR_DURATION);
|
||||
Service.sync();
|
||||
do_check_false(errorHandler.shouldReportError());
|
||||
|
||||
// Clean up.
|
||||
Service.verifyLogin = Service._verifyLogin;
|
||||
clean();
|
||||
let deferred = Promise.defer();
|
||||
server.stop(deferred.resolve);
|
||||
yield deferred.promise;
|
||||
});
|
||||
|
||||
// Test that even if we don't have a cluster URL, a login failure due to
|
||||
// authentication errors is always reported.
|
||||
add_identity_test(this, function test_shouldReportLoginFailureWithNoCluster() {
|
||||
// Ensure no clusterURL - any error not specific to login should not be reported.
|
||||
Service.serverURL = "";
|
||||
Service.clusterURL = "";
|
||||
|
||||
// Test explicit "login rejected" state.
|
||||
Status.resetSync();
|
||||
// If we have a LOGIN_REJECTED state, we always report the error.
|
||||
Status.login = LOGIN_FAILED_LOGIN_REJECTED;
|
||||
do_check_true(errorHandler.shouldReportError());
|
||||
// But any other status with a missing clusterURL is treated as a mid-sync
|
||||
// 401 (ie, should be treated as a node reassignment)
|
||||
Status.login = LOGIN_SUCCEEDED;
|
||||
do_check_false(errorHandler.shouldReportError());
|
||||
});
|
||||
|
||||
// XXX - how to arrange for 'Service.identity.basicPassword = null;' in
|
||||
// an fxaccounts environment?
|
||||
add_task(function* test_login_syncAndReportErrors_non_network_error() {
|
||||
// Test non-network errors are reported
|
||||
// when calling syncAndReportErrors
|
||||
let server = EHTestsCommon.sync_httpd_setup();
|
||||
yield EHTestsCommon.setUp(server);
|
||||
Service.identity.basicPassword = null;
|
||||
|
||||
let deferred = Promise.defer();
|
||||
Svc.Obs.add("weave:ui:login:error", function onSyncError() {
|
||||
Svc.Obs.remove("weave:ui:login:error", onSyncError);
|
||||
do_check_eq(Status.login, LOGIN_FAILED_NO_PASSWORD);
|
||||
|
||||
clean();
|
||||
server.stop(deferred.resolve);
|
||||
});
|
||||
|
||||
setLastSync(NON_PROLONGED_ERROR_DURATION);
|
||||
errorHandler.syncAndReportErrors();
|
||||
yield deferred.promise;
|
||||
});
|
||||
|
||||
add_identity_test(this, function* test_sync_syncAndReportErrors_non_network_error() {
|
||||
// Test non-network errors are reported
|
||||
// when calling syncAndReportErrors
|
||||
let server = EHTestsCommon.sync_httpd_setup();
|
||||
yield EHTestsCommon.setUp(server);
|
||||
|
||||
// By calling sync, we ensure we're logged in.
|
||||
Service.sync();
|
||||
do_check_eq(Status.sync, SYNC_SUCCEEDED);
|
||||
do_check_true(Service.isLoggedIn);
|
||||
|
||||
EHTestsCommon.generateCredentialsChangedFailure();
|
||||
|
||||
let deferred = Promise.defer();
|
||||
Svc.Obs.add("weave:ui:sync:error", function onSyncError() {
|
||||
Svc.Obs.remove("weave:ui:sync:error", onSyncError);
|
||||
do_check_eq(Status.sync, CREDENTIALS_CHANGED);
|
||||
// If we clean this tick, telemetry won't get the right error
|
||||
server.stop(() => {
|
||||
clean();
|
||||
deferred.resolve();
|
||||
});
|
||||
});
|
||||
|
||||
setLastSync(NON_PROLONGED_ERROR_DURATION);
|
||||
let ping = yield wait_for_ping(() => errorHandler.syncAndReportErrors(), true);
|
||||
equal(ping.status.sync, CREDENTIALS_CHANGED);
|
||||
deepEqual(ping.failureReason, {
|
||||
name: "unexpectederror",
|
||||
error: "Error: Aborting sync, remote setup failed"
|
||||
});
|
||||
yield deferred.promise;
|
||||
});
|
||||
|
||||
// XXX - how to arrange for 'Service.identity.basicPassword = null;' in
|
||||
// an fxaccounts environment?
|
||||
add_task(function* test_login_syncAndReportErrors_prolonged_non_network_error() {
|
||||
// Test prolonged, non-network errors are
|
||||
// reported when calling syncAndReportErrors.
|
||||
let server = EHTestsCommon.sync_httpd_setup();
|
||||
yield EHTestsCommon.setUp(server);
|
||||
Service.identity.basicPassword = null;
|
||||
|
||||
let deferred = Promise.defer();
|
||||
Svc.Obs.add("weave:ui:login:error", function onSyncError() {
|
||||
Svc.Obs.remove("weave:ui:login:error", onSyncError);
|
||||
do_check_eq(Status.login, LOGIN_FAILED_NO_PASSWORD);
|
||||
|
||||
clean();
|
||||
server.stop(deferred.resolve);
|
||||
});
|
||||
|
||||
setLastSync(PROLONGED_ERROR_DURATION);
|
||||
errorHandler.syncAndReportErrors();
|
||||
yield deferred.promise;
|
||||
});
|
||||
|
||||
add_identity_test(this, function* test_sync_syncAndReportErrors_prolonged_non_network_error() {
|
||||
// Test prolonged, non-network errors are
|
||||
// reported when calling syncAndReportErrors.
|
||||
let server = EHTestsCommon.sync_httpd_setup();
|
||||
yield EHTestsCommon.setUp(server);
|
||||
|
||||
// By calling sync, we ensure we're logged in.
|
||||
Service.sync();
|
||||
do_check_eq(Status.sync, SYNC_SUCCEEDED);
|
||||
do_check_true(Service.isLoggedIn);
|
||||
|
||||
EHTestsCommon.generateCredentialsChangedFailure();
|
||||
|
||||
let deferred = Promise.defer();
|
||||
Svc.Obs.add("weave:ui:sync:error", function onSyncError() {
|
||||
Svc.Obs.remove("weave:ui:sync:error", onSyncError);
|
||||
do_check_eq(Status.sync, CREDENTIALS_CHANGED);
|
||||
// If we clean this tick, telemetry won't get the right error
|
||||
server.stop(() => {
|
||||
clean();
|
||||
deferred.resolve();
|
||||
});
|
||||
});
|
||||
|
||||
setLastSync(PROLONGED_ERROR_DURATION);
|
||||
let ping = yield wait_for_ping(() => errorHandler.syncAndReportErrors(), true);
|
||||
equal(ping.status.sync, CREDENTIALS_CHANGED);
|
||||
deepEqual(ping.failureReason, {
|
||||
name: "unexpectederror",
|
||||
error: "Error: Aborting sync, remote setup failed"
|
||||
});
|
||||
yield deferred.promise;
|
||||
});
|
||||
|
||||
add_identity_test(this, function* test_login_syncAndReportErrors_network_error() {
|
||||
// Test network errors are reported when calling syncAndReportErrors.
|
||||
yield configureIdentity({username: "broken.wipe"});
|
||||
Service.serverURL = fakeServerUrl;
|
||||
Service.clusterURL = fakeServerUrl;
|
||||
|
||||
let deferred = Promise.defer();
|
||||
Svc.Obs.add("weave:ui:login:error", function onSyncError() {
|
||||
Svc.Obs.remove("weave:ui:login:error", onSyncError);
|
||||
do_check_eq(Status.login, LOGIN_FAILED_NETWORK_ERROR);
|
||||
|
||||
clean();
|
||||
deferred.resolve();
|
||||
});
|
||||
|
||||
setLastSync(NON_PROLONGED_ERROR_DURATION);
|
||||
errorHandler.syncAndReportErrors();
|
||||
yield deferred.promise;
|
||||
});
|
||||
|
||||
|
||||
add_test(function test_sync_syncAndReportErrors_network_error() {
|
||||
// Test network errors are reported when calling syncAndReportErrors.
|
||||
Services.io.offline = true;
|
||||
|
||||
Svc.Obs.add("weave:ui:sync:error", function onSyncError() {
|
||||
Svc.Obs.remove("weave:ui:sync:error", onSyncError);
|
||||
do_check_eq(Status.sync, LOGIN_FAILED_NETWORK_ERROR);
|
||||
|
||||
Services.io.offline = false;
|
||||
clean();
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
setLastSync(NON_PROLONGED_ERROR_DURATION);
|
||||
errorHandler.syncAndReportErrors();
|
||||
});
|
||||
|
||||
add_identity_test(this, function* test_login_syncAndReportErrors_prolonged_network_error() {
|
||||
// Test prolonged, network errors are reported
|
||||
// when calling syncAndReportErrors.
|
||||
yield configureIdentity({username: "johndoe"});
|
||||
|
||||
Service.serverURL = fakeServerUrl;
|
||||
Service.clusterURL = fakeServerUrl;
|
||||
|
||||
let deferred = Promise.defer();
|
||||
Svc.Obs.add("weave:ui:login:error", function onSyncError() {
|
||||
Svc.Obs.remove("weave:ui:login:error", onSyncError);
|
||||
do_check_eq(Status.login, LOGIN_FAILED_NETWORK_ERROR);
|
||||
|
||||
clean();
|
||||
deferred.resolve();
|
||||
});
|
||||
|
||||
setLastSync(PROLONGED_ERROR_DURATION);
|
||||
errorHandler.syncAndReportErrors();
|
||||
yield deferred.promise;
|
||||
});
|
||||
|
||||
add_test(function test_sync_syncAndReportErrors_prolonged_network_error() {
|
||||
// Test prolonged, network errors are reported
|
||||
// when calling syncAndReportErrors.
|
||||
Services.io.offline = true;
|
||||
|
||||
Svc.Obs.add("weave:ui:sync:error", function onSyncError() {
|
||||
Svc.Obs.remove("weave:ui:sync:error", onSyncError);
|
||||
do_check_eq(Status.sync, LOGIN_FAILED_NETWORK_ERROR);
|
||||
|
||||
Services.io.offline = false;
|
||||
clean();
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
setLastSync(PROLONGED_ERROR_DURATION);
|
||||
errorHandler.syncAndReportErrors();
|
||||
});
|
||||
|
||||
add_task(function* test_login_prolonged_non_network_error() {
|
||||
// Test prolonged, non-network errors are reported
|
||||
let server = EHTestsCommon.sync_httpd_setup();
|
||||
yield EHTestsCommon.setUp(server);
|
||||
Service.identity.basicPassword = null;
|
||||
|
||||
let deferred = Promise.defer();
|
||||
Svc.Obs.add("weave:ui:login:error", function onSyncError() {
|
||||
Svc.Obs.remove("weave:ui:login:error", onSyncError);
|
||||
do_check_eq(Status.sync, PROLONGED_SYNC_FAILURE);
|
||||
do_check_true(errorHandler.didReportProlongedError);
|
||||
|
||||
clean();
|
||||
server.stop(deferred.resolve);
|
||||
});
|
||||
|
||||
setLastSync(PROLONGED_ERROR_DURATION);
|
||||
Service.sync();
|
||||
yield deferred.promise;
|
||||
});
|
||||
|
||||
add_task(function* test_sync_prolonged_non_network_error() {
|
||||
// Test prolonged, non-network errors are reported
|
||||
let server = EHTestsCommon.sync_httpd_setup();
|
||||
yield EHTestsCommon.setUp(server);
|
||||
|
||||
// By calling sync, we ensure we're logged in.
|
||||
Service.sync();
|
||||
do_check_eq(Status.sync, SYNC_SUCCEEDED);
|
||||
do_check_true(Service.isLoggedIn);
|
||||
|
||||
EHTestsCommon.generateCredentialsChangedFailure();
|
||||
|
||||
let deferred = Promise.defer();
|
||||
Svc.Obs.add("weave:ui:sync:error", function onSyncError() {
|
||||
Svc.Obs.remove("weave:ui:sync:error", onSyncError);
|
||||
do_check_eq(Status.sync, PROLONGED_SYNC_FAILURE);
|
||||
do_check_true(errorHandler.didReportProlongedError);
|
||||
server.stop(() => {
|
||||
clean();
|
||||
deferred.resolve();
|
||||
});
|
||||
});
|
||||
|
||||
setLastSync(PROLONGED_ERROR_DURATION);
|
||||
|
||||
let ping = yield sync_and_validate_telem(true);
|
||||
equal(ping.status.sync, PROLONGED_SYNC_FAILURE);
|
||||
deepEqual(ping.failureReason, {
|
||||
name: "unexpectederror",
|
||||
error: "Error: Aborting sync, remote setup failed"
|
||||
});
|
||||
yield deferred.promise;
|
||||
});
|
||||
|
||||
add_identity_test(this, function* test_login_prolonged_network_error() {
|
||||
// Test prolonged, network errors are reported
|
||||
yield configureIdentity({username: "johndoe"});
|
||||
Service.serverURL = fakeServerUrl;
|
||||
Service.clusterURL = fakeServerUrl;
|
||||
|
||||
let deferred = Promise.defer();
|
||||
Svc.Obs.add("weave:ui:login:error", function onSyncError() {
|
||||
Svc.Obs.remove("weave:ui:login:error", onSyncError);
|
||||
do_check_eq(Status.sync, PROLONGED_SYNC_FAILURE);
|
||||
do_check_true(errorHandler.didReportProlongedError);
|
||||
|
||||
clean();
|
||||
deferred.resolve();
|
||||
});
|
||||
|
||||
setLastSync(PROLONGED_ERROR_DURATION);
|
||||
Service.sync();
|
||||
yield deferred.promise;
|
||||
});
|
||||
|
||||
add_test(function test_sync_prolonged_network_error() {
|
||||
// Test prolonged, network errors are reported
|
||||
Services.io.offline = true;
|
||||
|
||||
Svc.Obs.add("weave:ui:sync:error", function onSyncError() {
|
||||
Svc.Obs.remove("weave:ui:sync:error", onSyncError);
|
||||
do_check_eq(Status.sync, PROLONGED_SYNC_FAILURE);
|
||||
do_check_true(errorHandler.didReportProlongedError);
|
||||
|
||||
Services.io.offline = false;
|
||||
clean();
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
setLastSync(PROLONGED_ERROR_DURATION);
|
||||
Service.sync();
|
||||
});
|
||||
|
||||
add_task(function* test_login_non_network_error() {
|
||||
// Test non-network errors are reported
|
||||
let server = EHTestsCommon.sync_httpd_setup();
|
||||
yield EHTestsCommon.setUp(server);
|
||||
Service.identity.basicPassword = null;
|
||||
|
||||
let deferred = Promise.defer();
|
||||
Svc.Obs.add("weave:ui:login:error", function onSyncError() {
|
||||
Svc.Obs.remove("weave:ui:login:error", onSyncError);
|
||||
do_check_eq(Status.login, LOGIN_FAILED_NO_PASSWORD);
|
||||
do_check_false(errorHandler.didReportProlongedError);
|
||||
|
||||
clean();
|
||||
server.stop(deferred.resolve);
|
||||
});
|
||||
|
||||
setLastSync(NON_PROLONGED_ERROR_DURATION);
|
||||
Service.sync();
|
||||
yield deferred.promise;
|
||||
});
|
||||
|
||||
add_task(function* test_sync_non_network_error() {
|
||||
// Test non-network errors are reported
|
||||
let server = EHTestsCommon.sync_httpd_setup();
|
||||
yield EHTestsCommon.setUp(server);
|
||||
|
||||
// By calling sync, we ensure we're logged in.
|
||||
Service.sync();
|
||||
do_check_eq(Status.sync, SYNC_SUCCEEDED);
|
||||
do_check_true(Service.isLoggedIn);
|
||||
|
||||
EHTestsCommon.generateCredentialsChangedFailure();
|
||||
|
||||
let deferred = Promise.defer();
|
||||
Svc.Obs.add("weave:ui:sync:error", function onSyncError() {
|
||||
Svc.Obs.remove("weave:ui:sync:error", onSyncError);
|
||||
do_check_eq(Status.sync, CREDENTIALS_CHANGED);
|
||||
do_check_false(errorHandler.didReportProlongedError);
|
||||
|
||||
clean();
|
||||
server.stop(deferred.resolve);
|
||||
});
|
||||
|
||||
setLastSync(NON_PROLONGED_ERROR_DURATION);
|
||||
Service.sync();
|
||||
yield deferred.promise;
|
||||
});
|
||||
|
||||
add_identity_test(this, function* test_login_network_error() {
|
||||
yield configureIdentity({username: "johndoe"});
|
||||
Service.serverURL = fakeServerUrl;
|
||||
Service.clusterURL = fakeServerUrl;
|
||||
|
||||
let deferred = Promise.defer();
|
||||
// Test network errors are not reported.
|
||||
Svc.Obs.add("weave:ui:clear-error", function onClearError() {
|
||||
Svc.Obs.remove("weave:ui:clear-error", onClearError);
|
||||
|
||||
do_check_eq(Status.login, LOGIN_FAILED_NETWORK_ERROR);
|
||||
do_check_false(errorHandler.didReportProlongedError);
|
||||
|
||||
Services.io.offline = false;
|
||||
clean();
|
||||
deferred.resolve()
|
||||
});
|
||||
|
||||
setLastSync(NON_PROLONGED_ERROR_DURATION);
|
||||
Service.sync();
|
||||
yield deferred.promise;
|
||||
});
|
||||
|
||||
add_test(function test_sync_network_error() {
|
||||
// Test network errors are not reported.
|
||||
Services.io.offline = true;
|
||||
|
||||
Svc.Obs.add("weave:ui:sync:finish", function onUIUpdate() {
|
||||
Svc.Obs.remove("weave:ui:sync:finish", onUIUpdate);
|
||||
do_check_eq(Status.sync, LOGIN_FAILED_NETWORK_ERROR);
|
||||
do_check_false(errorHandler.didReportProlongedError);
|
||||
|
||||
Services.io.offline = false;
|
||||
clean();
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
setLastSync(NON_PROLONGED_ERROR_DURATION);
|
||||
Service.sync();
|
||||
});
|
||||
|
||||
add_identity_test(this, function* test_sync_server_maintenance_error() {
|
||||
// Test server maintenance errors are not reported.
|
||||
let server = EHTestsCommon.sync_httpd_setup();
|
||||
yield EHTestsCommon.setUp(server);
|
||||
|
||||
const BACKOFF = 42;
|
||||
let engine = engineManager.get("catapult");
|
||||
engine.enabled = true;
|
||||
engine.exception = {status: 503,
|
||||
headers: {"retry-after": BACKOFF}};
|
||||
|
||||
function onSyncError() {
|
||||
do_throw("Shouldn't get here!");
|
||||
}
|
||||
Svc.Obs.add("weave:ui:sync:error", onSyncError);
|
||||
|
||||
do_check_eq(Status.service, STATUS_OK);
|
||||
|
||||
let deferred = Promise.defer();
|
||||
Svc.Obs.add("weave:ui:sync:finish", function onSyncFinish() {
|
||||
Svc.Obs.remove("weave:ui:sync:finish", onSyncFinish);
|
||||
|
||||
do_check_eq(Status.service, SYNC_FAILED_PARTIAL);
|
||||
do_check_eq(Status.sync, SERVER_MAINTENANCE);
|
||||
do_check_false(errorHandler.didReportProlongedError);
|
||||
|
||||
Svc.Obs.remove("weave:ui:sync:error", onSyncError);
|
||||
server.stop(() => {
|
||||
clean();
|
||||
deferred.resolve();
|
||||
})
|
||||
});
|
||||
|
||||
setLastSync(NON_PROLONGED_ERROR_DURATION);
|
||||
let ping = yield sync_and_validate_telem(true);
|
||||
equal(ping.status.sync, SERVER_MAINTENANCE);
|
||||
deepEqual(ping.engines.find(e => e.failureReason).failureReason, { name: "httperror", code: 503 })
|
||||
|
||||
yield deferred.promise;
|
||||
});
|
||||
|
||||
add_identity_test(this, function* test_info_collections_login_server_maintenance_error() {
|
||||
// Test info/collections server maintenance errors are not reported.
|
||||
let server = EHTestsCommon.sync_httpd_setup();
|
||||
yield EHTestsCommon.setUp(server);
|
||||
|
||||
Service.username = "broken.info";
|
||||
yield configureIdentity({username: "broken.info"});
|
||||
Service.serverURL = server.baseURI + "/maintenance/";
|
||||
Service.clusterURL = server.baseURI + "/maintenance/";
|
||||
|
||||
let backoffInterval;
|
||||
Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
|
||||
Svc.Obs.remove("weave:service:backoff:interval", observe);
|
||||
backoffInterval = subject;
|
||||
});
|
||||
|
||||
function onUIUpdate() {
|
||||
do_throw("Shouldn't experience UI update!");
|
||||
}
|
||||
Svc.Obs.add("weave:ui:login:error", onUIUpdate);
|
||||
|
||||
do_check_false(Status.enforceBackoff);
|
||||
do_check_eq(Status.service, STATUS_OK);
|
||||
|
||||
let deferred = Promise.defer();
|
||||
Svc.Obs.add("weave:ui:clear-error", function onLoginFinish() {
|
||||
Svc.Obs.remove("weave:ui:clear-error", onLoginFinish);
|
||||
|
||||
do_check_true(Status.enforceBackoff);
|
||||
do_check_eq(backoffInterval, 42);
|
||||
do_check_eq(Status.service, LOGIN_FAILED);
|
||||
do_check_eq(Status.login, SERVER_MAINTENANCE);
|
||||
do_check_false(errorHandler.didReportProlongedError);
|
||||
|
||||
Svc.Obs.remove("weave:ui:login:error", onUIUpdate);
|
||||
clean();
|
||||
server.stop(deferred.resolve);
|
||||
});
|
||||
|
||||
setLastSync(NON_PROLONGED_ERROR_DURATION);
|
||||
Service.sync();
|
||||
yield deferred.promise;
|
||||
});
|
||||
|
||||
add_identity_test(this, function* test_meta_global_login_server_maintenance_error() {
|
||||
// Test meta/global server maintenance errors are not reported.
|
||||
let server = EHTestsCommon.sync_httpd_setup();
|
||||
yield EHTestsCommon.setUp(server);
|
||||
|
||||
yield configureIdentity({username: "broken.meta"});
|
||||
Service.serverURL = server.baseURI + "/maintenance/";
|
||||
Service.clusterURL = server.baseURI + "/maintenance/";
|
||||
|
||||
let backoffInterval;
|
||||
Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
|
||||
Svc.Obs.remove("weave:service:backoff:interval", observe);
|
||||
backoffInterval = subject;
|
||||
});
|
||||
|
||||
function onUIUpdate() {
|
||||
do_throw("Shouldn't get here!");
|
||||
}
|
||||
Svc.Obs.add("weave:ui:login:error", onUIUpdate);
|
||||
|
||||
do_check_false(Status.enforceBackoff);
|
||||
do_check_eq(Status.service, STATUS_OK);
|
||||
|
||||
let deferred = Promise.defer();
|
||||
Svc.Obs.add("weave:ui:clear-error", function onLoginFinish() {
|
||||
Svc.Obs.remove("weave:ui:clear-error", onLoginFinish);
|
||||
|
||||
do_check_true(Status.enforceBackoff);
|
||||
do_check_eq(backoffInterval, 42);
|
||||
do_check_eq(Status.service, LOGIN_FAILED);
|
||||
do_check_eq(Status.login, SERVER_MAINTENANCE);
|
||||
do_check_false(errorHandler.didReportProlongedError);
|
||||
|
||||
Svc.Obs.remove("weave:ui:login:error", onUIUpdate);
|
||||
clean();
|
||||
server.stop(deferred.resolve);
|
||||
});
|
||||
|
||||
setLastSync(NON_PROLONGED_ERROR_DURATION);
|
||||
Service.sync();
|
||||
yield deferred.promise;
|
||||
});
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -43,7 +43,7 @@ function sync_httpd_setup(infoHandler) {
|
|||
return httpd_setup(handlers);
|
||||
}
|
||||
|
||||
function* setUp(server) {
|
||||
function setUp(server) {
|
||||
yield configureIdentity({username: "johndoe"});
|
||||
Service.serverURL = server.baseURI + "/";
|
||||
Service.clusterURL = server.baseURI + "/";
|
||||
|
|
@ -66,7 +66,7 @@ function do_check_hard_eol(eh, start) {
|
|||
do_check_true(Status.eol);
|
||||
}
|
||||
|
||||
add_identity_test(this, function* test_200_hard() {
|
||||
add_identity_test(this, function test_200_hard() {
|
||||
let eh = Service.errorHandler;
|
||||
let start = Date.now();
|
||||
let server = sync_httpd_setup(handler200("hard-eol"));
|
||||
|
|
@ -88,7 +88,7 @@ add_identity_test(this, function* test_200_hard() {
|
|||
yield deferred.promise;
|
||||
});
|
||||
|
||||
add_identity_test(this, function* test_513_hard() {
|
||||
add_identity_test(this, function test_513_hard() {
|
||||
let eh = Service.errorHandler;
|
||||
let start = Date.now();
|
||||
let server = sync_httpd_setup(handler513);
|
||||
|
|
@ -114,7 +114,7 @@ add_identity_test(this, function* test_513_hard() {
|
|||
yield deferred.promise;
|
||||
});
|
||||
|
||||
add_identity_test(this, function* test_200_soft() {
|
||||
add_identity_test(this, function test_200_soft() {
|
||||
let eh = Service.errorHandler;
|
||||
let start = Date.now();
|
||||
let server = sync_httpd_setup(handler200("soft-eol"));
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ const DELAY_BUFFER = 500; // Buffer for timers on different OS platforms.
|
|||
const PROLONGED_ERROR_DURATION =
|
||||
(Svc.Prefs.get('errorhandler.networkFailureReportTimeout') * 2) * 1000;
|
||||
|
||||
var errorHandler = Service.errorHandler;
|
||||
let errorHandler = Service.errorHandler;
|
||||
|
||||
function setLastSync(lastSyncValue) {
|
||||
Svc.Prefs.set("lastSync", (new Date(Date.now() - lastSyncValue)).toString());
|
||||
|
|
@ -35,8 +35,6 @@ function run_test() {
|
|||
Log.repository.getLogger("Sync.SyncScheduler").level = Log.Level.Trace;
|
||||
Log.repository.getLogger("Sync.ErrorHandler").level = Log.Level.Trace;
|
||||
|
||||
validate_all_future_pings();
|
||||
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
|
|
@ -47,22 +45,20 @@ add_test(function test_noOutput() {
|
|||
// Clear log output from startup.
|
||||
Svc.Prefs.set("log.appender.file.logOnSuccess", false);
|
||||
Svc.Obs.notify("weave:service:sync:finish");
|
||||
Svc.Obs.add("weave:service:reset-file-log", function onResetFileLogOuter() {
|
||||
Svc.Obs.remove("weave:service:reset-file-log", onResetFileLogOuter);
|
||||
// Clear again without having issued any output.
|
||||
Svc.Prefs.set("log.appender.file.logOnSuccess", true);
|
||||
|
||||
Svc.Obs.add("weave:service:reset-file-log", function onResetFileLogInner() {
|
||||
Svc.Obs.remove("weave:service:reset-file-log", onResetFileLogInner);
|
||||
// Clear again without having issued any output.
|
||||
Svc.Prefs.set("log.appender.file.logOnSuccess", true);
|
||||
|
||||
errorHandler._logManager._fileAppender.level = Log.Level.Trace;
|
||||
Svc.Prefs.resetBranch("");
|
||||
run_next_test();
|
||||
});
|
||||
Svc.Obs.add("weave:service:reset-file-log", function onResetFileLog() {
|
||||
Svc.Obs.remove("weave:service:reset-file-log", onResetFileLog);
|
||||
|
||||
// Fake a successful sync.
|
||||
Svc.Obs.notify("weave:service:sync:finish");
|
||||
errorHandler._logManager._fileAppender.level = Log.Level.Trace;
|
||||
Svc.Prefs.resetBranch("");
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
// Fake a successful sync.
|
||||
Svc.Obs.notify("weave:service:sync:finish");
|
||||
});
|
||||
|
||||
add_test(function test_logOnSuccess_false() {
|
||||
|
|
@ -85,14 +81,16 @@ add_test(function test_logOnSuccess_false() {
|
|||
});
|
||||
|
||||
function readFile(file, callback) {
|
||||
NetUtil.asyncFetch({
|
||||
uri: NetUtil.newURI(file),
|
||||
loadUsingSystemPrincipal: true
|
||||
}, function (inputStream, statusCode, request) {
|
||||
NetUtil.asyncFetch2(file, function (inputStream, statusCode, request) {
|
||||
let data = NetUtil.readInputStreamToString(inputStream,
|
||||
inputStream.available());
|
||||
callback(statusCode, data);
|
||||
});
|
||||
},
|
||||
null, // aLoadingNode
|
||||
Services.scriptSecurityManager.getSystemPrincipal(),
|
||||
null, // aTriggeringPrincipal
|
||||
Ci.nsILoadInfo.SEC_NORMAL,
|
||||
Ci.nsIContentPolicy.TYPE_OTHER);
|
||||
}
|
||||
|
||||
add_test(function test_logOnSuccess_true() {
|
||||
|
|
@ -269,51 +267,6 @@ add_test(function test_login_error_logOnError_true() {
|
|||
Svc.Obs.notify("weave:service:login:error");
|
||||
});
|
||||
|
||||
|
||||
add_test(function test_errorLog_dumpAddons() {
|
||||
Svc.Prefs.set("log.appender.file.logOnError", true);
|
||||
|
||||
let log = Log.repository.getLogger("Sync.Test.FileLog");
|
||||
|
||||
// We need to wait until the log cleanup started by this test is complete
|
||||
// or the next test will fail as it is ongoing.
|
||||
Svc.Obs.add("services-tests:common:log-manager:cleanup-logs", function onCleanupLogs() {
|
||||
Svc.Obs.remove("services-tests:common:log-manager:cleanup-logs", onCleanupLogs);
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
Svc.Obs.add("weave:service:reset-file-log", function onResetFileLog() {
|
||||
Svc.Obs.remove("weave:service:reset-file-log", onResetFileLog);
|
||||
|
||||
let entries = logsdir.directoryEntries;
|
||||
do_check_true(entries.hasMoreElements());
|
||||
let logfile = entries.getNext().QueryInterface(Ci.nsILocalFile);
|
||||
do_check_eq(logfile.leafName.slice(-4), ".txt");
|
||||
do_check_true(logfile.leafName.startsWith("error-sync-"), logfile.leafName);
|
||||
do_check_false(entries.hasMoreElements());
|
||||
|
||||
// Ensure we logged some addon list (which is probably empty)
|
||||
readFile(logfile, function (error, data) {
|
||||
do_check_true(Components.isSuccessCode(error));
|
||||
do_check_neq(data.indexOf("Addons installed"), -1);
|
||||
|
||||
// Clean up.
|
||||
try {
|
||||
logfile.remove(false);
|
||||
} catch(ex) {
|
||||
dump("Couldn't delete file: " + ex + "\n");
|
||||
// Stupid Windows box.
|
||||
}
|
||||
|
||||
Svc.Prefs.resetBranch("");
|
||||
});
|
||||
});
|
||||
|
||||
// Fake an unsuccessful sync due to prolonged failure.
|
||||
setLastSync(PROLONGED_ERROR_DURATION);
|
||||
Svc.Obs.notify("weave:service:sync:error");
|
||||
});
|
||||
|
||||
// Check that error log files are deleted above an age threshold.
|
||||
add_test(function test_logErrorCleanup_age() {
|
||||
_("Beginning test_logErrorCleanup_age.");
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ Cu.import("resource://testing-common/services/sync/utils.js");
|
|||
|
||||
initTestLogging("Trace");
|
||||
|
||||
var engineManager = Service.engineManager;
|
||||
let engineManager = Service.engineManager;
|
||||
engineManager.clear();
|
||||
|
||||
function promiseStopServer(server) {
|
||||
|
|
@ -59,7 +59,7 @@ function sync_httpd_setup() {
|
|||
return httpd_setup(handlers);
|
||||
}
|
||||
|
||||
function* setUp(server) {
|
||||
function setUp(server) {
|
||||
yield configureIdentity({username: "johndoe"});
|
||||
Service.serverURL = server.baseURI + "/";
|
||||
Service.clusterURL = server.baseURI + "/";
|
||||
|
|
@ -75,7 +75,7 @@ function generateAndUploadKeys(server) {
|
|||
}
|
||||
|
||||
|
||||
add_identity_test(this, function* test_backoff500() {
|
||||
add_identity_test(this, function test_backoff500() {
|
||||
_("Test: HTTP 500 sets backoff status.");
|
||||
let server = sync_httpd_setup();
|
||||
yield setUp(server);
|
||||
|
|
@ -102,7 +102,7 @@ add_identity_test(this, function* test_backoff500() {
|
|||
yield promiseStopServer(server);
|
||||
});
|
||||
|
||||
add_identity_test(this, function* test_backoff503() {
|
||||
add_identity_test(this, function test_backoff503() {
|
||||
_("Test: HTTP 503 with Retry-After header leads to backoff notification and sets backoff status.");
|
||||
let server = sync_httpd_setup();
|
||||
yield setUp(server);
|
||||
|
|
@ -138,7 +138,7 @@ add_identity_test(this, function* test_backoff503() {
|
|||
yield promiseStopServer(server);
|
||||
});
|
||||
|
||||
add_identity_test(this, function* test_overQuota() {
|
||||
add_identity_test(this, function test_overQuota() {
|
||||
_("Test: HTTP 400 with body error code 14 means over quota.");
|
||||
let server = sync_httpd_setup();
|
||||
yield setUp(server);
|
||||
|
|
@ -167,7 +167,7 @@ add_identity_test(this, function* test_overQuota() {
|
|||
yield promiseStopServer(server);
|
||||
});
|
||||
|
||||
add_identity_test(this, function* test_service_networkError() {
|
||||
add_identity_test(this, function test_service_networkError() {
|
||||
_("Test: Connection refused error from Service.sync() leads to the right status code.");
|
||||
let server = sync_httpd_setup();
|
||||
yield setUp(server);
|
||||
|
|
@ -193,14 +193,13 @@ add_identity_test(this, function* test_service_networkError() {
|
|||
yield deferred.promise;
|
||||
});
|
||||
|
||||
add_identity_test(this, function* test_service_offline() {
|
||||
add_identity_test(this, function test_service_offline() {
|
||||
_("Test: Wanting to sync in offline mode leads to the right status code but does not increment the ignorable error count.");
|
||||
let server = sync_httpd_setup();
|
||||
yield setUp(server);
|
||||
let deferred = Promise.defer();
|
||||
server.stop(() => {
|
||||
Services.io.offline = true;
|
||||
Services.prefs.setBoolPref("network.dns.offline-localhost", false);
|
||||
|
||||
try {
|
||||
do_check_eq(Status.sync, SYNC_SUCCEEDED);
|
||||
|
|
@ -215,13 +214,12 @@ add_identity_test(this, function* test_service_offline() {
|
|||
Service.startOver();
|
||||
}
|
||||
Services.io.offline = false;
|
||||
Services.prefs.clearUserPref("network.dns.offline-localhost");
|
||||
deferred.resolve();
|
||||
});
|
||||
yield deferred.promise;
|
||||
});
|
||||
|
||||
add_identity_test(this, function* test_engine_networkError() {
|
||||
add_identity_test(this, function test_engine_networkError() {
|
||||
_("Test: Network related exceptions from engine.sync() lead to the right status code.");
|
||||
let server = sync_httpd_setup();
|
||||
yield setUp(server);
|
||||
|
|
@ -248,7 +246,7 @@ add_identity_test(this, function* test_engine_networkError() {
|
|||
yield promiseStopServer(server);
|
||||
});
|
||||
|
||||
add_identity_test(this, function* test_resource_timeout() {
|
||||
add_identity_test(this, function test_resource_timeout() {
|
||||
let server = sync_httpd_setup();
|
||||
yield setUp(server);
|
||||
|
||||
|
|
@ -276,7 +274,6 @@ add_identity_test(this, function* test_resource_timeout() {
|
|||
});
|
||||
|
||||
function run_test() {
|
||||
validate_all_future_pings();
|
||||
engineManager.register(CatapultEngine);
|
||||
run_next_test();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,93 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
Cu.import("resource://services-crypto/utils.js");
|
||||
Cu.import("resource://services-sync/engines/extension-storage.js");
|
||||
Cu.import("resource://services-sync/util.js");
|
||||
|
||||
/**
|
||||
* Like Assert.throws, but for generators.
|
||||
*
|
||||
* @param {string | Object | function} constraint
|
||||
* What to use to check the exception.
|
||||
* @param {function} f
|
||||
* The function to call.
|
||||
*/
|
||||
function* throwsGen(constraint, f) {
|
||||
let threw = false;
|
||||
let exception;
|
||||
try {
|
||||
yield* f();
|
||||
}
|
||||
catch (e) {
|
||||
threw = true;
|
||||
exception = e;
|
||||
}
|
||||
|
||||
ok(threw, "did not throw an exception");
|
||||
|
||||
const debuggingMessage = `got ${exception}, expected ${constraint}`;
|
||||
let message = exception;
|
||||
if (typeof exception === "object") {
|
||||
message = exception.message;
|
||||
}
|
||||
|
||||
if (typeof constraint === "function") {
|
||||
ok(constraint(message), debuggingMessage);
|
||||
} else {
|
||||
ok(constraint === message, debuggingMessage);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* An EncryptionRemoteTransformer that uses a fixed key bundle,
|
||||
* suitable for testing.
|
||||
*/
|
||||
class StaticKeyEncryptionRemoteTransformer extends EncryptionRemoteTransformer {
|
||||
constructor(keyBundle) {
|
||||
super();
|
||||
this.keyBundle = keyBundle;
|
||||
}
|
||||
|
||||
getKeys() {
|
||||
return Promise.resolve(this.keyBundle);
|
||||
}
|
||||
}
|
||||
const BORING_KB = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef";
|
||||
const STRETCHED_KEY = CryptoUtils.hkdf(BORING_KB, undefined, `testing storage.sync encryption`, 2*32);
|
||||
const KEY_BUNDLE = {
|
||||
sha256HMACHasher: Utils.makeHMACHasher(Ci.nsICryptoHMAC.SHA256, Utils.makeHMACKey(STRETCHED_KEY.slice(0, 32))),
|
||||
encryptionKeyB64: btoa(STRETCHED_KEY.slice(32, 64)),
|
||||
};
|
||||
const transformer = new StaticKeyEncryptionRemoteTransformer(KEY_BUNDLE);
|
||||
|
||||
add_task(function* test_encryption_transformer_roundtrip() {
|
||||
const POSSIBLE_DATAS = [
|
||||
"string",
|
||||
2, // number
|
||||
[1, 2, 3], // array
|
||||
{key: "value"}, // object
|
||||
];
|
||||
|
||||
for (let data of POSSIBLE_DATAS) {
|
||||
const record = {data: data, id: "key-some_2D_key", key: "some-key"};
|
||||
|
||||
deepEqual(record, yield transformer.decode(yield transformer.encode(record)));
|
||||
}
|
||||
});
|
||||
|
||||
add_task(function* test_refuses_to_decrypt_tampered() {
|
||||
const encryptedRecord = yield transformer.encode({data: [1, 2, 3], id: "key-some_2D_key", key: "some-key"});
|
||||
const tamperedHMAC = Object.assign({}, encryptedRecord, {hmac: "0000000000000000000000000000000000000000000000000000000000000001"});
|
||||
yield* throwsGen(Utils.isHMACMismatch, function*() {
|
||||
yield transformer.decode(tamperedHMAC);
|
||||
});
|
||||
|
||||
const tamperedIV = Object.assign({}, encryptedRecord, {IV: "aaaaaaaaaaaaaaaaaaaaaa=="});
|
||||
yield* throwsGen(Utils.isHMACMismatch, function*() {
|
||||
yield transformer.decode(tamperedIV);
|
||||
});
|
||||
});
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
Cu.import("resource://services-sync/engines.js");
|
||||
Cu.import("resource://services-sync/engines/extension-storage.js");
|
||||
Cu.import("resource://services-sync/service.js");
|
||||
Cu.import("resource://services-sync/util.js");
|
||||
Cu.import("resource://testing-common/services/sync/utils.js");
|
||||
Cu.import("resource://gre/modules/ExtensionStorageSync.jsm");
|
||||
|
||||
Service.engineManager.register(ExtensionStorageEngine);
|
||||
const engine = Service.engineManager.get("extension-storage");
|
||||
do_get_profile(); // so we can use FxAccounts
|
||||
loadWebExtensionTestFunctions();
|
||||
|
||||
function mock(options) {
|
||||
let calls = [];
|
||||
let ret = function() {
|
||||
calls.push(arguments);
|
||||
return options.returns;
|
||||
}
|
||||
Object.setPrototypeOf(ret, {
|
||||
__proto__: Function.prototype,
|
||||
get calls() {
|
||||
return calls;
|
||||
}
|
||||
});
|
||||
return ret;
|
||||
}
|
||||
|
||||
add_task(function* test_calling_sync_calls__sync() {
|
||||
let oldSync = ExtensionStorageEngine.prototype._sync;
|
||||
let syncMock = ExtensionStorageEngine.prototype._sync = mock({returns: true});
|
||||
try {
|
||||
// I wanted to call the main sync entry point for the entire
|
||||
// package, but that fails because it tries to sync ClientEngine
|
||||
// first, which fails.
|
||||
yield engine.sync();
|
||||
} finally {
|
||||
ExtensionStorageEngine.prototype._sync = oldSync;
|
||||
}
|
||||
equal(syncMock.calls.length, 1);
|
||||
});
|
||||
|
||||
add_task(function* test_calling_sync_calls_ext_storage_sync() {
|
||||
const extension = {id: "my-extension"};
|
||||
let oldSync = ExtensionStorageSync.syncAll;
|
||||
let syncMock = ExtensionStorageSync.syncAll = mock({returns: Promise.resolve()});
|
||||
try {
|
||||
yield* withSyncContext(function* (context) {
|
||||
// Set something so that everyone knows that we're using storage.sync
|
||||
yield ExtensionStorageSync.set(extension, {"a": "b"}, context);
|
||||
|
||||
yield engine._sync();
|
||||
});
|
||||
} finally {
|
||||
ExtensionStorageSync.syncAll = oldSync;
|
||||
}
|
||||
do_check_true(syncMock.calls.length >= 1);
|
||||
});
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
Cu.import("resource://services-sync/constants.js");
|
||||
Cu.import("resource://services-sync/engines.js");
|
||||
Cu.import("resource://services-sync/engines/extension-storage.js");
|
||||
Cu.import("resource://services-sync/service.js");
|
||||
Cu.import("resource://services-sync/util.js");
|
||||
Cu.import("resource://gre/modules/ExtensionStorageSync.jsm");
|
||||
|
||||
Service.engineManager.register(ExtensionStorageEngine);
|
||||
const engine = Service.engineManager.get("extension-storage");
|
||||
do_get_profile(); // so we can use FxAccounts
|
||||
loadWebExtensionTestFunctions();
|
||||
|
||||
add_task(function* test_changing_extension_storage_changes_score() {
|
||||
const tracker = engine._tracker;
|
||||
const extension = {id: "my-extension-id"};
|
||||
Svc.Obs.notify("weave:engine:start-tracking");
|
||||
yield* withSyncContext(function*(context) {
|
||||
yield ExtensionStorageSync.set(extension, {"a": "b"}, context);
|
||||
});
|
||||
do_check_eq(tracker.score, SCORE_INCREMENT_MEDIUM);
|
||||
|
||||
tracker.resetScore();
|
||||
yield* withSyncContext(function*(context) {
|
||||
yield ExtensionStorageSync.remove(extension, "a", context);
|
||||
});
|
||||
do_check_eq(tracker.score, SCORE_INCREMENT_MEDIUM);
|
||||
|
||||
Svc.Obs.notify("weave:engine:stop-tracking");
|
||||
});
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
|
@ -40,18 +40,6 @@ function run_test() {
|
|||
addEntry("address", "Memory Lane");
|
||||
do_check_attribute_count(tracker.changedIDs, 3);
|
||||
|
||||
|
||||
_("Check that ignoreAll is respected");
|
||||
tracker.clearChangedIDs();
|
||||
tracker.score = 0;
|
||||
tracker.ignoreAll = true;
|
||||
addEntry("username", "johndoe123");
|
||||
addEntry("favoritecolor", "green");
|
||||
removeEntry("name", "John Doe");
|
||||
tracker.ignoreAll = false;
|
||||
do_check_empty(tracker.changedIDs);
|
||||
equal(tracker.score, 0);
|
||||
|
||||
_("Let's stop tracking again.");
|
||||
tracker.clearChangedIDs();
|
||||
Svc.Obs.notify("weave:engine:stop-tracking");
|
||||
|
|
@ -63,8 +51,6 @@ function run_test() {
|
|||
removeEntry("email", "john@doe.com");
|
||||
do_check_empty(tracker.changedIDs);
|
||||
|
||||
|
||||
|
||||
} finally {
|
||||
_("Clean up.");
|
||||
engine._store.wipe();
|
||||
|
|
|
|||
279
services/sync/tests/unit/test_fxa_migration.js
Normal file
279
services/sync/tests/unit/test_fxa_migration.js
Normal file
|
|
@ -0,0 +1,279 @@
|
|||
// Test the FxAMigration module
|
||||
Cu.import("resource://services-sync/FxaMigrator.jsm");
|
||||
Cu.import("resource://gre/modules/Promise.jsm");
|
||||
Cu.import("resource://gre/modules/FxAccounts.jsm");
|
||||
Cu.import("resource://gre/modules/FxAccountsCommon.js");
|
||||
Cu.import("resource://services-sync/browserid_identity.js");
|
||||
|
||||
// Set our username pref early so sync initializes with the legacy provider.
|
||||
Services.prefs.setCharPref("services.sync.username", "foo");
|
||||
// And ensure all debug messages end up being printed.
|
||||
Services.prefs.setCharPref("services.sync.log.appender.dump", "Debug");
|
||||
|
||||
// Now import sync
|
||||
Cu.import("resource://services-sync/service.js");
|
||||
Cu.import("resource://services-sync/record.js");
|
||||
Cu.import("resource://services-sync/util.js");
|
||||
|
||||
// And reset the username.
|
||||
Services.prefs.clearUserPref("services.sync.username");
|
||||
|
||||
Cu.import("resource://testing-common/services/sync/utils.js");
|
||||
Cu.import("resource://testing-common/services/common/logging.js");
|
||||
Cu.import("resource://testing-common/services/sync/rotaryengine.js");
|
||||
|
||||
const FXA_USERNAME = "someone@somewhere";
|
||||
|
||||
// Utilities
|
||||
function promiseOneObserver(topic) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let observer = function(subject, topic, data) {
|
||||
Services.obs.removeObserver(observer, topic);
|
||||
resolve({ subject: subject, data: data });
|
||||
}
|
||||
Services.obs.addObserver(observer, topic, false);
|
||||
});
|
||||
}
|
||||
|
||||
function promiseStopServer(server) {
|
||||
return new Promise((resolve, reject) => {
|
||||
server.stop(resolve);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// Helpers
|
||||
function configureLegacySync() {
|
||||
let engine = new RotaryEngine(Service);
|
||||
engine.enabled = true;
|
||||
Svc.Prefs.set("registerEngines", engine.name);
|
||||
Svc.Prefs.set("log.logger.engine.rotary", "Trace");
|
||||
|
||||
let contents = {
|
||||
meta: {global: {engines: {rotary: {version: engine.version,
|
||||
syncID: engine.syncID}}}},
|
||||
crypto: {},
|
||||
rotary: {}
|
||||
};
|
||||
|
||||
const USER = "foo";
|
||||
const PASSPHRASE = "abcdeabcdeabcdeabcdeabcdea";
|
||||
|
||||
setBasicCredentials(USER, "password", PASSPHRASE);
|
||||
|
||||
let onRequest = function(request, response) {
|
||||
// ideally we'd only do this while a legacy user is configured, but WTH.
|
||||
response.setHeader("x-weave-alert", JSON.stringify({code: "soft-eol"}));
|
||||
}
|
||||
let server = new SyncServer({onRequest: onRequest});
|
||||
server.registerUser(USER, "password");
|
||||
server.createContents(USER, contents);
|
||||
server.start();
|
||||
|
||||
Service.serverURL = server.baseURI;
|
||||
Service.clusterURL = server.baseURI;
|
||||
Service.identity.username = USER;
|
||||
Service._updateCachedURLs();
|
||||
|
||||
Service.engineManager._engines[engine.name] = engine;
|
||||
|
||||
return [engine, server];
|
||||
}
|
||||
|
||||
function configureFxa() {
|
||||
Services.prefs.setCharPref("identity.fxaccounts.auth.uri", "http://localhost");
|
||||
}
|
||||
|
||||
add_task(function *testMigration() {
|
||||
configureFxa();
|
||||
|
||||
// when we do a .startOver we want the new provider.
|
||||
let oldValue = Services.prefs.getBoolPref("services.sync-testing.startOverKeepIdentity");
|
||||
Services.prefs.setBoolPref("services.sync-testing.startOverKeepIdentity", false);
|
||||
|
||||
// disable the addons engine - this engine choice is arbitrary, but we
|
||||
// want to check it remains disabled after migration.
|
||||
Services.prefs.setBoolPref("services.sync.engine.addons", false);
|
||||
|
||||
do_register_cleanup(() => {
|
||||
Services.prefs.setBoolPref("services.sync-testing.startOverKeepIdentity", oldValue)
|
||||
Services.prefs.setBoolPref("services.sync.engine.addons", true);
|
||||
});
|
||||
|
||||
// No sync user - that should report no user-action necessary.
|
||||
Assert.deepEqual((yield fxaMigrator._queueCurrentUserState()), null,
|
||||
"no user state when complete");
|
||||
|
||||
// Arrange for a legacy sync user and manually bump the migrator
|
||||
let [engine, server] = configureLegacySync();
|
||||
|
||||
// Check our disabling of the "addons" engine worked, and for good measure,
|
||||
// that the "passwords" engine is enabled.
|
||||
Assert.ok(!Service.engineManager.get("addons").enabled, "addons is disabled");
|
||||
Assert.ok(Service.engineManager.get("passwords").enabled, "passwords is enabled");
|
||||
|
||||
// monkey-patch the migration sentinel code so we know it was called.
|
||||
let haveStartedSentinel = false;
|
||||
let origSetFxAMigrationSentinel = Service.setFxAMigrationSentinel;
|
||||
let promiseSentinelWritten = new Promise((resolve, reject) => {
|
||||
Service.setFxAMigrationSentinel = function(arg) {
|
||||
haveStartedSentinel = true;
|
||||
return origSetFxAMigrationSentinel.call(Service, arg).then(result => {
|
||||
Service.setFxAMigrationSentinel = origSetFxAMigrationSentinel;
|
||||
resolve(result);
|
||||
return result;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// We are now configured for legacy sync, but we aren't in an EOL state yet,
|
||||
// so should still be not waiting for a user.
|
||||
Assert.deepEqual((yield fxaMigrator._queueCurrentUserState()), null,
|
||||
"no user state before server EOL");
|
||||
|
||||
// Start a sync - this will cause an EOL notification which the migrator's
|
||||
// observer will notice.
|
||||
let promise = promiseOneObserver("fxa-migration:state-changed");
|
||||
_("Starting sync");
|
||||
Service.sync();
|
||||
_("Finished sync");
|
||||
|
||||
// We should have seen the observer, so be waiting for an FxA user.
|
||||
Assert.equal((yield promise).data, fxaMigrator.STATE_USER_FXA, "now waiting for FxA.")
|
||||
|
||||
// Re-calling our user-state promise should also reflect the same state.
|
||||
Assert.equal((yield fxaMigrator._queueCurrentUserState()),
|
||||
fxaMigrator.STATE_USER_FXA,
|
||||
"still waiting for FxA.");
|
||||
|
||||
// arrange for an unverified FxA user.
|
||||
let config = makeIdentityConfig({username: FXA_USERNAME});
|
||||
let fxa = new FxAccounts({});
|
||||
config.fxaccount.user.email = config.username;
|
||||
delete config.fxaccount.user.verified;
|
||||
// *sob* - shouldn't need this boilerplate
|
||||
fxa.internal.currentAccountState.getCertificate = function(data, keyPair, mustBeValidUntil) {
|
||||
this.cert = {
|
||||
validUntil: fxa.internal.now() + CERT_LIFETIME,
|
||||
cert: "certificate",
|
||||
};
|
||||
return Promise.resolve(this.cert.cert);
|
||||
};
|
||||
|
||||
// As soon as we set the FxA user the observers should fire and magically
|
||||
// transition.
|
||||
promise = promiseOneObserver("fxa-migration:state-changed");
|
||||
fxAccounts.setSignedInUser(config.fxaccount.user);
|
||||
|
||||
let observerInfo = yield promise;
|
||||
Assert.equal(observerInfo.data,
|
||||
fxaMigrator.STATE_USER_FXA_VERIFIED,
|
||||
"now waiting for verification");
|
||||
Assert.ok(observerInfo.subject instanceof Ci.nsISupportsString,
|
||||
"email was passed to observer");
|
||||
Assert.equal(observerInfo.subject.data,
|
||||
FXA_USERNAME,
|
||||
"email passed to observer is correct");
|
||||
|
||||
// should have seen the user set, so state should automatically update.
|
||||
Assert.equal((yield fxaMigrator._queueCurrentUserState()),
|
||||
fxaMigrator.STATE_USER_FXA_VERIFIED,
|
||||
"now waiting for verification");
|
||||
|
||||
// Before we verify the user, fire off a sync that calls us back during
|
||||
// the sync and before it completes - this way we can ensure we do the right
|
||||
// thing in terms of blocking sync and waiting for it to complete.
|
||||
|
||||
let wasWaiting = false;
|
||||
// This is a PITA as sync is pseudo-blocking.
|
||||
engine._syncFinish = function () {
|
||||
// We aren't in a generator here, so use a helper to block on promises.
|
||||
function getState() {
|
||||
let cb = Async.makeSpinningCallback();
|
||||
fxaMigrator._queueCurrentUserState().then(state => cb(null, state));
|
||||
return cb.wait();
|
||||
}
|
||||
// should still be waiting for verification.
|
||||
Assert.equal(getState(), fxaMigrator.STATE_USER_FXA_VERIFIED,
|
||||
"still waiting for verification");
|
||||
|
||||
// arrange for the user to be verified. The fxAccount's mock story is
|
||||
// broken, so go behind its back.
|
||||
config.fxaccount.user.verified = true;
|
||||
fxAccounts.setSignedInUser(config.fxaccount.user);
|
||||
Services.obs.notifyObservers(null, ONVERIFIED_NOTIFICATION, null);
|
||||
|
||||
// spinningly wait for the migrator to catch up - sync is running so
|
||||
// we should be in a 'null' user-state as there is no user-action
|
||||
// necessary.
|
||||
let cb = Async.makeSpinningCallback();
|
||||
promiseOneObserver("fxa-migration:state-changed").then(({ data: state }) => cb(null, state));
|
||||
Assert.equal(cb.wait(), null, "no user action necessary while sync completes.");
|
||||
|
||||
// We must not have started writing the sentinel yet.
|
||||
Assert.ok(!haveStartedSentinel, "haven't written a sentinel yet");
|
||||
|
||||
// sync should be blocked from continuing
|
||||
Assert.ok(Service.scheduler.isBlocked, "sync is blocked.")
|
||||
|
||||
wasWaiting = true;
|
||||
throw ex;
|
||||
};
|
||||
|
||||
_("Starting sync");
|
||||
Service.sync();
|
||||
_("Finished sync");
|
||||
|
||||
// mock sync so we can ensure the final sync is scheduled with the FxA user.
|
||||
// (letting a "normal" sync complete is a PITA without mocking huge amounts
|
||||
// of FxA infra)
|
||||
let promiseFinalSync = new Promise((resolve, reject) => {
|
||||
let oldSync = Service.sync;
|
||||
Service.sync = function() {
|
||||
Service.sync = oldSync;
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
|
||||
Assert.ok(wasWaiting, "everything was good while sync was running.")
|
||||
|
||||
// The migration is now going to run to completion.
|
||||
// sync should still be "blocked"
|
||||
Assert.ok(Service.scheduler.isBlocked, "sync is blocked.");
|
||||
|
||||
// We should see the migration sentinel written and it should return true.
|
||||
Assert.ok((yield promiseSentinelWritten), "wrote the sentinel");
|
||||
|
||||
// And we should see a new sync start
|
||||
yield promiseFinalSync;
|
||||
|
||||
// and we should be configured for FxA
|
||||
let WeaveService = Cc["@mozilla.org/weave/service;1"]
|
||||
.getService(Components.interfaces.nsISupports)
|
||||
.wrappedJSObject;
|
||||
Assert.ok(WeaveService.fxAccountsEnabled, "FxA is enabled");
|
||||
Assert.ok(Service.identity instanceof BrowserIDManager,
|
||||
"sync is configured with the browserid_identity provider.");
|
||||
Assert.equal(Service.identity.username, config.username, "correct user configured")
|
||||
Assert.ok(!Service.scheduler.isBlocked, "sync is not blocked.")
|
||||
// and the user state should remain null.
|
||||
Assert.deepEqual((yield fxaMigrator._queueCurrentUserState()),
|
||||
null,
|
||||
"still no user action necessary");
|
||||
// and our engines should be in the same enabled/disabled state as before.
|
||||
Assert.ok(!Service.engineManager.get("addons").enabled, "addons is still disabled");
|
||||
Assert.ok(Service.engineManager.get("passwords").enabled, "passwords is still enabled");
|
||||
|
||||
// aaaand, we are done - clean up.
|
||||
yield promiseStopServer(server);
|
||||
});
|
||||
|
||||
|
||||
function run_test() {
|
||||
initTestLogging();
|
||||
do_register_cleanup(() => {
|
||||
fxaMigrator.finalize();
|
||||
Svc.Prefs.resetBranch("");
|
||||
});
|
||||
run_next_test();
|
||||
}
|
||||
150
services/sync/tests/unit/test_fxa_migration_sentinel.js
Normal file
150
services/sync/tests/unit/test_fxa_migration_sentinel.js
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Test the reading and writing of the sync migration sentinel.
|
||||
Cu.import("resource://gre/modules/Promise.jsm");
|
||||
Cu.import("resource://gre/modules/FxAccounts.jsm");
|
||||
Cu.import("resource://gre/modules/FxAccountsCommon.js");
|
||||
|
||||
Cu.import("resource://testing-common/services/sync/utils.js");
|
||||
Cu.import("resource://testing-common/services/common/logging.js");
|
||||
|
||||
Cu.import("resource://services-sync/record.js");
|
||||
|
||||
// Set our username pref early so sync initializes with the legacy provider.
|
||||
Services.prefs.setCharPref("services.sync.username", "foo");
|
||||
|
||||
// Now import sync
|
||||
Cu.import("resource://services-sync/service.js");
|
||||
|
||||
const USER = "foo";
|
||||
const PASSPHRASE = "abcdeabcdeabcdeabcdeabcdea";
|
||||
|
||||
function promiseStopServer(server) {
|
||||
return new Promise((resolve, reject) => {
|
||||
server.stop(resolve);
|
||||
});
|
||||
}
|
||||
|
||||
let numServerRequests = 0;
|
||||
|
||||
// Helpers
|
||||
function configureLegacySync() {
|
||||
let contents = {
|
||||
meta: {global: {}},
|
||||
crypto: {},
|
||||
};
|
||||
|
||||
setBasicCredentials(USER, "password", PASSPHRASE);
|
||||
|
||||
numServerRequests = 0;
|
||||
let server = new SyncServer({
|
||||
onRequest: () => {
|
||||
++numServerRequests
|
||||
}
|
||||
});
|
||||
server.registerUser(USER, "password");
|
||||
server.createContents(USER, contents);
|
||||
server.start();
|
||||
|
||||
Service.serverURL = server.baseURI;
|
||||
Service.clusterURL = server.baseURI;
|
||||
Service.identity.username = USER;
|
||||
Service._updateCachedURLs();
|
||||
|
||||
return server;
|
||||
}
|
||||
|
||||
// Test a simple round-trip of the get/set functions.
|
||||
add_task(function *() {
|
||||
// Arrange for a legacy sync user.
|
||||
let server = configureLegacySync();
|
||||
|
||||
Assert.equal((yield Service.getFxAMigrationSentinel()), null, "no sentinel to start");
|
||||
|
||||
let sentinel = {foo: "bar"};
|
||||
yield Service.setFxAMigrationSentinel(sentinel);
|
||||
|
||||
Assert.deepEqual((yield Service.getFxAMigrationSentinel()), sentinel, "got the sentinel back");
|
||||
|
||||
yield promiseStopServer(server);
|
||||
});
|
||||
|
||||
// Test the records are cached by the record manager.
|
||||
add_task(function *() {
|
||||
// Arrange for a legacy sync user.
|
||||
let server = configureLegacySync();
|
||||
Service.login();
|
||||
|
||||
// Reset the request count here as the login would have made some.
|
||||
numServerRequests = 0;
|
||||
|
||||
Assert.equal((yield Service.getFxAMigrationSentinel()), null, "no sentinel to start");
|
||||
Assert.equal(numServerRequests, 1, "first fetch should hit the server");
|
||||
|
||||
let sentinel = {foo: "bar"};
|
||||
yield Service.setFxAMigrationSentinel(sentinel);
|
||||
Assert.equal(numServerRequests, 2, "setting sentinel should hit the server");
|
||||
|
||||
Assert.deepEqual((yield Service.getFxAMigrationSentinel()), sentinel, "got the sentinel back");
|
||||
Assert.equal(numServerRequests, 2, "second fetch should not should hit the server");
|
||||
|
||||
// Clobber the caches and ensure we still get the correct value back when we
|
||||
// do hit the server.
|
||||
Service.recordManager.clearCache();
|
||||
Assert.deepEqual((yield Service.getFxAMigrationSentinel()), sentinel, "got the sentinel back");
|
||||
Assert.equal(numServerRequests, 3, "should have re-hit the server with empty caches");
|
||||
|
||||
yield promiseStopServer(server);
|
||||
});
|
||||
|
||||
// Test the records are cached by a sync.
|
||||
add_task(function* () {
|
||||
let server = configureLegacySync();
|
||||
|
||||
// A first sync clobbers meta/global due to it being empty, so we first
|
||||
// do a sync which forces a good set of data on the server.
|
||||
Service.sync();
|
||||
|
||||
// Now create a sentinel exists on the server. It's encrypted, so we need to
|
||||
// put an encrypted version.
|
||||
let cryptoWrapper = new CryptoWrapper("meta", "fxa_credentials");
|
||||
let sentinel = {foo: "bar"};
|
||||
cryptoWrapper.cleartext = {
|
||||
id: "fxa_credentials",
|
||||
sentinel: sentinel,
|
||||
deleted: false,
|
||||
}
|
||||
cryptoWrapper.encrypt(Service.identity.syncKeyBundle);
|
||||
let payload = {
|
||||
ciphertext: cryptoWrapper.ciphertext,
|
||||
IV: cryptoWrapper.IV,
|
||||
hmac: cryptoWrapper.hmac,
|
||||
};
|
||||
|
||||
server.createContents(USER, {
|
||||
meta: {fxa_credentials: payload},
|
||||
crypto: {},
|
||||
});
|
||||
|
||||
// Another sync - this will cause the encrypted record to be fetched.
|
||||
Service.sync();
|
||||
// Reset the request count here as the sync will have made many!
|
||||
numServerRequests = 0;
|
||||
|
||||
// Asking for the sentinel should use the copy cached in the record manager.
|
||||
Assert.deepEqual((yield Service.getFxAMigrationSentinel()), sentinel, "got it");
|
||||
Assert.equal(numServerRequests, 0, "should not have hit the server");
|
||||
|
||||
// And asking for it again should work (we have to work around the fact the
|
||||
// ciphertext is clobbered on first decrypt...)
|
||||
Assert.deepEqual((yield Service.getFxAMigrationSentinel()), sentinel, "got it again");
|
||||
Assert.equal(numServerRequests, 0, "should not have hit the server");
|
||||
|
||||
yield promiseStopServer(server);
|
||||
});
|
||||
|
||||
function run_test() {
|
||||
initTestLogging();
|
||||
run_next_test();
|
||||
}
|
||||
|
|
@ -1,368 +1,321 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
_("Test that node reassignment happens correctly using the FxA identity mgr.");
|
||||
// The node-reassignment logic is quite different for FxA than for the legacy
|
||||
// provider. In particular, there's no special request necessary for
|
||||
// reassignment - it comes from the token server - so we need to ensure the
|
||||
// Fxa cluster manager grabs a new token.
|
||||
|
||||
Cu.import("resource://gre/modules/Log.jsm");
|
||||
Cu.import("resource://services-common/rest.js");
|
||||
Cu.import("resource://services-sync/constants.js");
|
||||
Cu.import("resource://services-sync/service.js");
|
||||
Cu.import("resource://services-sync/status.js");
|
||||
Cu.import("resource://services-sync/util.js");
|
||||
Cu.import("resource://testing-common/services/sync/rotaryengine.js");
|
||||
Cu.import("resource://services-sync/browserid_identity.js");
|
||||
Cu.import("resource://testing-common/services/sync/utils.js");
|
||||
|
||||
Service.engineManager.clear();
|
||||
|
||||
function run_test() {
|
||||
Log.repository.getLogger("Sync.AsyncResource").level = Log.Level.Trace;
|
||||
Log.repository.getLogger("Sync.ErrorHandler").level = Log.Level.Trace;
|
||||
Log.repository.getLogger("Sync.Resource").level = Log.Level.Trace;
|
||||
Log.repository.getLogger("Sync.RESTRequest").level = Log.Level.Trace;
|
||||
Log.repository.getLogger("Sync.Service").level = Log.Level.Trace;
|
||||
Log.repository.getLogger("Sync.SyncScheduler").level = Log.Level.Trace;
|
||||
initTestLogging();
|
||||
|
||||
Service.engineManager.register(RotaryEngine);
|
||||
|
||||
// Setup the FxA identity manager and cluster manager.
|
||||
Status.__authManager = Service.identity = new BrowserIDManager();
|
||||
Service._clusterManager = Service.identity.createClusterManager(Service);
|
||||
|
||||
// None of the failures in this file should result in a UI error.
|
||||
function onUIError() {
|
||||
do_throw("Errors should not be presented in the UI.");
|
||||
}
|
||||
Svc.Obs.add("weave:ui:login:error", onUIError);
|
||||
Svc.Obs.add("weave:ui:sync:error", onUIError);
|
||||
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
|
||||
// API-compatible with SyncServer handler. Bind `handler` to something to use
|
||||
// as a ServerCollection handler.
|
||||
function handleReassign(handler, req, resp) {
|
||||
resp.setStatusLine(req.httpVersion, 401, "Node reassignment");
|
||||
resp.setHeader("Content-Type", "application/json");
|
||||
let reassignBody = JSON.stringify({error: "401inator in place"});
|
||||
resp.bodyOutputStream.write(reassignBody, reassignBody.length);
|
||||
}
|
||||
|
||||
var numTokenRequests = 0;
|
||||
|
||||
function prepareServer(cbAfterTokenFetch) {
|
||||
let config = makeIdentityConfig({username: "johndoe"});
|
||||
// A server callback to ensure we don't accidentally hit the wrong endpoint
|
||||
// after a node reassignment.
|
||||
let callback = {
|
||||
__proto__: SyncServerCallback,
|
||||
onRequest(req, resp) {
|
||||
let full = `${req.scheme}://${req.host}:${req.port}${req.path}`;
|
||||
do_check_true(full.startsWith(config.fxaccount.token.endpoint),
|
||||
`request made to ${full}`);
|
||||
}
|
||||
}
|
||||
let server = new SyncServer(callback);
|
||||
server.registerUser("johndoe");
|
||||
server.start();
|
||||
|
||||
// Set the token endpoint for the initial token request that's done implicitly
|
||||
// via configureIdentity.
|
||||
config.fxaccount.token.endpoint = server.baseURI + "1.1/johndoe/";
|
||||
// And future token fetches will do magic around numReassigns.
|
||||
let numReassigns = 0;
|
||||
return configureIdentity(config).then(() => {
|
||||
Service.identity._tokenServerClient = {
|
||||
getTokenFromBrowserIDAssertion: function(uri, assertion, cb) {
|
||||
// Build a new URL with trailing zeros for the SYNC_VERSION part - this
|
||||
// will still be seen as equivalent by the test server, but different
|
||||
// by sync itself.
|
||||
numReassigns += 1;
|
||||
let trailingZeros = new Array(numReassigns + 1).join('0');
|
||||
let token = config.fxaccount.token;
|
||||
token.endpoint = server.baseURI + "1.1" + trailingZeros + "/johndoe";
|
||||
token.uid = config.username;
|
||||
numTokenRequests += 1;
|
||||
cb(null, token);
|
||||
if (cbAfterTokenFetch) {
|
||||
cbAfterTokenFetch();
|
||||
}
|
||||
},
|
||||
};
|
||||
return server;
|
||||
});
|
||||
}
|
||||
|
||||
function getReassigned() {
|
||||
try {
|
||||
return Services.prefs.getBoolPref("services.sync.lastSyncReassigned");
|
||||
} catch (ex) {
|
||||
if (ex.result == Cr.NS_ERROR_UNEXPECTED) {
|
||||
return false;
|
||||
}
|
||||
do_throw("Got exception retrieving lastSyncReassigned: " +
|
||||
Log.exceptionStr(ex));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a test request to `url`, then watch the result of two syncs
|
||||
* to ensure that a node request was made.
|
||||
* Runs `between` between the two. This can be used to undo deliberate failure
|
||||
* setup, detach observers, etc.
|
||||
*/
|
||||
function* syncAndExpectNodeReassignment(server, firstNotification, between,
|
||||
secondNotification, url) {
|
||||
_("Starting syncAndExpectNodeReassignment\n");
|
||||
let deferred = Promise.defer();
|
||||
function onwards() {
|
||||
let numTokenRequestsBefore;
|
||||
function onFirstSync() {
|
||||
_("First sync completed.");
|
||||
Svc.Obs.remove(firstNotification, onFirstSync);
|
||||
Svc.Obs.add(secondNotification, onSecondSync);
|
||||
|
||||
do_check_eq(Service.clusterURL, "");
|
||||
|
||||
// Track whether we fetched a new token.
|
||||
numTokenRequestsBefore = numTokenRequests;
|
||||
|
||||
// Allow for tests to clean up error conditions.
|
||||
between();
|
||||
}
|
||||
function onSecondSync() {
|
||||
_("Second sync completed.");
|
||||
Svc.Obs.remove(secondNotification, onSecondSync);
|
||||
Service.scheduler.clearSyncTriggers();
|
||||
|
||||
// Make absolutely sure that any event listeners are done with their work
|
||||
// before we proceed.
|
||||
waitForZeroTimer(function () {
|
||||
_("Second sync nextTick.");
|
||||
do_check_eq(numTokenRequests, numTokenRequestsBefore + 1, "fetched a new token");
|
||||
Service.startOver();
|
||||
server.stop(deferred.resolve);
|
||||
});
|
||||
}
|
||||
|
||||
Svc.Obs.add(firstNotification, onFirstSync);
|
||||
Service.sync();
|
||||
}
|
||||
|
||||
// Make sure that we really do get a 401 (but we can only do that if we are
|
||||
// already logged in, as the login process is what sets up the URLs)
|
||||
if (Service.isLoggedIn) {
|
||||
_("Making request to " + url + " which should 401");
|
||||
let request = new RESTRequest(url);
|
||||
request.get(function () {
|
||||
do_check_eq(request.response.status, 401);
|
||||
Utils.nextTick(onwards);
|
||||
});
|
||||
} else {
|
||||
_("Skipping preliminary validation check for a 401 as we aren't logged in");
|
||||
Utils.nextTick(onwards);
|
||||
}
|
||||
yield deferred.promise;
|
||||
}
|
||||
|
||||
// Check that when we sync we don't request a new token by default - our
|
||||
// test setup has configured the client with a valid token, and that token
|
||||
// should be used to form the cluster URL.
|
||||
add_task(function* test_single_token_fetch() {
|
||||
_("Test a normal sync only fetches 1 token");
|
||||
|
||||
let numTokenFetches = 0;
|
||||
|
||||
function afterTokenFetch() {
|
||||
numTokenFetches++;
|
||||
}
|
||||
|
||||
// Set the cluster URL to an "old" version - this is to ensure we don't
|
||||
// use that old cached version for the first sync but prefer the value
|
||||
// we got from the token (and as above, we are also checking we don't grab
|
||||
// a new token). If the test actually attempts to connect to this URL
|
||||
// it will crash.
|
||||
Service.clusterURL = "http://example.com/";
|
||||
|
||||
let server = yield prepareServer(afterTokenFetch);
|
||||
|
||||
do_check_false(Service.isLoggedIn, "not already logged in");
|
||||
Service.sync();
|
||||
do_check_eq(Status.sync, SYNC_SUCCEEDED, "sync succeeded");
|
||||
do_check_eq(numTokenFetches, 0, "didn't fetch a new token");
|
||||
// A bit hacky, but given we know how prepareServer works we can deduce
|
||||
// that clusterURL we expect.
|
||||
let expectedClusterURL = server.baseURI + "1.1/johndoe/";
|
||||
do_check_eq(Service.clusterURL, expectedClusterURL);
|
||||
yield new Promise(resolve => server.stop(resolve));
|
||||
});
|
||||
|
||||
add_task(function* test_momentary_401_engine() {
|
||||
_("Test a failure for engine URLs that's resolved by reassignment.");
|
||||
let server = yield prepareServer();
|
||||
let john = server.user("johndoe");
|
||||
|
||||
_("Enabling the Rotary engine.");
|
||||
let engine = Service.engineManager.get("rotary");
|
||||
engine.enabled = true;
|
||||
|
||||
// We need the server to be correctly set up prior to experimenting. Do this
|
||||
// through a sync.
|
||||
let global = {syncID: Service.syncID,
|
||||
storageVersion: STORAGE_VERSION,
|
||||
rotary: {version: engine.version,
|
||||
syncID: engine.syncID}}
|
||||
john.createCollection("meta").insert("global", global);
|
||||
|
||||
_("First sync to prepare server contents.");
|
||||
Service.sync();
|
||||
|
||||
_("Setting up Rotary collection to 401.");
|
||||
let rotary = john.createCollection("rotary");
|
||||
let oldHandler = rotary.collectionHandler;
|
||||
rotary.collectionHandler = handleReassign.bind(this, undefined);
|
||||
|
||||
// We want to verify that the clusterURL pref has been cleared after a 401
|
||||
// inside a sync. Flag the Rotary engine to need syncing.
|
||||
john.collection("rotary").timestamp += 1000;
|
||||
|
||||
function between() {
|
||||
_("Undoing test changes.");
|
||||
rotary.collectionHandler = oldHandler;
|
||||
|
||||
function onLoginStart() {
|
||||
// lastSyncReassigned shouldn't be cleared until a sync has succeeded.
|
||||
_("Ensuring that lastSyncReassigned is still set at next sync start.");
|
||||
Svc.Obs.remove("weave:service:login:start", onLoginStart);
|
||||
do_check_true(getReassigned());
|
||||
}
|
||||
|
||||
_("Adding observer that lastSyncReassigned is still set on login.");
|
||||
Svc.Obs.add("weave:service:login:start", onLoginStart);
|
||||
}
|
||||
|
||||
yield syncAndExpectNodeReassignment(server,
|
||||
"weave:service:sync:finish",
|
||||
between,
|
||||
"weave:service:sync:finish",
|
||||
Service.storageURL + "rotary");
|
||||
});
|
||||
|
||||
// This test ends up being a failing info fetch *after we're already logged in*.
|
||||
add_task(function* test_momentary_401_info_collections_loggedin() {
|
||||
_("Test a failure for info/collections after login that's resolved by reassignment.");
|
||||
let server = yield prepareServer();
|
||||
|
||||
_("First sync to prepare server contents.");
|
||||
Service.sync();
|
||||
|
||||
_("Arrange for info/collections to return a 401.");
|
||||
let oldHandler = server.toplevelHandlers.info;
|
||||
server.toplevelHandlers.info = handleReassign;
|
||||
|
||||
function undo() {
|
||||
_("Undoing test changes.");
|
||||
server.toplevelHandlers.info = oldHandler;
|
||||
}
|
||||
|
||||
do_check_true(Service.isLoggedIn, "already logged in");
|
||||
|
||||
yield syncAndExpectNodeReassignment(server,
|
||||
"weave:service:sync:error",
|
||||
undo,
|
||||
"weave:service:sync:finish",
|
||||
Service.infoURL);
|
||||
});
|
||||
|
||||
// This test ends up being a failing info fetch *before we're logged in*.
|
||||
// In this case we expect to recover during the login phase - so the first
|
||||
// sync succeeds.
|
||||
add_task(function* test_momentary_401_info_collections_loggedout() {
|
||||
_("Test a failure for info/collections before login that's resolved by reassignment.");
|
||||
|
||||
let oldHandler;
|
||||
let sawTokenFetch = false;
|
||||
|
||||
function afterTokenFetch() {
|
||||
// After a single token fetch, we undo our evil handleReassign hack, so
|
||||
// the next /info request returns the collection instead of a 401
|
||||
server.toplevelHandlers.info = oldHandler;
|
||||
sawTokenFetch = true;
|
||||
}
|
||||
|
||||
let server = yield prepareServer(afterTokenFetch);
|
||||
|
||||
// Return a 401 for the next /info request - it will be reset immediately
|
||||
// after a new token is fetched.
|
||||
oldHandler = server.toplevelHandlers.info
|
||||
server.toplevelHandlers.info = handleReassign;
|
||||
|
||||
do_check_false(Service.isLoggedIn, "not already logged in");
|
||||
|
||||
Service.sync();
|
||||
do_check_eq(Status.sync, SYNC_SUCCEEDED, "sync succeeded");
|
||||
// sync was successful - check we grabbed a new token.
|
||||
do_check_true(sawTokenFetch, "a new token was fetched by this test.")
|
||||
// and we are done.
|
||||
Service.startOver();
|
||||
let deferred = Promise.defer();
|
||||
server.stop(deferred.resolve);
|
||||
yield deferred.promise;
|
||||
});
|
||||
|
||||
// This test ends up being a failing meta/global fetch *after we're already logged in*.
|
||||
add_task(function* test_momentary_401_storage_loggedin() {
|
||||
_("Test a failure for any storage URL after login that's resolved by" +
|
||||
"reassignment.");
|
||||
let server = yield prepareServer();
|
||||
|
||||
_("First sync to prepare server contents.");
|
||||
Service.sync();
|
||||
|
||||
_("Arrange for meta/global to return a 401.");
|
||||
let oldHandler = server.toplevelHandlers.storage;
|
||||
server.toplevelHandlers.storage = handleReassign;
|
||||
|
||||
function undo() {
|
||||
_("Undoing test changes.");
|
||||
server.toplevelHandlers.storage = oldHandler;
|
||||
}
|
||||
|
||||
do_check_true(Service.isLoggedIn, "already logged in");
|
||||
|
||||
yield syncAndExpectNodeReassignment(server,
|
||||
"weave:service:sync:error",
|
||||
undo,
|
||||
"weave:service:sync:finish",
|
||||
Service.storageURL + "meta/global");
|
||||
});
|
||||
|
||||
// This test ends up being a failing meta/global fetch *before we've logged in*.
|
||||
add_task(function* test_momentary_401_storage_loggedout() {
|
||||
_("Test a failure for any storage URL before login, not just engine parts. " +
|
||||
"Resolved by reassignment.");
|
||||
let server = yield prepareServer();
|
||||
|
||||
// Return a 401 for all storage requests.
|
||||
let oldHandler = server.toplevelHandlers.storage;
|
||||
server.toplevelHandlers.storage = handleReassign;
|
||||
|
||||
function undo() {
|
||||
_("Undoing test changes.");
|
||||
server.toplevelHandlers.storage = oldHandler;
|
||||
}
|
||||
|
||||
do_check_false(Service.isLoggedIn, "already logged in");
|
||||
|
||||
yield syncAndExpectNodeReassignment(server,
|
||||
"weave:service:login:error",
|
||||
undo,
|
||||
"weave:service:sync:finish",
|
||||
Service.storageURL + "meta/global");
|
||||
});
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
_("Test that node reassignment happens correctly using the FxA identity mgr.");
|
||||
// The node-reassignment logic is quite different for FxA than for the legacy
|
||||
// provider. In particular, there's no special request necessary for
|
||||
// reassignment - it comes from the token server - so we need to ensure the
|
||||
// Fxa cluster manager grabs a new token.
|
||||
|
||||
Cu.import("resource://gre/modules/Log.jsm");
|
||||
Cu.import("resource://services-common/rest.js");
|
||||
Cu.import("resource://services-sync/constants.js");
|
||||
Cu.import("resource://services-sync/service.js");
|
||||
Cu.import("resource://services-sync/status.js");
|
||||
Cu.import("resource://services-sync/util.js");
|
||||
Cu.import("resource://testing-common/services/sync/rotaryengine.js");
|
||||
Cu.import("resource://services-sync/browserid_identity.js");
|
||||
Cu.import("resource://testing-common/services/sync/utils.js");
|
||||
|
||||
Service.engineManager.clear();
|
||||
|
||||
function run_test() {
|
||||
Log.repository.getLogger("Sync.AsyncResource").level = Log.Level.Trace;
|
||||
Log.repository.getLogger("Sync.ErrorHandler").level = Log.Level.Trace;
|
||||
Log.repository.getLogger("Sync.Resource").level = Log.Level.Trace;
|
||||
Log.repository.getLogger("Sync.RESTRequest").level = Log.Level.Trace;
|
||||
Log.repository.getLogger("Sync.Service").level = Log.Level.Trace;
|
||||
Log.repository.getLogger("Sync.SyncScheduler").level = Log.Level.Trace;
|
||||
initTestLogging();
|
||||
|
||||
Service.engineManager.register(RotaryEngine);
|
||||
|
||||
// Setup the FxA identity manager and cluster manager.
|
||||
Status.__authManager = Service.identity = new BrowserIDManager();
|
||||
Service._clusterManager = Service.identity.createClusterManager(Service);
|
||||
|
||||
// None of the failures in this file should result in a UI error.
|
||||
function onUIError() {
|
||||
do_throw("Errors should not be presented in the UI.");
|
||||
}
|
||||
Svc.Obs.add("weave:ui:login:error", onUIError);
|
||||
Svc.Obs.add("weave:ui:sync:error", onUIError);
|
||||
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
|
||||
// API-compatible with SyncServer handler. Bind `handler` to something to use
|
||||
// as a ServerCollection handler.
|
||||
function handleReassign(handler, req, resp) {
|
||||
resp.setStatusLine(req.httpVersion, 401, "Node reassignment");
|
||||
resp.setHeader("Content-Type", "application/json");
|
||||
let reassignBody = JSON.stringify({error: "401inator in place"});
|
||||
resp.bodyOutputStream.write(reassignBody, reassignBody.length);
|
||||
}
|
||||
|
||||
let numTokenRequests = 0;
|
||||
|
||||
function prepareServer(cbAfterTokenFetch) {
|
||||
let config = makeIdentityConfig({username: "johndoe"});
|
||||
let server = new SyncServer();
|
||||
server.registerUser("johndoe");
|
||||
server.start();
|
||||
|
||||
// Set the token endpoint for the initial token request that's done implicitly
|
||||
// via configureIdentity.
|
||||
config.fxaccount.token.endpoint = server.baseURI + "1.1/johndoe";
|
||||
// And future token fetches will do magic around numReassigns.
|
||||
let numReassigns = 0;
|
||||
return configureIdentity(config).then(() => {
|
||||
Service.identity._tokenServerClient = {
|
||||
getTokenFromBrowserIDAssertion: function(uri, assertion, cb) {
|
||||
// Build a new URL with trailing zeros for the SYNC_VERSION part - this
|
||||
// will still be seen as equivalent by the test server, but different
|
||||
// by sync itself.
|
||||
numReassigns += 1;
|
||||
let trailingZeros = new Array(numReassigns + 1).join('0');
|
||||
let token = config.fxaccount.token;
|
||||
token.endpoint = server.baseURI + "1.1" + trailingZeros + "/johndoe";
|
||||
token.uid = config.username;
|
||||
numTokenRequests += 1;
|
||||
cb(null, token);
|
||||
if (cbAfterTokenFetch) {
|
||||
cbAfterTokenFetch();
|
||||
}
|
||||
},
|
||||
};
|
||||
Service.clusterURL = config.fxaccount.token.endpoint;
|
||||
return server;
|
||||
});
|
||||
}
|
||||
|
||||
function getReassigned() {
|
||||
try {
|
||||
return Services.prefs.getBoolPref("services.sync.lastSyncReassigned");
|
||||
} catch (ex if (ex.result == Cr.NS_ERROR_UNEXPECTED)) {
|
||||
return false;
|
||||
} catch (ex) {
|
||||
do_throw("Got exception retrieving lastSyncReassigned: " +
|
||||
Utils.exceptionStr(ex));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a test request to `url`, then watch the result of two syncs
|
||||
* to ensure that a node request was made.
|
||||
* Runs `between` between the two. This can be used to undo deliberate failure
|
||||
* setup, detach observers, etc.
|
||||
*/
|
||||
function syncAndExpectNodeReassignment(server, firstNotification, between,
|
||||
secondNotification, url) {
|
||||
_("Starting syncAndExpectNodeReassignment\n");
|
||||
let deferred = Promise.defer();
|
||||
function onwards() {
|
||||
let numTokenRequestsBefore;
|
||||
function onFirstSync() {
|
||||
_("First sync completed.");
|
||||
Svc.Obs.remove(firstNotification, onFirstSync);
|
||||
Svc.Obs.add(secondNotification, onSecondSync);
|
||||
|
||||
do_check_eq(Service.clusterURL, "");
|
||||
|
||||
// Track whether we fetched a new token.
|
||||
numTokenRequestsBefore = numTokenRequests;
|
||||
|
||||
// Allow for tests to clean up error conditions.
|
||||
between();
|
||||
}
|
||||
function onSecondSync() {
|
||||
_("Second sync completed.");
|
||||
Svc.Obs.remove(secondNotification, onSecondSync);
|
||||
Service.scheduler.clearSyncTriggers();
|
||||
|
||||
// Make absolutely sure that any event listeners are done with their work
|
||||
// before we proceed.
|
||||
waitForZeroTimer(function () {
|
||||
_("Second sync nextTick.");
|
||||
do_check_eq(numTokenRequests, numTokenRequestsBefore + 1, "fetched a new token");
|
||||
Service.startOver();
|
||||
server.stop(deferred.resolve);
|
||||
});
|
||||
}
|
||||
|
||||
Svc.Obs.add(firstNotification, onFirstSync);
|
||||
Service.sync();
|
||||
}
|
||||
|
||||
// Make sure that it works!
|
||||
_("Making request to " + url + " which should 401");
|
||||
let request = new RESTRequest(url);
|
||||
request.get(function () {
|
||||
do_check_eq(request.response.status, 401);
|
||||
Utils.nextTick(onwards);
|
||||
});
|
||||
yield deferred.promise;
|
||||
}
|
||||
|
||||
add_task(function test_momentary_401_engine() {
|
||||
_("Test a failure for engine URLs that's resolved by reassignment.");
|
||||
let server = yield prepareServer();
|
||||
let john = server.user("johndoe");
|
||||
|
||||
_("Enabling the Rotary engine.");
|
||||
let engine = Service.engineManager.get("rotary");
|
||||
engine.enabled = true;
|
||||
|
||||
// We need the server to be correctly set up prior to experimenting. Do this
|
||||
// through a sync.
|
||||
let global = {syncID: Service.syncID,
|
||||
storageVersion: STORAGE_VERSION,
|
||||
rotary: {version: engine.version,
|
||||
syncID: engine.syncID}}
|
||||
john.createCollection("meta").insert("global", global);
|
||||
|
||||
_("First sync to prepare server contents.");
|
||||
Service.sync();
|
||||
|
||||
_("Setting up Rotary collection to 401.");
|
||||
let rotary = john.createCollection("rotary");
|
||||
let oldHandler = rotary.collectionHandler;
|
||||
rotary.collectionHandler = handleReassign.bind(this, undefined);
|
||||
|
||||
// We want to verify that the clusterURL pref has been cleared after a 401
|
||||
// inside a sync. Flag the Rotary engine to need syncing.
|
||||
john.collection("rotary").timestamp += 1000;
|
||||
|
||||
function between() {
|
||||
_("Undoing test changes.");
|
||||
rotary.collectionHandler = oldHandler;
|
||||
|
||||
function onLoginStart() {
|
||||
// lastSyncReassigned shouldn't be cleared until a sync has succeeded.
|
||||
_("Ensuring that lastSyncReassigned is still set at next sync start.");
|
||||
Svc.Obs.remove("weave:service:login:start", onLoginStart);
|
||||
do_check_true(getReassigned());
|
||||
}
|
||||
|
||||
_("Adding observer that lastSyncReassigned is still set on login.");
|
||||
Svc.Obs.add("weave:service:login:start", onLoginStart);
|
||||
}
|
||||
|
||||
yield syncAndExpectNodeReassignment(server,
|
||||
"weave:service:sync:finish",
|
||||
between,
|
||||
"weave:service:sync:finish",
|
||||
Service.storageURL + "rotary");
|
||||
});
|
||||
|
||||
// This test ends up being a failing info fetch *after we're already logged in*.
|
||||
add_task(function test_momentary_401_info_collections_loggedin() {
|
||||
_("Test a failure for info/collections after login that's resolved by reassignment.");
|
||||
let server = yield prepareServer();
|
||||
|
||||
_("First sync to prepare server contents.");
|
||||
Service.sync();
|
||||
|
||||
_("Arrange for info/collections to return a 401.");
|
||||
let oldHandler = server.toplevelHandlers.info;
|
||||
server.toplevelHandlers.info = handleReassign;
|
||||
|
||||
function undo() {
|
||||
_("Undoing test changes.");
|
||||
server.toplevelHandlers.info = oldHandler;
|
||||
}
|
||||
|
||||
do_check_true(Service.isLoggedIn, "already logged in");
|
||||
|
||||
yield syncAndExpectNodeReassignment(server,
|
||||
"weave:service:sync:error",
|
||||
undo,
|
||||
"weave:service:sync:finish",
|
||||
Service.infoURL);
|
||||
});
|
||||
|
||||
// This test ends up being a failing info fetch *before we're logged in*.
|
||||
// In this case we expect to recover during the login phase - so the first
|
||||
// sync succeeds.
|
||||
add_task(function test_momentary_401_info_collections_loggedout() {
|
||||
_("Test a failure for info/collections before login that's resolved by reassignment.");
|
||||
|
||||
let oldHandler;
|
||||
let sawTokenFetch = false;
|
||||
|
||||
function afterTokenFetch() {
|
||||
// After a single token fetch, we undo our evil handleReassign hack, so
|
||||
// the next /info request returns the collection instead of a 401
|
||||
server.toplevelHandlers.info = oldHandler;
|
||||
sawTokenFetch = true;
|
||||
}
|
||||
|
||||
let server = yield prepareServer(afterTokenFetch);
|
||||
|
||||
// Return a 401 for the next /info request - it will be reset immediately
|
||||
// after a new token is fetched.
|
||||
oldHandler = server.toplevelHandlers.info
|
||||
server.toplevelHandlers.info = handleReassign;
|
||||
|
||||
do_check_false(Service.isLoggedIn, "not already logged in");
|
||||
|
||||
Service.sync();
|
||||
do_check_eq(Status.sync, SYNC_SUCCEEDED, "sync succeeded");
|
||||
// sync was successful - check we grabbed a new token.
|
||||
do_check_true(sawTokenFetch, "a new token was fetched by this test.")
|
||||
// and we are done.
|
||||
Service.startOver();
|
||||
let deferred = Promise.defer();
|
||||
server.stop(deferred.resolve);
|
||||
yield deferred.promise;
|
||||
});
|
||||
|
||||
// This test ends up being a failing meta/global fetch *after we're already logged in*.
|
||||
add_task(function test_momentary_401_storage_loggedin() {
|
||||
_("Test a failure for any storage URL after login that's resolved by" +
|
||||
"reassignment.");
|
||||
let server = yield prepareServer();
|
||||
|
||||
_("First sync to prepare server contents.");
|
||||
Service.sync();
|
||||
|
||||
_("Arrange for meta/global to return a 401.");
|
||||
let oldHandler = server.toplevelHandlers.storage;
|
||||
server.toplevelHandlers.storage = handleReassign;
|
||||
|
||||
function undo() {
|
||||
_("Undoing test changes.");
|
||||
server.toplevelHandlers.storage = oldHandler;
|
||||
}
|
||||
|
||||
do_check_true(Service.isLoggedIn, "already logged in");
|
||||
|
||||
yield syncAndExpectNodeReassignment(server,
|
||||
"weave:service:sync:error",
|
||||
undo,
|
||||
"weave:service:sync:finish",
|
||||
Service.storageURL + "meta/global");
|
||||
});
|
||||
|
||||
// This test ends up being a failing meta/global fetch *before we've logged in*.
|
||||
add_task(function test_momentary_401_storage_loggedout() {
|
||||
_("Test a failure for any storage URL before login, not just engine parts. " +
|
||||
"Resolved by reassignment.");
|
||||
let server = yield prepareServer();
|
||||
|
||||
// Return a 401 for all storage requests.
|
||||
let oldHandler = server.toplevelHandlers.storage;
|
||||
server.toplevelHandlers.storage = handleReassign;
|
||||
|
||||
function undo() {
|
||||
_("Undoing test changes.");
|
||||
server.toplevelHandlers.storage = oldHandler;
|
||||
}
|
||||
|
||||
do_check_false(Service.isLoggedIn, "already logged in");
|
||||
|
||||
yield syncAndExpectNodeReassignment(server,
|
||||
"weave:service:login:error",
|
||||
undo,
|
||||
"weave:service:sync:finish",
|
||||
Service.storageURL + "meta/global");
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -1,68 +1,68 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
Cu.import("resource://services-sync/service.js");
|
||||
Cu.import("resource://services-sync/util.js");
|
||||
Cu.import("resource://testing-common/services/sync/fxa_utils.js");
|
||||
Cu.import("resource://testing-common/services/sync/utils.js");
|
||||
|
||||
add_task(function* test_findCluster() {
|
||||
_("Test FxA _findCluster()");
|
||||
|
||||
_("_findCluster() throws on 500 errors.");
|
||||
initializeIdentityWithTokenServerResponse({
|
||||
status: 500,
|
||||
headers: [],
|
||||
body: "",
|
||||
});
|
||||
|
||||
yield Service.identity.initializeWithCurrentIdentity();
|
||||
yield Assert.rejects(Service.identity.whenReadyToAuthenticate.promise,
|
||||
"should reject due to 500");
|
||||
|
||||
Assert.throws(function() {
|
||||
Service._clusterManager._findCluster();
|
||||
});
|
||||
|
||||
_("_findCluster() returns null on authentication errors.");
|
||||
initializeIdentityWithTokenServerResponse({
|
||||
status: 401,
|
||||
headers: {"content-type": "application/json"},
|
||||
body: "{}",
|
||||
});
|
||||
|
||||
yield Service.identity.initializeWithCurrentIdentity();
|
||||
yield Assert.rejects(Service.identity.whenReadyToAuthenticate.promise,
|
||||
"should reject due to 401");
|
||||
|
||||
cluster = Service._clusterManager._findCluster();
|
||||
Assert.strictEqual(cluster, null);
|
||||
|
||||
_("_findCluster() works with correct tokenserver response.");
|
||||
let endpoint = "http://example.com/something";
|
||||
initializeIdentityWithTokenServerResponse({
|
||||
status: 200,
|
||||
headers: {"content-type": "application/json"},
|
||||
body:
|
||||
JSON.stringify({
|
||||
api_endpoint: endpoint,
|
||||
duration: 300,
|
||||
id: "id",
|
||||
key: "key",
|
||||
uid: "uid",
|
||||
})
|
||||
});
|
||||
|
||||
yield Service.identity.initializeWithCurrentIdentity();
|
||||
yield Service.identity.whenReadyToAuthenticate.promise;
|
||||
cluster = Service._clusterManager._findCluster();
|
||||
// The cluster manager ensures a trailing "/"
|
||||
Assert.strictEqual(cluster, endpoint + "/");
|
||||
|
||||
Svc.Prefs.resetBranch("");
|
||||
});
|
||||
|
||||
function run_test() {
|
||||
initTestLogging();
|
||||
run_next_test();
|
||||
}
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
Cu.import("resource://services-sync/service.js");
|
||||
Cu.import("resource://services-sync/util.js");
|
||||
Cu.import("resource://testing-common/services/sync/fxa_utils.js");
|
||||
Cu.import("resource://testing-common/services/sync/utils.js");
|
||||
|
||||
add_task(function test_findCluster() {
|
||||
_("Test FxA _findCluster()");
|
||||
|
||||
_("_findCluster() throws on 500 errors.");
|
||||
initializeIdentityWithTokenServerResponse({
|
||||
status: 500,
|
||||
headers: [],
|
||||
body: "",
|
||||
});
|
||||
|
||||
yield Service.identity.initializeWithCurrentIdentity();
|
||||
yield Assert.rejects(Service.identity.whenReadyToAuthenticate.promise,
|
||||
"should reject due to 500");
|
||||
|
||||
Assert.throws(function() {
|
||||
Service._clusterManager._findCluster();
|
||||
});
|
||||
|
||||
_("_findCluster() returns null on authentication errors.");
|
||||
initializeIdentityWithTokenServerResponse({
|
||||
status: 401,
|
||||
headers: {"content-type": "application/json"},
|
||||
body: "{}",
|
||||
});
|
||||
|
||||
yield Service.identity.initializeWithCurrentIdentity();
|
||||
yield Assert.rejects(Service.identity.whenReadyToAuthenticate.promise,
|
||||
"should reject due to 401");
|
||||
|
||||
cluster = Service._clusterManager._findCluster();
|
||||
Assert.strictEqual(cluster, null);
|
||||
|
||||
_("_findCluster() works with correct tokenserver response.");
|
||||
let endpoint = "http://example.com/something";
|
||||
initializeIdentityWithTokenServerResponse({
|
||||
status: 200,
|
||||
headers: {"content-type": "application/json"},
|
||||
body:
|
||||
JSON.stringify({
|
||||
api_endpoint: endpoint,
|
||||
duration: 300,
|
||||
id: "id",
|
||||
key: "key",
|
||||
uid: "uid",
|
||||
})
|
||||
});
|
||||
|
||||
yield Service.identity.initializeWithCurrentIdentity();
|
||||
yield Service.identity.whenReadyToAuthenticate.promise;
|
||||
cluster = Service._clusterManager._findCluster();
|
||||
// The cluster manager ensures a trailing "/"
|
||||
Assert.strictEqual(cluster, endpoint + "/");
|
||||
|
||||
Svc.Prefs.resetBranch("");
|
||||
});
|
||||
|
||||
function run_test() {
|
||||
initTestLogging();
|
||||
run_next_test();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,63 +1,63 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
Cu.import("resource://testing-common/services/sync/utils.js");
|
||||
Cu.import("resource://services-sync/identity.js");
|
||||
Cu.import("resource://services-sync/browserid_identity.js");
|
||||
Cu.import("resource://services-sync/service.js");
|
||||
|
||||
function run_test() {
|
||||
initTestLogging("Trace");
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* test_startover() {
|
||||
let oldValue = Services.prefs.getBoolPref("services.sync-testing.startOverKeepIdentity", true);
|
||||
Services.prefs.setBoolPref("services.sync-testing.startOverKeepIdentity", false);
|
||||
|
||||
ensureLegacyIdentityManager();
|
||||
yield configureIdentity({username: "johndoe"});
|
||||
|
||||
// The boolean flag on the xpcom service should reflect a legacy provider.
|
||||
let xps = Cc["@mozilla.org/weave/service;1"]
|
||||
.getService(Components.interfaces.nsISupports)
|
||||
.wrappedJSObject;
|
||||
do_check_false(xps.fxAccountsEnabled);
|
||||
|
||||
// we expect the "legacy" provider (but can't instanceof that, as BrowserIDManager
|
||||
// extends it)
|
||||
do_check_false(Service.identity instanceof BrowserIDManager);
|
||||
|
||||
Service.serverURL = "https://localhost/";
|
||||
Service.clusterURL = Service.serverURL;
|
||||
|
||||
Service.login();
|
||||
// We should have a cluster URL
|
||||
do_check_true(Service.clusterURL.length > 0);
|
||||
|
||||
// remember some stuff so we can reset it after.
|
||||
let oldIdentity = Service.identity;
|
||||
let oldClusterManager = Service._clusterManager;
|
||||
let deferred = Promise.defer();
|
||||
Services.obs.addObserver(function observeStartOverFinished() {
|
||||
Services.obs.removeObserver(observeStartOverFinished, "weave:service:start-over:finish");
|
||||
deferred.resolve();
|
||||
}, "weave:service:start-over:finish", false);
|
||||
|
||||
Service.startOver();
|
||||
yield deferred.promise; // wait for the observer to fire.
|
||||
|
||||
// the xpcom service should indicate FxA is enabled.
|
||||
do_check_true(xps.fxAccountsEnabled);
|
||||
// should have swapped identities.
|
||||
do_check_true(Service.identity instanceof BrowserIDManager);
|
||||
// should have clobbered the cluster URL
|
||||
do_check_eq(Service.clusterURL, "");
|
||||
|
||||
// we should have thrown away the old identity provider and cluster manager.
|
||||
do_check_neq(oldIdentity, Service.identity);
|
||||
do_check_neq(oldClusterManager, Service._clusterManager);
|
||||
|
||||
// reset the world.
|
||||
Services.prefs.setBoolPref("services.sync-testing.startOverKeepIdentity", oldValue);
|
||||
});
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
Cu.import("resource://testing-common/services/sync/utils.js");
|
||||
Cu.import("resource://services-sync/identity.js");
|
||||
Cu.import("resource://services-sync/browserid_identity.js");
|
||||
Cu.import("resource://services-sync/service.js");
|
||||
|
||||
function run_test() {
|
||||
initTestLogging("Trace");
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* test_startover() {
|
||||
let oldValue = Services.prefs.getBoolPref("services.sync-testing.startOverKeepIdentity", true);
|
||||
Services.prefs.setBoolPref("services.sync-testing.startOverKeepIdentity", false);
|
||||
|
||||
ensureLegacyIdentityManager();
|
||||
yield configureIdentity({username: "johndoe"});
|
||||
|
||||
// The boolean flag on the xpcom service should reflect a legacy provider.
|
||||
let xps = Cc["@mozilla.org/weave/service;1"]
|
||||
.getService(Components.interfaces.nsISupports)
|
||||
.wrappedJSObject;
|
||||
do_check_false(xps.fxAccountsEnabled);
|
||||
|
||||
// we expect the "legacy" provider (but can't instanceof that, as BrowserIDManager
|
||||
// extends it)
|
||||
do_check_false(Service.identity instanceof BrowserIDManager);
|
||||
|
||||
Service.serverURL = "https://localhost/";
|
||||
Service.clusterURL = Service.serverURL;
|
||||
|
||||
Service.login();
|
||||
// We should have a cluster URL
|
||||
do_check_true(Service.clusterURL.length > 0);
|
||||
|
||||
// remember some stuff so we can reset it after.
|
||||
let oldIdentity = Service.identity;
|
||||
let oldClusterManager = Service._clusterManager;
|
||||
let deferred = Promise.defer();
|
||||
Services.obs.addObserver(function observeStartOverFinished() {
|
||||
Services.obs.removeObserver(observeStartOverFinished, "weave:service:start-over:finish");
|
||||
deferred.resolve();
|
||||
}, "weave:service:start-over:finish", false);
|
||||
|
||||
Service.startOver();
|
||||
yield deferred.promise; // wait for the observer to fire.
|
||||
|
||||
// the xpcom service should indicate FxA is enabled.
|
||||
do_check_true(xps.fxAccountsEnabled);
|
||||
// should have swapped identities.
|
||||
do_check_true(Service.identity instanceof BrowserIDManager);
|
||||
// should have clobbered the cluster URL
|
||||
do_check_eq(Service.clusterURL, "");
|
||||
|
||||
// we should have thrown away the old identity provider and cluster manager.
|
||||
do_check_neq(oldIdentity, Service.identity);
|
||||
do_check_neq(oldClusterManager, Service._clusterManager);
|
||||
|
||||
// reset the world.
|
||||
Services.prefs.setBoolPref("services.sync-testing.startOverKeepIdentity", oldValue);
|
||||
});
|
||||
|
|
|
|||
194
services/sync/tests/unit/test_healthreport.js
Normal file
194
services/sync/tests/unit/test_healthreport.js
Normal file
|
|
@ -0,0 +1,194 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
Cu.import("resource://gre/modules/Metrics.jsm", this);
|
||||
Cu.import("resource://gre/modules/Preferences.jsm", this);
|
||||
Cu.import("resource://gre/modules/Promise.jsm", this);
|
||||
Cu.import("resource://services-sync/main.js", this);
|
||||
Cu.import("resource://services-sync/healthreport.jsm", this);
|
||||
Cu.import("resource://testing-common/services/common/logging.js", this);
|
||||
Cu.import("resource://testing-common/services/healthreport/utils.jsm", this);
|
||||
|
||||
function run_test() {
|
||||
initTestLogging();
|
||||
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function test_constructor() {
|
||||
let provider = new SyncProvider();
|
||||
});
|
||||
|
||||
// Provider can initialize and de-initialize properly.
|
||||
add_task(function* test_init() {
|
||||
let storage = yield Metrics.Storage("init");
|
||||
let provider = new SyncProvider();
|
||||
yield provider.init(storage);
|
||||
yield provider.shutdown();
|
||||
yield storage.close();
|
||||
});
|
||||
|
||||
add_task(function* test_collect() {
|
||||
let storage = yield Metrics.Storage("collect");
|
||||
let provider = new SyncProvider();
|
||||
yield provider.init(storage);
|
||||
|
||||
// Initially nothing should be configured.
|
||||
let now = new Date();
|
||||
yield provider.collectDailyData();
|
||||
|
||||
let m = provider.getMeasurement("sync", 1);
|
||||
let values = yield m.getValues();
|
||||
Assert.equal(values.days.size, 1);
|
||||
Assert.ok(values.days.hasDay(now));
|
||||
let day = values.days.getDay(now);
|
||||
Assert.ok(day.has("enabled"));
|
||||
Assert.ok(day.has("activeProtocol"));
|
||||
Assert.ok(day.has("preferredProtocol"));
|
||||
Assert.equal(day.get("enabled"), 0);
|
||||
Assert.equal(day.get("preferredProtocol"), "1.5");
|
||||
Assert.equal(day.get("activeProtocol"), "1.5",
|
||||
"Protocol without setup should be FX Accounts version.");
|
||||
|
||||
// Now check for old Sync setup.
|
||||
let branch = new Preferences("services.sync.");
|
||||
branch.set("username", "foo");
|
||||
branch.reset("fxaccounts.enabled");
|
||||
yield provider.collectDailyData();
|
||||
values = yield m.getValues();
|
||||
Assert.equal(values.days.getDay(now).get("activeProtocol"), "1.1",
|
||||
"Protocol with old Sync setup is correct.");
|
||||
|
||||
Assert.equal(Weave.Status.__authManager, undefined, "Detect code changes");
|
||||
|
||||
// Let's enable Sync so we can get more useful data.
|
||||
// We need to do this because the FHR probe only records more info if Sync
|
||||
// is configured properly.
|
||||
Weave.Service.identity.account = "johndoe";
|
||||
Weave.Service.identity.basicPassword = "ilovejane";
|
||||
Weave.Service.identity.syncKey = Weave.Utils.generatePassphrase();
|
||||
Weave.Service.clusterURL = "http://localhost/";
|
||||
Assert.equal(Weave.Status.checkSetup(), Weave.STATUS_OK);
|
||||
|
||||
yield provider.collectDailyData();
|
||||
values = yield m.getValues();
|
||||
day = values.days.getDay(now);
|
||||
Assert.equal(day.get("enabled"), 1);
|
||||
|
||||
// An empty account should have 1 device: us.
|
||||
let dm = provider.getMeasurement("devices", 1);
|
||||
values = yield dm.getValues();
|
||||
Assert.ok(values.days.hasDay(now));
|
||||
day = values.days.getDay(now);
|
||||
Assert.equal(day.size, 1);
|
||||
let engine = Weave.Service.clientsEngine;
|
||||
Assert.ok(engine);
|
||||
Assert.ok(day.has(engine.localType));
|
||||
Assert.equal(day.get(engine.localType), 1);
|
||||
|
||||
// Add some devices and ensure they show up.
|
||||
engine._store._remoteClients["id1"] = {type: "mobile"};
|
||||
engine._store._remoteClients["id2"] = {type: "tablet"};
|
||||
engine._store._remoteClients["id3"] = {type: "mobile"};
|
||||
|
||||
yield provider.collectDailyData();
|
||||
values = yield dm.getValues();
|
||||
day = values.days.getDay(now);
|
||||
|
||||
let expected = {
|
||||
"foobar": 0,
|
||||
"tablet": 1,
|
||||
"mobile": 2,
|
||||
"desktop": 0,
|
||||
};
|
||||
|
||||
for (let type in expected) {
|
||||
let count = expected[type];
|
||||
|
||||
if (engine.localType == type) {
|
||||
count++;
|
||||
}
|
||||
|
||||
if (!count) {
|
||||
Assert.ok(!day.has(type));
|
||||
} else {
|
||||
Assert.ok(day.has(type));
|
||||
Assert.equal(day.get(type), count);
|
||||
}
|
||||
}
|
||||
|
||||
engine._store._remoteClients = {};
|
||||
|
||||
yield provider.shutdown();
|
||||
yield storage.close();
|
||||
});
|
||||
|
||||
add_task(function* test_sync_events() {
|
||||
let storage = yield Metrics.Storage("sync_events");
|
||||
let provider = new SyncProvider();
|
||||
yield provider.init(storage);
|
||||
|
||||
let m = provider.getMeasurement("sync", 1);
|
||||
|
||||
for (let i = 0; i < 5; i++) {
|
||||
Services.obs.notifyObservers(null, "weave:service:sync:start", null);
|
||||
}
|
||||
|
||||
for (let i = 0; i < 3; i++) {
|
||||
Services.obs.notifyObservers(null, "weave:service:sync:finish", null);
|
||||
}
|
||||
|
||||
for (let i = 0; i < 2; i++) {
|
||||
Services.obs.notifyObservers(null, "weave:service:sync:error", null);
|
||||
}
|
||||
|
||||
// Wait for storage to complete.
|
||||
yield m.storage.enqueueOperation(() => {
|
||||
return Promise.resolve();
|
||||
});
|
||||
|
||||
let values = yield m.getValues();
|
||||
let now = new Date();
|
||||
Assert.ok(values.days.hasDay(now));
|
||||
let day = values.days.getDay(now);
|
||||
|
||||
Assert.ok(day.has("syncStart"));
|
||||
Assert.ok(day.has("syncSuccess"));
|
||||
Assert.ok(day.has("syncError"));
|
||||
Assert.equal(day.get("syncStart"), 5);
|
||||
Assert.equal(day.get("syncSuccess"), 3);
|
||||
Assert.equal(day.get("syncError"), 2);
|
||||
|
||||
yield provider.shutdown();
|
||||
yield storage.close();
|
||||
});
|
||||
|
||||
add_task(function* test_healthreporter_json() {
|
||||
let reporter = yield getHealthReporter("healthreporter_json");
|
||||
yield reporter.init();
|
||||
try {
|
||||
yield reporter._providerManager.registerProvider(new SyncProvider());
|
||||
yield reporter.collectMeasurements();
|
||||
let payload = yield reporter.getJSONPayload(true);
|
||||
let now = new Date();
|
||||
let today = reporter._formatDate(now);
|
||||
|
||||
Assert.ok(today in payload.data.days);
|
||||
let day = payload.data.days[today];
|
||||
|
||||
Assert.ok("org.mozilla.sync.sync" in day);
|
||||
Assert.ok("org.mozilla.sync.devices" in day);
|
||||
|
||||
let devices = day["org.mozilla.sync.devices"];
|
||||
let engine = Weave.Service.clientsEngine;
|
||||
Assert.ok(engine);
|
||||
let type = engine.localType;
|
||||
Assert.ok(type);
|
||||
Assert.ok(type in devices);
|
||||
Assert.equal(devices[type], 1);
|
||||
} finally {
|
||||
reporter._shutdown();
|
||||
}
|
||||
});
|
||||
155
services/sync/tests/unit/test_healthreport_migration.js
Normal file
155
services/sync/tests/unit/test_healthreport_migration.js
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
Cu.import("resource://gre/modules/Metrics.jsm", this);
|
||||
Cu.import("resource://gre/modules/Preferences.jsm", this);
|
||||
Cu.import("resource://gre/modules/Promise.jsm", this);
|
||||
Cu.import("resource://services-sync/healthreport.jsm", this);
|
||||
Cu.import("resource://services-sync/FxaMigrator.jsm", this);
|
||||
Cu.import("resource://testing-common/services/common/logging.js", this);
|
||||
Cu.import("resource://testing-common/services/healthreport/utils.jsm", this);
|
||||
|
||||
|
||||
function run_test() {
|
||||
initTestLogging();
|
||||
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* test_no_data() {
|
||||
let storage = yield Metrics.Storage("collect");
|
||||
let provider = new SyncProvider();
|
||||
yield provider.init(storage);
|
||||
|
||||
try {
|
||||
// Initially nothing should be configured.
|
||||
let now = new Date();
|
||||
yield provider.collectDailyData();
|
||||
|
||||
let m = provider.getMeasurement("migration", 1);
|
||||
let values = yield m.getValues();
|
||||
Assert.equal(values.days.size, 0);
|
||||
Assert.ok(!values.days.hasDay(now));
|
||||
} finally {
|
||||
yield provider.shutdown();
|
||||
yield storage.close();
|
||||
}
|
||||
});
|
||||
|
||||
function checkCorrectStateRecorded(provider, state) {
|
||||
// Wait for storage to complete.
|
||||
yield m.storage.enqueueOperation(() => {
|
||||
return Promise.resolve();
|
||||
});
|
||||
|
||||
let m = provider.getMeasurement("migration", 1);
|
||||
let values = yield m.getValues();
|
||||
Assert.equal(values.days.size, 1);
|
||||
Assert.ok(values.days.hasDay(now));
|
||||
let day = values.days.getDay(now);
|
||||
|
||||
Assert.ok(day.has("state"));
|
||||
Assert.equal(day.get("state"), state);
|
||||
}
|
||||
|
||||
add_task(function* test_state() {
|
||||
let storage = yield Metrics.Storage("collect");
|
||||
let provider = new SyncProvider();
|
||||
yield provider.init(storage);
|
||||
|
||||
try {
|
||||
// Initially nothing should be configured.
|
||||
let now = new Date();
|
||||
|
||||
// We record both a "user" and "internal" state in the same field.
|
||||
// So simulate a "user" state first.
|
||||
Services.obs.notifyObservers(null, "fxa-migration:state-changed",
|
||||
fxaMigrator.STATE_USER_FXA_VERIFIED);
|
||||
checkCorrectStateRecorded(provider, fxaMigrator.STATE_USER_FXA_VERIFIED);
|
||||
|
||||
// And an internal state.
|
||||
Services.obs.notifyObservers(null, "fxa-migration:internal-state-changed",
|
||||
fxaMigrator.STATE_INTERNAL_WAITING_SYNC_COMPLETE);
|
||||
checkCorrectStateRecorded(provider, fxaMigrator.STATE_INTERNAL_WAITING_SYNC_COMPLETE);
|
||||
} finally {
|
||||
yield provider.shutdown();
|
||||
yield storage.close();
|
||||
}
|
||||
});
|
||||
|
||||
add_task(function* test_flags() {
|
||||
let storage = yield Metrics.Storage("collect");
|
||||
let provider = new SyncProvider();
|
||||
yield provider.init(storage);
|
||||
|
||||
try {
|
||||
// Initially nothing should be configured.
|
||||
let now = new Date();
|
||||
|
||||
let m = provider.getMeasurement("migration", 1);
|
||||
|
||||
let record = function*(what) {
|
||||
Services.obs.notifyObservers(null, "fxa-migration:internal-telemetry", what);
|
||||
// Wait for storage to complete.
|
||||
yield m.storage.enqueueOperation(Promise.resolve);
|
||||
let values = yield m.getValues();
|
||||
Assert.equal(values.days.size, 1);
|
||||
return values.days.getDay(now);
|
||||
}
|
||||
|
||||
let values = yield m.getValues();
|
||||
Assert.equal(values.days.size, 1);
|
||||
let day = values.days.getDay(now);
|
||||
Assert.ok(!day.has(fxaMigrator.TELEMETRY_ACCEPTED));
|
||||
Assert.ok(!day.has(fxaMigrator.TELEMETRY_DECLINED));
|
||||
Assert.ok(!day.has(fxaMigrator.TELEMETRY_UNLINKED));
|
||||
|
||||
// let's send an unknown value to ensure our error mitigation works.
|
||||
day = yield record("unknown");
|
||||
Assert.ok(!day.has(fxaMigrator.TELEMETRY_ACCEPTED));
|
||||
Assert.ok(!day.has(fxaMigrator.TELEMETRY_DECLINED));
|
||||
Assert.ok(!day.has(fxaMigrator.TELEMETRY_UNLINKED));
|
||||
|
||||
// record an fxaMigrator.TELEMETRY_ACCEPTED state.
|
||||
day = yield record(fxaMigrator.TELEMETRY_ACCEPTED);
|
||||
Assert.ok(day.has(fxaMigrator.TELEMETRY_ACCEPTED));
|
||||
Assert.ok(!day.has(fxaMigrator.TELEMETRY_DECLINED));
|
||||
Assert.ok(!day.has(fxaMigrator.TELEMETRY_UNLINKED));
|
||||
Assert.equal(day.get(fxaMigrator.TELEMETRY_ACCEPTED), 1);
|
||||
|
||||
// and again - it should get 2.
|
||||
day = yield record(fxaMigrator.TELEMETRY_ACCEPTED);
|
||||
Assert.equal(day.get(fxaMigrator.TELEMETRY_ACCEPTED), 2);
|
||||
|
||||
// record fxaMigrator.TELEMETRY_DECLINED - also a counter.
|
||||
day = yield record(fxaMigrator.TELEMETRY_DECLINED);
|
||||
Assert.ok(day.has(fxaMigrator.TELEMETRY_ACCEPTED));
|
||||
Assert.ok(day.has(fxaMigrator.TELEMETRY_DECLINED));
|
||||
Assert.ok(!day.has(fxaMigrator.TELEMETRY_UNLINKED));
|
||||
Assert.equal(day.get(fxaMigrator.TELEMETRY_ACCEPTED), 2);
|
||||
Assert.equal(day.get(fxaMigrator.TELEMETRY_DECLINED), 1);
|
||||
|
||||
day = yield record(fxaMigrator.TELEMETRY_DECLINED);
|
||||
Assert.ok(day.has(fxaMigrator.TELEMETRY_ACCEPTED));
|
||||
Assert.ok(day.has(fxaMigrator.TELEMETRY_DECLINED));
|
||||
Assert.ok(!day.has(fxaMigrator.TELEMETRY_UNLINKED));
|
||||
Assert.equal(day.get(fxaMigrator.TELEMETRY_ACCEPTED), 2);
|
||||
Assert.equal(day.get(fxaMigrator.TELEMETRY_DECLINED), 2);
|
||||
|
||||
// and fxaMigrator.TELEMETRY_UNLINKED - this is conceptually a "daily bool".
|
||||
// (ie, it's DAILY_LAST_NUMERIC_FIELD and only ever has |1| written to it)
|
||||
day = yield record(fxaMigrator.TELEMETRY_UNLINKED);
|
||||
Assert.ok(day.has(fxaMigrator.TELEMETRY_ACCEPTED));
|
||||
Assert.ok(day.has(fxaMigrator.TELEMETRY_DECLINED));
|
||||
Assert.ok(day.has(fxaMigrator.TELEMETRY_UNLINKED));
|
||||
Assert.equal(day.get(fxaMigrator.TELEMETRY_UNLINKED), 1);
|
||||
// and doing it again still leaves us with |1|
|
||||
day = yield record(fxaMigrator.TELEMETRY_UNLINKED);
|
||||
Assert.equal(day.get(fxaMigrator.TELEMETRY_UNLINKED), 1);
|
||||
} finally {
|
||||
yield provider.shutdown();
|
||||
yield storage.close();
|
||||
}
|
||||
});
|
||||
|
|
@ -68,12 +68,12 @@ function ensureThrows(func) {
|
|||
};
|
||||
}
|
||||
|
||||
var store = new HistoryEngine(Service)._store;
|
||||
let store = new HistoryEngine(Service)._store;
|
||||
function applyEnsureNoFailures(records) {
|
||||
do_check_eq(store.applyIncomingBatch(records).length, 0);
|
||||
}
|
||||
|
||||
var fxuri, fxguid, tburi, tbguid;
|
||||
let fxuri, fxguid, tburi, tbguid;
|
||||
|
||||
function run_test() {
|
||||
initTestLogging("Trace");
|
||||
|
|
@ -189,8 +189,8 @@ add_test(function test_invalid_records() {
|
|||
.DBConnection;
|
||||
let stmt = connection.createAsyncStatement(
|
||||
"INSERT INTO moz_places "
|
||||
+ "(url, url_hash, title, rev_host, visit_count, last_visit_date) "
|
||||
+ "VALUES ('invalid-uri', hash('invalid-uri'), 'Invalid URI', '.', 1, " + TIMESTAMP3 + ")"
|
||||
+ "(url, title, rev_host, visit_count, last_visit_date) "
|
||||
+ "VALUES ('invalid-uri', 'Invalid URI', '.', 1, " + TIMESTAMP3 + ")"
|
||||
);
|
||||
Async.querySpinningly(stmt);
|
||||
stmt.finalize();
|
||||
|
|
@ -198,7 +198,7 @@ add_test(function test_invalid_records() {
|
|||
stmt = connection.createAsyncStatement(
|
||||
"INSERT INTO moz_historyvisits "
|
||||
+ "(place_id, visit_date, visit_type, session) "
|
||||
+ "VALUES ((SELECT id FROM moz_places WHERE url_hash = hash('invalid-uri') AND url = 'invalid-uri'), "
|
||||
+ "VALUES ((SELECT id FROM moz_places WHERE url = 'invalid-uri'), "
|
||||
+ TIMESTAMP3 + ", " + Ci.nsINavHistoryService.TRANSITION_TYPED + ", 1)"
|
||||
);
|
||||
Async.querySpinningly(stmt);
|
||||
|
|
@ -226,7 +226,7 @@ add_test(function test_invalid_records() {
|
|||
type: Ci.nsINavHistoryService.TRANSITION_EMBED}]}
|
||||
]);
|
||||
|
||||
_("Make sure we handle records with invalid visit codes or visit dates, gracefully ignoring those visits.");
|
||||
_("Make sure we report records with invalid visits, gracefully handle non-integer dates.");
|
||||
let no_date_visit_guid = Utils.makeGUID();
|
||||
let no_type_visit_guid = Utils.makeGUID();
|
||||
let invalid_type_visit_guid = Utils.makeGUID();
|
||||
|
|
@ -235,11 +235,11 @@ add_test(function test_invalid_records() {
|
|||
{id: no_date_visit_guid,
|
||||
histUri: "http://no.date.visit/",
|
||||
title: "Visit has no date",
|
||||
visits: [{type: Ci.nsINavHistoryService.TRANSITION_EMBED}]},
|
||||
visits: [{date: TIMESTAMP3}]},
|
||||
{id: no_type_visit_guid,
|
||||
histUri: "http://no.type.visit/",
|
||||
title: "Visit has no type",
|
||||
visits: [{date: TIMESTAMP3}]},
|
||||
visits: [{type: Ci.nsINavHistoryService.TRANSITION_EMBED}]},
|
||||
{id: invalid_type_visit_guid,
|
||||
histUri: "http://invalid.type.visit/",
|
||||
title: "Visit has invalid type",
|
||||
|
|
@ -251,7 +251,14 @@ add_test(function test_invalid_records() {
|
|||
visits: [{date: 1234.567,
|
||||
type: Ci.nsINavHistoryService.TRANSITION_EMBED}]}
|
||||
]);
|
||||
do_check_eq(failed.length, 0);
|
||||
do_check_eq(failed.length, 3);
|
||||
failed.sort();
|
||||
let expected = [no_date_visit_guid,
|
||||
no_type_visit_guid,
|
||||
invalid_type_visit_guid].sort();
|
||||
for (let i = 0; i < expected.length; i++) {
|
||||
do_check_eq(failed[i], expected[i]);
|
||||
}
|
||||
|
||||
_("Make sure we handle records with javascript: URLs gracefully.");
|
||||
applyEnsureNoFailures([
|
||||
|
|
|
|||
|
|
@ -22,13 +22,13 @@ function onScoreUpdated(callback) {
|
|||
|
||||
Service.engineManager.clear();
|
||||
Service.engineManager.register(HistoryEngine);
|
||||
var engine = Service.engineManager.get("history");
|
||||
var tracker = engine._tracker;
|
||||
let engine = Service.engineManager.get("history");
|
||||
let tracker = engine._tracker;
|
||||
|
||||
// Don't write out by default.
|
||||
tracker.persistChangedIDs = false;
|
||||
|
||||
var _counter = 0;
|
||||
let _counter = 0;
|
||||
function addVisit() {
|
||||
let uriString = "http://getfirefox.com/" + _counter++;
|
||||
let uri = Utils.makeURI(uriString);
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ Cu.import("resource://testing-common/services/sync/rotaryengine.js");
|
|||
Cu.import("resource://testing-common/services/sync/utils.js");
|
||||
|
||||
// Track HMAC error counts.
|
||||
var hmacErrorCount = 0;
|
||||
let hmacErrorCount = 0;
|
||||
(function () {
|
||||
let hHE = Service.handleHMACEvent;
|
||||
Service.handleHMACEvent = function () {
|
||||
|
|
@ -49,7 +49,7 @@ function shared_setup() {
|
|||
return [engine, rotaryColl, clientsColl, keysWBO, global];
|
||||
}
|
||||
|
||||
add_task(function *hmac_error_during_404() {
|
||||
add_test(function hmac_error_during_404() {
|
||||
_("Attempt to replicate the HMAC error setup.");
|
||||
let [engine, rotaryColl, clientsColl, keysWBO, global] = shared_setup();
|
||||
|
||||
|
|
@ -83,14 +83,13 @@ add_task(function *hmac_error_during_404() {
|
|||
|
||||
try {
|
||||
_("Syncing.");
|
||||
yield sync_and_validate_telem();
|
||||
|
||||
Service.sync();
|
||||
_("Partially resetting client, as if after a restart, and forcing redownload.");
|
||||
Service.collectionKeys.clear();
|
||||
engine.lastSync = 0; // So that we redownload records.
|
||||
key404Counter = 1;
|
||||
_("---------------------------");
|
||||
yield sync_and_validate_telem();
|
||||
Service.sync();
|
||||
_("---------------------------");
|
||||
|
||||
// Two rotary items, one client record... no errors.
|
||||
|
|
@ -98,7 +97,7 @@ add_task(function *hmac_error_during_404() {
|
|||
} finally {
|
||||
Svc.Prefs.resetBranch("");
|
||||
Service.recordManager.clearCache();
|
||||
yield new Promise(resolve => server.stop(resolve));
|
||||
server.stop(run_next_test);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ Cu.import("resource://services-sync/constants.js");
|
|||
Cu.import("resource://services-sync/identity.js");
|
||||
Cu.import("resource://services-sync/util.js");
|
||||
|
||||
var identity = new IdentityManager();
|
||||
let identity = new IdentityManager();
|
||||
|
||||
function run_test() {
|
||||
initTestLogging("Trace");
|
||||
|
|
|
|||
|
|
@ -10,13 +10,8 @@ Cu.import("resource://testing-common/services/sync/utils.js");
|
|||
Svc.DefaultPrefs.set("registerEngines", "");
|
||||
Cu.import("resource://services-sync/service.js");
|
||||
|
||||
var scheduler = Service.scheduler;
|
||||
var clientsEngine = Service.clientsEngine;
|
||||
|
||||
// Don't remove stale clients when syncing. This is a test-only workaround
|
||||
// that lets us add clients directly to the store, without losing them on
|
||||
// the next sync.
|
||||
clientsEngine._removeRemoteClient = id => {};
|
||||
let scheduler = Service.scheduler;
|
||||
let clientsEngine = Service.clientsEngine;
|
||||
|
||||
function promiseStopServer(server) {
|
||||
let deferred = Promise.defer();
|
||||
|
|
@ -46,7 +41,7 @@ function sync_httpd_setup() {
|
|||
});
|
||||
}
|
||||
|
||||
function* setUp(server) {
|
||||
function setUp(server) {
|
||||
yield configureIdentity({username: "johndoe"});
|
||||
Service.serverURL = server.baseURI + "/";
|
||||
Service.clusterURL = server.baseURI + "/";
|
||||
|
|
@ -65,7 +60,7 @@ function run_test() {
|
|||
run_next_test();
|
||||
}
|
||||
|
||||
add_identity_test(this, function* test_successful_sync_adjustSyncInterval() {
|
||||
add_identity_test(this, function test_successful_sync_adjustSyncInterval() {
|
||||
_("Test successful sync calling adjustSyncInterval");
|
||||
let syncSuccesses = 0;
|
||||
function onSyncFinish() {
|
||||
|
|
@ -164,7 +159,7 @@ add_identity_test(this, function* test_successful_sync_adjustSyncInterval() {
|
|||
yield promiseStopServer(server);
|
||||
});
|
||||
|
||||
add_identity_test(this, function* test_unsuccessful_sync_adjustSyncInterval() {
|
||||
add_identity_test(this, function test_unsuccessful_sync_adjustSyncInterval() {
|
||||
_("Test unsuccessful sync calling adjustSyncInterval");
|
||||
|
||||
let syncFailures = 0;
|
||||
|
|
@ -269,7 +264,7 @@ add_identity_test(this, function* test_unsuccessful_sync_adjustSyncInterval() {
|
|||
yield promiseStopServer(server);
|
||||
});
|
||||
|
||||
add_identity_test(this, function* test_back_triggers_sync() {
|
||||
add_identity_test(this, function test_back_triggers_sync() {
|
||||
let server = sync_httpd_setup();
|
||||
yield setUp(server);
|
||||
|
||||
|
|
@ -301,7 +296,7 @@ add_identity_test(this, function* test_back_triggers_sync() {
|
|||
yield deferred.promise;
|
||||
});
|
||||
|
||||
add_identity_test(this, function* test_adjust_interval_on_sync_error() {
|
||||
add_identity_test(this, function test_adjust_interval_on_sync_error() {
|
||||
let server = sync_httpd_setup();
|
||||
yield setUp(server);
|
||||
|
||||
|
|
@ -332,7 +327,7 @@ add_identity_test(this, function* test_adjust_interval_on_sync_error() {
|
|||
yield promiseStopServer(server);
|
||||
});
|
||||
|
||||
add_identity_test(this, function* test_bug671378_scenario() {
|
||||
add_identity_test(this, function test_bug671378_scenario() {
|
||||
// Test scenario similar to bug 671378. This bug appeared when a score
|
||||
// update occurred that wasn't large enough to trigger a sync so
|
||||
// scheduleNextSync() was called without a time interval parameter,
|
||||
|
|
|
|||
|
|
@ -38,8 +38,8 @@ function new_channel() {
|
|||
return cid;
|
||||
}
|
||||
|
||||
var server;
|
||||
var channels = {}; // Map channel -> ServerChannel object
|
||||
let server;
|
||||
let channels = {}; // Map channel -> ServerChannel object
|
||||
function server_new_channel(request, response) {
|
||||
check_headers(request);
|
||||
let cid = new_channel();
|
||||
|
|
@ -48,7 +48,7 @@ function server_new_channel(request, response) {
|
|||
response.bodyOutputStream.write(body, body.length);
|
||||
}
|
||||
|
||||
var error_report;
|
||||
let error_report;
|
||||
function server_report(request, response) {
|
||||
check_headers(request);
|
||||
|
||||
|
|
@ -68,7 +68,7 @@ function server_report(request, response) {
|
|||
}
|
||||
|
||||
// Hook for test code.
|
||||
var hooks = {};
|
||||
let hooks = {};
|
||||
function initHooks() {
|
||||
hooks.onGET = function onGET(request) {};
|
||||
}
|
||||
|
|
@ -146,7 +146,7 @@ ServerChannel.prototype = {
|
|||
/**
|
||||
* Controller that throws for everything.
|
||||
*/
|
||||
var BaseController = {
|
||||
let BaseController = {
|
||||
displayPIN: function displayPIN() {
|
||||
do_throw("displayPIN() shouldn't have been called!");
|
||||
},
|
||||
|
|
@ -369,7 +369,7 @@ add_test(function test_wrongPIN() {
|
|||
displayPIN: function displayPIN(pin) {
|
||||
this.cid = pin.slice(JPAKE_LENGTH_SECRET);
|
||||
let secret = pin.slice(0, JPAKE_LENGTH_SECRET);
|
||||
secret = Array.prototype.slice.call(secret).reverse().join("");
|
||||
secret = [char for each (char in secret)].reverse().join("");
|
||||
let new_pin = secret + this.cid;
|
||||
_("Received PIN " + pin + ", but I'm entering " + new_pin);
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ Cu.import("resource://services-sync/keys.js");
|
|||
Cu.import("resource://services-sync/record.js");
|
||||
Cu.import("resource://services-sync/util.js");
|
||||
|
||||
var collectionKeys = new CollectionKeyManager();
|
||||
let collectionKeys = new CollectionKeyManager();
|
||||
|
||||
function sha256HMAC(message, key) {
|
||||
let h = Utils.makeHMACHasher(Ci.nsICryptoHMAC.SHA256, key);
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ const modules = [
|
|||
"engines/addons.js",
|
||||
"engines/bookmarks.js",
|
||||
"engines/clients.js",
|
||||
"engines/extension-storage.js",
|
||||
"engines/forms.js",
|
||||
"engines/history.js",
|
||||
"engines/passwords.js",
|
||||
|
|
@ -20,6 +19,7 @@ const modules = [
|
|||
"jpakeclient.js",
|
||||
"keys.js",
|
||||
"main.js",
|
||||
"notifications.js",
|
||||
"policies.js",
|
||||
"record.js",
|
||||
"resource.js",
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ function run_test() {
|
|||
Log.repository.getLogger("Sync.Service").level = Log.Level.Trace;
|
||||
Log.repository.getLogger("Sync.SyncScheduler").level = Log.Level.Trace;
|
||||
initTestLogging();
|
||||
validate_all_future_pings();
|
||||
|
||||
ensureLegacyIdentityManager();
|
||||
|
||||
Service.engineManager.register(RotaryEngine);
|
||||
|
|
@ -92,12 +92,11 @@ function prepareServer() {
|
|||
function getReassigned() {
|
||||
try {
|
||||
return Services.prefs.getBoolPref("services.sync.lastSyncReassigned");
|
||||
} catch (ex if (ex.result == Cr.NS_ERROR_UNEXPECTED)) {
|
||||
return false;
|
||||
} catch (ex) {
|
||||
if (ex.result == Cr.NS_ERROR_UNEXPECTED) {
|
||||
return false;
|
||||
}
|
||||
do_throw("Got exception retrieving lastSyncReassigned: " +
|
||||
Log.exceptionStr(ex));
|
||||
Utils.exceptionStr(ex));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -107,7 +106,7 @@ function getReassigned() {
|
|||
* Runs `between` between the two. This can be used to undo deliberate failure
|
||||
* setup, detach observers, etc.
|
||||
*/
|
||||
function* syncAndExpectNodeReassignment(server, firstNotification, between,
|
||||
function syncAndExpectNodeReassignment(server, firstNotification, between,
|
||||
secondNotification, url) {
|
||||
let deferred = Promise.defer();
|
||||
function onwards() {
|
||||
|
|
@ -161,7 +160,7 @@ function* syncAndExpectNodeReassignment(server, firstNotification, between,
|
|||
yield deferred.promise;
|
||||
}
|
||||
|
||||
add_task(function* test_momentary_401_engine() {
|
||||
add_task(function test_momentary_401_engine() {
|
||||
_("Test a failure for engine URLs that's resolved by reassignment.");
|
||||
let server = yield prepareServer();
|
||||
let john = server.user("johndoe");
|
||||
|
|
@ -213,7 +212,7 @@ add_task(function* test_momentary_401_engine() {
|
|||
});
|
||||
|
||||
// This test ends up being a failing fetch *after we're already logged in*.
|
||||
add_task(function* test_momentary_401_info_collections() {
|
||||
add_task(function test_momentary_401_info_collections() {
|
||||
_("Test a failure for info/collections that's resolved by reassignment.");
|
||||
let server = yield prepareServer();
|
||||
|
||||
|
|
@ -236,7 +235,7 @@ add_task(function* test_momentary_401_info_collections() {
|
|||
Service.infoURL);
|
||||
});
|
||||
|
||||
add_task(function* test_momentary_401_storage_loggedin() {
|
||||
add_task(function test_momentary_401_storage_loggedin() {
|
||||
_("Test a failure for any storage URL, not just engine parts. " +
|
||||
"Resolved by reassignment.");
|
||||
let server = yield prepareServer();
|
||||
|
|
@ -261,7 +260,7 @@ add_task(function* test_momentary_401_storage_loggedin() {
|
|||
Service.storageURL + "meta/global");
|
||||
});
|
||||
|
||||
add_task(function* test_momentary_401_storage_loggedout() {
|
||||
add_task(function test_momentary_401_storage_loggedout() {
|
||||
_("Test a failure for any storage URL, not just engine parts. " +
|
||||
"Resolved by reassignment.");
|
||||
let server = yield prepareServer();
|
||||
|
|
@ -283,7 +282,7 @@ add_task(function* test_momentary_401_storage_loggedout() {
|
|||
Service.storageURL + "meta/global");
|
||||
});
|
||||
|
||||
add_task(function* test_loop_avoidance_storage() {
|
||||
add_task(function test_loop_avoidance_storage() {
|
||||
_("Test that a repeated failure doesn't result in a sync loop " +
|
||||
"if node reassignment cannot resolve the failure.");
|
||||
|
||||
|
|
@ -383,7 +382,7 @@ add_task(function* test_loop_avoidance_storage() {
|
|||
yield deferred.promise;
|
||||
});
|
||||
|
||||
add_task(function* test_loop_avoidance_engine() {
|
||||
add_task(function test_loop_avoidance_engine() {
|
||||
_("Test that a repeated 401 in an engine doesn't result in a sync loop " +
|
||||
"if node reassignment cannot resolve the failure.");
|
||||
let server = yield prepareServer();
|
||||
|
|
|
|||
32
services/sync/tests/unit/test_notifications.js
Normal file
32
services/sync/tests/unit/test_notifications.js
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
Cu.import("resource://services-sync/notifications.js");
|
||||
|
||||
function run_test() {
|
||||
var logStats = initTestLogging("Info");
|
||||
|
||||
var blah = 0;
|
||||
|
||||
function callback(i) {
|
||||
blah = i;
|
||||
}
|
||||
|
||||
let button = new NotificationButton("label", "accessKey", callback);
|
||||
|
||||
button.callback(5);
|
||||
|
||||
do_check_eq(blah, 5);
|
||||
do_check_eq(logStats.errorsLogged, 0);
|
||||
|
||||
function badCallback() {
|
||||
throw new Error("oops");
|
||||
}
|
||||
|
||||
button = new NotificationButton("label", "accessKey", badCallback);
|
||||
|
||||
try {
|
||||
button.callback();
|
||||
} catch (e) {
|
||||
do_check_eq(e.message, "oops");
|
||||
}
|
||||
|
||||
do_check_eq(logStats.errorsLogged, 1);
|
||||
}
|
||||
|
|
@ -5,137 +5,6 @@ Cu.import("resource://services-sync/engines/passwords.js");
|
|||
Cu.import("resource://services-sync/service.js");
|
||||
Cu.import("resource://services-sync/util.js");
|
||||
|
||||
|
||||
function checkRecord(name, record, expectedCount, timeCreated,
|
||||
expectedTimeCreated, timePasswordChanged,
|
||||
expectedTimePasswordChanged, recordIsUpdated) {
|
||||
let engine = Service.engineManager.get("passwords");
|
||||
let store = engine._store;
|
||||
|
||||
let count = {};
|
||||
let logins = Services.logins.findLogins(count, record.hostname,
|
||||
record.formSubmitURL, null);
|
||||
|
||||
_("Record" + name + ":" + JSON.stringify(logins));
|
||||
_("Count" + name + ":" + count.value);
|
||||
|
||||
do_check_eq(count.value, expectedCount);
|
||||
|
||||
if (expectedCount > 0) {
|
||||
do_check_true(!!store.getAllIDs()[record.id]);
|
||||
let stored_record = logins[0].QueryInterface(Ci.nsILoginMetaInfo);
|
||||
|
||||
if (timeCreated !== undefined) {
|
||||
do_check_eq(stored_record.timeCreated, expectedTimeCreated);
|
||||
}
|
||||
|
||||
if (timePasswordChanged !== undefined) {
|
||||
if (recordIsUpdated) {
|
||||
do_check_true(stored_record.timePasswordChanged >= expectedTimePasswordChanged);
|
||||
} else {
|
||||
do_check_eq(stored_record.timePasswordChanged, expectedTimePasswordChanged);
|
||||
}
|
||||
return stored_record.timePasswordChanged;
|
||||
}
|
||||
} else {
|
||||
do_check_true(!store.getAllIDs()[record.id]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function changePassword(name, hostname, password, expectedCount, timeCreated,
|
||||
expectedTimeCreated, timePasswordChanged,
|
||||
expectedTimePasswordChanged, insert, recordIsUpdated) {
|
||||
|
||||
const BOGUS_GUID = "zzzzzz" + hostname;
|
||||
|
||||
let record = {id: BOGUS_GUID,
|
||||
hostname: hostname,
|
||||
formSubmitURL: hostname,
|
||||
username: "john",
|
||||
password: password,
|
||||
usernameField: "username",
|
||||
passwordField: "password"};
|
||||
|
||||
if (timeCreated !== undefined) {
|
||||
record.timeCreated = timeCreated;
|
||||
}
|
||||
|
||||
if (timePasswordChanged !== undefined) {
|
||||
record.timePasswordChanged = timePasswordChanged;
|
||||
}
|
||||
|
||||
|
||||
let engine = Service.engineManager.get("passwords");
|
||||
let store = engine._store;
|
||||
|
||||
if (insert) {
|
||||
do_check_eq(store.applyIncomingBatch([record]).length, 0);
|
||||
}
|
||||
|
||||
return checkRecord(name, record, expectedCount, timeCreated,
|
||||
expectedTimeCreated, timePasswordChanged,
|
||||
expectedTimePasswordChanged, recordIsUpdated);
|
||||
|
||||
}
|
||||
|
||||
|
||||
function test_apply_records_with_times(hostname, timeCreated, timePasswordChanged) {
|
||||
// The following record is going to be inserted in the store and it needs
|
||||
// to be found there. Then its timestamps are going to be compared to
|
||||
// the expected values.
|
||||
changePassword(" ", hostname, "password", 1, timeCreated, timeCreated,
|
||||
timePasswordChanged, timePasswordChanged, true);
|
||||
}
|
||||
|
||||
|
||||
function test_apply_multiple_records_with_times() {
|
||||
// The following records are going to be inserted in the store and they need
|
||||
// to be found there. Then their timestamps are going to be compared to
|
||||
// the expected values.
|
||||
changePassword("A", "http://foo.a.com", "password", 1, undefined, undefined,
|
||||
undefined, undefined, true);
|
||||
changePassword("B", "http://foo.b.com", "password", 1, 1000, 1000, undefined,
|
||||
undefined, true);
|
||||
changePassword("C", "http://foo.c.com", "password", 1, undefined, undefined,
|
||||
1000, 1000, true);
|
||||
changePassword("D", "http://foo.d.com", "password", 1, 1000, 1000, 1000,
|
||||
1000, true);
|
||||
|
||||
// The following records are not going to be inserted in the store and they
|
||||
// are not going to be found there.
|
||||
changePassword("NotInStoreA", "http://foo.aaaa.com", "password", 0,
|
||||
undefined, undefined, undefined, undefined, false);
|
||||
changePassword("NotInStoreB", "http://foo.bbbb.com", "password", 0, 1000,
|
||||
1000, undefined, undefined, false);
|
||||
changePassword("NotInStoreC", "http://foo.cccc.com", "password", 0,
|
||||
undefined, undefined, 1000, 1000, false);
|
||||
changePassword("NotInStoreD", "http://foo.dddd.com", "password", 0, 1000,
|
||||
1000, 1000, 1000, false);
|
||||
}
|
||||
|
||||
|
||||
function test_apply_same_record_with_different_times() {
|
||||
// The following record is going to be inserted multiple times in the store
|
||||
// and it needs to be found there. Then its timestamps are going to be
|
||||
// compared to the expected values.
|
||||
var timePasswordChanged = 100;
|
||||
timePasswordChanged = changePassword("A", "http://a.tn", "password", 1, 100,
|
||||
100, 100, timePasswordChanged, true);
|
||||
timePasswordChanged = changePassword("A", "http://a.tn", "password", 1, 100,
|
||||
100, 800, timePasswordChanged, true,
|
||||
true);
|
||||
timePasswordChanged = changePassword("A", "http://a.tn", "password", 1, 500,
|
||||
100, 800, timePasswordChanged, true,
|
||||
true);
|
||||
timePasswordChanged = changePassword("A", "http://a.tn", "password2", 1, 500,
|
||||
100, 1536213005222, timePasswordChanged,
|
||||
true, true);
|
||||
timePasswordChanged = changePassword("A", "http://a.tn", "password2", 1, 500,
|
||||
100, 800, timePasswordChanged, true, true);
|
||||
}
|
||||
|
||||
|
||||
function run_test() {
|
||||
initTestLogging("Trace");
|
||||
Log.repository.getLogger("Sync.Engine.Passwords").level = Log.Level.Trace;
|
||||
|
|
@ -161,9 +30,12 @@ function run_test() {
|
|||
|
||||
let engine = Service.engineManager.get("passwords");
|
||||
let store = engine._store;
|
||||
function applyEnsureNoFailures(records) {
|
||||
do_check_eq(store.applyIncomingBatch(records).length, 0);
|
||||
}
|
||||
|
||||
try {
|
||||
do_check_eq(store.applyIncomingBatch([recordA, recordB]).length, 0);
|
||||
applyEnsureNoFailures([recordA, recordB]);
|
||||
|
||||
// Only the good record makes it to Services.logins.
|
||||
let badCount = {};
|
||||
|
|
@ -183,17 +55,7 @@ function run_test() {
|
|||
|
||||
do_check_true(!!store.getAllIDs()[BOGUS_GUID_B]);
|
||||
do_check_true(!store.getAllIDs()[BOGUS_GUID_A]);
|
||||
|
||||
test_apply_records_with_times("http://afoo.baz.com", undefined, undefined);
|
||||
test_apply_records_with_times("http://bfoo.baz.com", 1000, undefined);
|
||||
test_apply_records_with_times("http://cfoo.baz.com", undefined, 2000);
|
||||
test_apply_records_with_times("http://dfoo.baz.com", 1000, 2000);
|
||||
|
||||
test_apply_multiple_records_with_times();
|
||||
|
||||
test_apply_same_record_with_different_times();
|
||||
|
||||
} finally {
|
||||
store.wipe();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,9 +8,9 @@ Cu.import("resource://services-sync/service.js");
|
|||
Cu.import("resource://services-sync/util.js");
|
||||
|
||||
Service.engineManager.register(PasswordEngine);
|
||||
var engine = Service.engineManager.get("passwords");
|
||||
var store = engine._store;
|
||||
var tracker = engine._tracker;
|
||||
let engine = Service.engineManager.get("passwords");
|
||||
let store = engine._store;
|
||||
let tracker = engine._tracker;
|
||||
|
||||
// Don't do asynchronous writes.
|
||||
tracker.persistChangedIDs = false;
|
||||
|
|
|
|||
|
|
@ -1,158 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
Components.utils.import("resource://services-sync/engines/passwords.js");
|
||||
|
||||
function getDummyServerAndClient() {
|
||||
return {
|
||||
server: [
|
||||
{
|
||||
id: "11111",
|
||||
guid: "11111",
|
||||
hostname: "https://www.11111.com",
|
||||
formSubmitURL: "https://www.11111.com/login",
|
||||
password: "qwerty123",
|
||||
passwordField: "pass",
|
||||
username: "foobar",
|
||||
usernameField: "user",
|
||||
httpRealm: null,
|
||||
},
|
||||
{
|
||||
id: "22222",
|
||||
guid: "22222",
|
||||
hostname: "https://www.22222.org",
|
||||
formSubmitURL: "https://www.22222.org/login",
|
||||
password: "hunter2",
|
||||
passwordField: "passwd",
|
||||
username: "baz12345",
|
||||
usernameField: "user",
|
||||
httpRealm: null,
|
||||
},
|
||||
{
|
||||
id: "33333",
|
||||
guid: "33333",
|
||||
hostname: "https://www.33333.com",
|
||||
formSubmitURL: "https://www.33333.com/login",
|
||||
password: "p4ssw0rd",
|
||||
passwordField: "passwad",
|
||||
username: "quux",
|
||||
usernameField: "user",
|
||||
httpRealm: null,
|
||||
},
|
||||
],
|
||||
client: [
|
||||
{
|
||||
id: "11111",
|
||||
guid: "11111",
|
||||
hostname: "https://www.11111.com",
|
||||
formSubmitURL: "https://www.11111.com/login",
|
||||
password: "qwerty123",
|
||||
passwordField: "pass",
|
||||
username: "foobar",
|
||||
usernameField: "user",
|
||||
httpRealm: null,
|
||||
},
|
||||
{
|
||||
id: "22222",
|
||||
guid: "22222",
|
||||
hostname: "https://www.22222.org",
|
||||
formSubmitURL: "https://www.22222.org/login",
|
||||
password: "hunter2",
|
||||
passwordField: "passwd",
|
||||
username: "baz12345",
|
||||
usernameField: "user",
|
||||
httpRealm: null,
|
||||
|
||||
},
|
||||
{
|
||||
id: "33333",
|
||||
guid: "33333",
|
||||
hostname: "https://www.33333.com",
|
||||
formSubmitURL: "https://www.33333.com/login",
|
||||
password: "p4ssw0rd",
|
||||
passwordField: "passwad",
|
||||
username: "quux",
|
||||
usernameField: "user",
|
||||
httpRealm: null,
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
add_test(function test_valid() {
|
||||
let { server, client } = getDummyServerAndClient();
|
||||
let validator = new PasswordValidator();
|
||||
let { problemData, clientRecords, records, deletedRecords } =
|
||||
validator.compareClientWithServer(client, server);
|
||||
equal(clientRecords.length, 3);
|
||||
equal(records.length, 3)
|
||||
equal(deletedRecords.length, 0);
|
||||
deepEqual(problemData, validator.emptyProblemData());
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_missing() {
|
||||
let validator = new PasswordValidator();
|
||||
{
|
||||
let { server, client } = getDummyServerAndClient();
|
||||
|
||||
client.pop();
|
||||
|
||||
let { problemData, clientRecords, records, deletedRecords } =
|
||||
validator.compareClientWithServer(client, server);
|
||||
|
||||
equal(clientRecords.length, 2);
|
||||
equal(records.length, 3)
|
||||
equal(deletedRecords.length, 0);
|
||||
|
||||
let expected = validator.emptyProblemData();
|
||||
expected.clientMissing.push("33333");
|
||||
deepEqual(problemData, expected);
|
||||
}
|
||||
{
|
||||
let { server, client } = getDummyServerAndClient();
|
||||
|
||||
server.pop();
|
||||
|
||||
let { problemData, clientRecords, records, deletedRecords } =
|
||||
validator.compareClientWithServer(client, server);
|
||||
|
||||
equal(clientRecords.length, 3);
|
||||
equal(records.length, 2)
|
||||
equal(deletedRecords.length, 0);
|
||||
|
||||
let expected = validator.emptyProblemData();
|
||||
expected.serverMissing.push("33333");
|
||||
deepEqual(problemData, expected);
|
||||
}
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
|
||||
add_test(function test_deleted() {
|
||||
let { server, client } = getDummyServerAndClient();
|
||||
let deletionRecord = { id: "444444", guid: "444444", deleted: true };
|
||||
|
||||
server.push(deletionRecord);
|
||||
let validator = new PasswordValidator();
|
||||
|
||||
let { problemData, clientRecords, records, deletedRecords } =
|
||||
validator.compareClientWithServer(client, server);
|
||||
|
||||
equal(clientRecords.length, 3);
|
||||
equal(records.length, 4);
|
||||
deepEqual(deletedRecords, [deletionRecord]);
|
||||
|
||||
let expected = validator.emptyProblemData();
|
||||
deepEqual(problemData, expected);
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
|
@ -1,455 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
let { PostQueue } = Cu.import("resource://services-sync/record.js", {});
|
||||
|
||||
initTestLogging("Trace");
|
||||
|
||||
function makeRecord(nbytes) {
|
||||
// make a string 2-bytes less - the added quotes will make it correct.
|
||||
return {
|
||||
toJSON: () => "x".repeat(nbytes-2),
|
||||
}
|
||||
}
|
||||
|
||||
function makePostQueue(config, lastModTime, responseGenerator) {
|
||||
let stats = {
|
||||
posts: [],
|
||||
}
|
||||
let poster = (data, headers, batch, commit) => {
|
||||
let thisPost = { nbytes: data.length, batch, commit };
|
||||
if (headers.length) {
|
||||
thisPost.headers = headers;
|
||||
}
|
||||
stats.posts.push(thisPost);
|
||||
return responseGenerator.next().value;
|
||||
}
|
||||
|
||||
let done = () => {}
|
||||
let pq = new PostQueue(poster, lastModTime, config, getTestLogger(), done);
|
||||
return { pq, stats };
|
||||
}
|
||||
|
||||
add_test(function test_simple() {
|
||||
let config = {
|
||||
max_post_bytes: 1000,
|
||||
max_post_records: 100,
|
||||
max_batch_bytes: Infinity,
|
||||
max_batch_records: Infinity,
|
||||
}
|
||||
|
||||
const time = 11111111;
|
||||
|
||||
function* responseGenerator() {
|
||||
yield { success: true, status: 200, headers: { 'x-weave-timestamp': time + 100, 'x-last-modified': time + 100 } };
|
||||
}
|
||||
|
||||
let { pq, stats } = makePostQueue(config, time, responseGenerator());
|
||||
pq.enqueue(makeRecord(10));
|
||||
pq.flush(true);
|
||||
|
||||
deepEqual(stats.posts, [{
|
||||
nbytes: 12, // expect our 10 byte record plus "[]" to wrap it.
|
||||
commit: true, // we don't know if we have batch semantics, so committed.
|
||||
headers: [["x-if-unmodified-since", time]],
|
||||
batch: "true"}]);
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
// Test we do the right thing when we need to make multiple posts when there
|
||||
// are no batch semantics
|
||||
add_test(function test_max_post_bytes_no_batch() {
|
||||
let config = {
|
||||
max_post_bytes: 50,
|
||||
max_post_records: 4,
|
||||
max_batch_bytes: Infinity,
|
||||
max_batch_records: Infinity,
|
||||
}
|
||||
|
||||
const time = 11111111;
|
||||
function* responseGenerator() {
|
||||
yield { success: true, status: 200, headers: { 'x-weave-timestamp': time + 100, 'x-last-modified': time + 100 } };
|
||||
yield { success: true, status: 200, headers: { 'x-weave-timestamp': time + 200, 'x-last-modified': time + 200 } };
|
||||
}
|
||||
|
||||
let { pq, stats } = makePostQueue(config, time, responseGenerator());
|
||||
pq.enqueue(makeRecord(20)); // total size now 22 bytes - "[" + record + "]"
|
||||
pq.enqueue(makeRecord(20)); // total size now 43 bytes - "[" + record + "," + record + "]"
|
||||
pq.enqueue(makeRecord(20)); // this will exceed our byte limit, so will be in the 2nd POST.
|
||||
pq.flush(true);
|
||||
|
||||
deepEqual(stats.posts, [
|
||||
{
|
||||
nbytes: 43, // 43 for the first post
|
||||
commit: false,
|
||||
headers: [["x-if-unmodified-since", time]],
|
||||
batch: "true",
|
||||
},{
|
||||
nbytes: 22,
|
||||
commit: false, // we know we aren't in a batch, so never commit.
|
||||
headers: [["x-if-unmodified-since", time + 100]],
|
||||
batch: null,
|
||||
}
|
||||
]);
|
||||
equal(pq.lastModified, time + 200);
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
// Similar to the above, but we've hit max_records instead of max_bytes.
|
||||
add_test(function test_max_post_records_no_batch() {
|
||||
let config = {
|
||||
max_post_bytes: 100,
|
||||
max_post_records: 2,
|
||||
max_batch_bytes: Infinity,
|
||||
max_batch_records: Infinity,
|
||||
}
|
||||
|
||||
const time = 11111111;
|
||||
|
||||
function* responseGenerator() {
|
||||
yield { success: true, status: 200, headers: { 'x-weave-timestamp': time + 100, 'x-last-modified': time + 100 } };
|
||||
yield { success: true, status: 200, headers: { 'x-weave-timestamp': time + 200, 'x-last-modified': time + 200 } };
|
||||
}
|
||||
|
||||
let { pq, stats } = makePostQueue(config, time, responseGenerator());
|
||||
pq.enqueue(makeRecord(20)); // total size now 22 bytes - "[" + record + "]"
|
||||
pq.enqueue(makeRecord(20)); // total size now 43 bytes - "[" + record + "," + record + "]"
|
||||
pq.enqueue(makeRecord(20)); // this will exceed our records limit, so will be in the 2nd POST.
|
||||
pq.flush(true);
|
||||
|
||||
deepEqual(stats.posts, [
|
||||
{
|
||||
nbytes: 43, // 43 for the first post
|
||||
commit: false,
|
||||
batch: "true",
|
||||
headers: [["x-if-unmodified-since", time]],
|
||||
},{
|
||||
nbytes: 22,
|
||||
commit: false, // we know we aren't in a batch, so never commit.
|
||||
batch: null,
|
||||
headers: [["x-if-unmodified-since", time + 100]],
|
||||
}
|
||||
]);
|
||||
equal(pq.lastModified, time + 200);
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
// Batch tests.
|
||||
|
||||
// Test making a single post when batch semantics are in place.
|
||||
add_test(function test_single_batch() {
|
||||
let config = {
|
||||
max_post_bytes: 1000,
|
||||
max_post_records: 100,
|
||||
max_batch_bytes: 2000,
|
||||
max_batch_records: 200,
|
||||
}
|
||||
const time = 11111111;
|
||||
function* responseGenerator() {
|
||||
yield { success: true, status: 202, obj: { batch: 1234 },
|
||||
headers: { 'x-last-modified': time, 'x-weave-timestamp': time + 100 },
|
||||
};
|
||||
}
|
||||
|
||||
let { pq, stats } = makePostQueue(config, time, responseGenerator());
|
||||
ok(pq.enqueue(makeRecord(10)).enqueued);
|
||||
pq.flush(true);
|
||||
|
||||
deepEqual(stats.posts, [
|
||||
{
|
||||
nbytes: 12, // expect our 10 byte record plus "[]" to wrap it.
|
||||
commit: true, // we don't know if we have batch semantics, so committed.
|
||||
batch: "true",
|
||||
headers: [["x-if-unmodified-since", time]],
|
||||
}
|
||||
]);
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
// Test we do the right thing when we need to make multiple posts when there
|
||||
// are batch semantics in place.
|
||||
add_test(function test_max_post_bytes_batch() {
|
||||
let config = {
|
||||
max_post_bytes: 50,
|
||||
max_post_records: 4,
|
||||
max_batch_bytes: 5000,
|
||||
max_batch_records: 100,
|
||||
}
|
||||
|
||||
const time = 11111111;
|
||||
function* responseGenerator() {
|
||||
yield { success: true, status: 202, obj: { batch: 1234 },
|
||||
headers: { 'x-last-modified': time, 'x-weave-timestamp': time + 100 },
|
||||
};
|
||||
yield { success: true, status: 202, obj: { batch: 1234 },
|
||||
headers: { 'x-last-modified': time + 200, 'x-weave-timestamp': time + 200 },
|
||||
};
|
||||
}
|
||||
|
||||
let { pq, stats } = makePostQueue(config, time, responseGenerator());
|
||||
ok(pq.enqueue(makeRecord(20)).enqueued); // total size now 22 bytes - "[" + record + "]"
|
||||
ok(pq.enqueue(makeRecord(20)).enqueued); // total size now 43 bytes - "[" + record + "," + record + "]"
|
||||
ok(pq.enqueue(makeRecord(20)).enqueued); // this will exceed our byte limit, so will be in the 2nd POST.
|
||||
pq.flush(true);
|
||||
|
||||
deepEqual(stats.posts, [
|
||||
{
|
||||
nbytes: 43, // 43 for the first post
|
||||
commit: false,
|
||||
batch: "true",
|
||||
headers: [['x-if-unmodified-since', time]],
|
||||
},{
|
||||
nbytes: 22,
|
||||
commit: true,
|
||||
batch: 1234,
|
||||
headers: [['x-if-unmodified-since', time]],
|
||||
}
|
||||
]);
|
||||
|
||||
equal(pq.lastModified, time + 200);
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
// Test we do the right thing when the batch bytes limit is exceeded.
|
||||
add_test(function test_max_post_bytes_batch() {
|
||||
let config = {
|
||||
max_post_bytes: 50,
|
||||
max_post_records: 20,
|
||||
max_batch_bytes: 70,
|
||||
max_batch_records: 100,
|
||||
}
|
||||
|
||||
const time0 = 11111111;
|
||||
const time1 = 22222222;
|
||||
function* responseGenerator() {
|
||||
yield { success: true, status: 202, obj: { batch: 1234 },
|
||||
headers: { 'x-last-modified': time0, 'x-weave-timestamp': time0 + 100 },
|
||||
};
|
||||
yield { success: true, status: 202, obj: { batch: 1234 },
|
||||
headers: { 'x-last-modified': time1, 'x-weave-timestamp': time1 },
|
||||
};
|
||||
yield { success: true, status: 202, obj: { batch: 5678 },
|
||||
headers: { 'x-last-modified': time1, 'x-weave-timestamp': time1 + 100 },
|
||||
};
|
||||
yield { success: true, status: 202, obj: { batch: 5678 },
|
||||
headers: { 'x-last-modified': time1 + 200, 'x-weave-timestamp': time1 + 200 },
|
||||
};
|
||||
}
|
||||
|
||||
let { pq, stats } = makePostQueue(config, time0, responseGenerator());
|
||||
ok(pq.enqueue(makeRecord(20)).enqueued); // total size now 22 bytes - "[" + record + "]"
|
||||
ok(pq.enqueue(makeRecord(20)).enqueued); // total size now 43 bytes - "[" + record + "," + record + "]"
|
||||
// this will exceed our POST byte limit, so will be in the 2nd POST - but still in the first batch.
|
||||
ok(pq.enqueue(makeRecord(20)).enqueued); // 22 bytes for 2nd post, 55 bytes in the batch.
|
||||
// this will exceed our batch byte limit, so will be in a new batch.
|
||||
ok(pq.enqueue(makeRecord(20)).enqueued); // 22 bytes in 3rd post/2nd batch
|
||||
ok(pq.enqueue(makeRecord(20)).enqueued); // 43 bytes in 3rd post/2nd batch
|
||||
// This will exceed POST byte limit, so will be in the 4th post, part of the 2nd batch.
|
||||
ok(pq.enqueue(makeRecord(20)).enqueued); // 22 bytes for 4th post/2nd batch
|
||||
pq.flush(true);
|
||||
|
||||
deepEqual(stats.posts, [
|
||||
{
|
||||
nbytes: 43, // 43 for the first post
|
||||
commit: false,
|
||||
batch: "true",
|
||||
headers: [['x-if-unmodified-since', time0]],
|
||||
},{
|
||||
// second post of 22 bytes in the first batch, committing it.
|
||||
nbytes: 22,
|
||||
commit: true,
|
||||
batch: 1234,
|
||||
headers: [['x-if-unmodified-since', time0]],
|
||||
}, {
|
||||
// 3rd post of 43 bytes in a new batch, not yet committing it.
|
||||
nbytes: 43,
|
||||
commit: false,
|
||||
batch: "true",
|
||||
headers: [['x-if-unmodified-since', time1]],
|
||||
},{
|
||||
// 4th post of 22 bytes in second batch, committing it.
|
||||
nbytes: 22,
|
||||
commit: true,
|
||||
batch: 5678,
|
||||
headers: [['x-if-unmodified-since', time1]],
|
||||
},
|
||||
]);
|
||||
|
||||
equal(pq.lastModified, time1 + 200);
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
// Test we split up the posts when we exceed the record limit when batch semantics
|
||||
// are in place.
|
||||
add_test(function test_max_post_bytes_batch() {
|
||||
let config = {
|
||||
max_post_bytes: 1000,
|
||||
max_post_records: 2,
|
||||
max_batch_bytes: 5000,
|
||||
max_batch_records: 100,
|
||||
}
|
||||
|
||||
const time = 11111111;
|
||||
function* responseGenerator() {
|
||||
yield { success: true, status: 202, obj: { batch: 1234 },
|
||||
headers: { 'x-last-modified': time, 'x-weave-timestamp': time + 100 },
|
||||
};
|
||||
yield { success: true, status: 202, obj: { batch: 1234 },
|
||||
headers: { 'x-last-modified': time + 200, 'x-weave-timestamp': time + 200 },
|
||||
};
|
||||
}
|
||||
|
||||
let { pq, stats } = makePostQueue(config, time, responseGenerator());
|
||||
ok(pq.enqueue(makeRecord(20)).enqueued); // total size now 22 bytes - "[" + record + "]"
|
||||
ok(pq.enqueue(makeRecord(20)).enqueued); // total size now 43 bytes - "[" + record + "," + record + "]"
|
||||
ok(pq.enqueue(makeRecord(20)).enqueued); // will exceed record limit, so will be in 2nd post.
|
||||
pq.flush(true);
|
||||
|
||||
deepEqual(stats.posts, [
|
||||
{
|
||||
nbytes: 43, // 43 for the first post
|
||||
commit: false,
|
||||
batch: "true",
|
||||
headers: [['x-if-unmodified-since', time]],
|
||||
},{
|
||||
nbytes: 22,
|
||||
commit: true,
|
||||
batch: 1234,
|
||||
headers: [['x-if-unmodified-since', time]],
|
||||
}
|
||||
]);
|
||||
|
||||
equal(pq.lastModified, time + 200);
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
// Test that a single huge record fails to enqueue
|
||||
add_test(function test_huge_record() {
|
||||
let config = {
|
||||
max_post_bytes: 50,
|
||||
max_post_records: 100,
|
||||
max_batch_bytes: 5000,
|
||||
max_batch_records: 100,
|
||||
}
|
||||
|
||||
const time = 11111111;
|
||||
function* responseGenerator() {
|
||||
yield { success: true, status: 202, obj: { batch: 1234 },
|
||||
headers: { 'x-last-modified': time, 'x-weave-timestamp': time + 100 },
|
||||
};
|
||||
yield { success: true, status: 202, obj: { batch: 1234 },
|
||||
headers: { 'x-last-modified': time + 200, 'x-weave-timestamp': time + 200 },
|
||||
};
|
||||
}
|
||||
|
||||
let { pq, stats } = makePostQueue(config, time, responseGenerator());
|
||||
ok(pq.enqueue(makeRecord(20)).enqueued);
|
||||
|
||||
let { enqueued, error } = pq.enqueue(makeRecord(1000));
|
||||
ok(!enqueued);
|
||||
notEqual(error, undefined);
|
||||
|
||||
// make sure that we keep working, skipping the bad record entirely
|
||||
// (handling the error the queue reported is left up to caller)
|
||||
ok(pq.enqueue(makeRecord(20)).enqueued);
|
||||
ok(pq.enqueue(makeRecord(20)).enqueued);
|
||||
|
||||
pq.flush(true);
|
||||
|
||||
deepEqual(stats.posts, [
|
||||
{
|
||||
nbytes: 43, // 43 for the first post
|
||||
commit: false,
|
||||
batch: "true",
|
||||
headers: [['x-if-unmodified-since', time]],
|
||||
},{
|
||||
nbytes: 22,
|
||||
commit: true,
|
||||
batch: 1234,
|
||||
headers: [['x-if-unmodified-since', time]],
|
||||
}
|
||||
]);
|
||||
|
||||
equal(pq.lastModified, time + 200);
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
// Test we do the right thing when the batch record limit is exceeded.
|
||||
add_test(function test_max_records_batch() {
|
||||
let config = {
|
||||
max_post_bytes: 1000,
|
||||
max_post_records: 3,
|
||||
max_batch_bytes: 10000,
|
||||
max_batch_records: 5,
|
||||
}
|
||||
|
||||
const time0 = 11111111;
|
||||
const time1 = 22222222;
|
||||
function* responseGenerator() {
|
||||
yield { success: true, status: 202, obj: { batch: 1234 },
|
||||
headers: { 'x-last-modified': time0, 'x-weave-timestamp': time0 + 100 },
|
||||
};
|
||||
yield { success: true, status: 202, obj: { batch: 1234 },
|
||||
headers: { 'x-last-modified': time1, 'x-weave-timestamp': time1 },
|
||||
};
|
||||
yield { success: true, status: 202, obj: { batch: 5678 },
|
||||
headers: { 'x-last-modified': time1, 'x-weave-timestamp': time1 + 100 },
|
||||
};
|
||||
yield { success: true, status: 202, obj: { batch: 5678 },
|
||||
headers: { 'x-last-modified': time1 + 200, 'x-weave-timestamp': time1 + 200 },
|
||||
};
|
||||
}
|
||||
|
||||
let { pq, stats } = makePostQueue(config, time0, responseGenerator());
|
||||
|
||||
ok(pq.enqueue(makeRecord(20)).enqueued);
|
||||
ok(pq.enqueue(makeRecord(20)).enqueued);
|
||||
ok(pq.enqueue(makeRecord(20)).enqueued);
|
||||
|
||||
ok(pq.enqueue(makeRecord(20)).enqueued);
|
||||
ok(pq.enqueue(makeRecord(20)).enqueued);
|
||||
|
||||
ok(pq.enqueue(makeRecord(20)).enqueued);
|
||||
ok(pq.enqueue(makeRecord(20)).enqueued);
|
||||
ok(pq.enqueue(makeRecord(20)).enqueued);
|
||||
|
||||
ok(pq.enqueue(makeRecord(20)).enqueued);
|
||||
|
||||
pq.flush(true);
|
||||
|
||||
deepEqual(stats.posts, [
|
||||
{ // 3 records
|
||||
nbytes: 64,
|
||||
commit: false,
|
||||
batch: "true",
|
||||
headers: [['x-if-unmodified-since', time0]],
|
||||
},{ // 2 records -- end batch1
|
||||
nbytes: 43,
|
||||
commit: true,
|
||||
batch: 1234,
|
||||
headers: [['x-if-unmodified-since', time0]],
|
||||
}, { // 3 records
|
||||
nbytes: 64,
|
||||
commit: false,
|
||||
batch: "true",
|
||||
headers: [['x-if-unmodified-since', time1]],
|
||||
},{ // 1 record -- end batch2
|
||||
nbytes: 22,
|
||||
commit: true,
|
||||
batch: 5678,
|
||||
headers: [['x-if-unmodified-since', time1]],
|
||||
},
|
||||
]);
|
||||
|
||||
equal(pq.lastModified, time1 + 200);
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
|
@ -23,22 +23,25 @@ function makePersona(id) {
|
|||
}
|
||||
|
||||
function run_test() {
|
||||
_("Test fixtures.");
|
||||
// read our custom prefs file before doing anything.
|
||||
Services.prefs.readUserPrefs(do_get_file("prefs_test_prefs_store.js"));
|
||||
// Now we've read from this file, any writes the pref service makes will be
|
||||
// back to this prefs_test_prefs_store.js directly in the obj dir. This
|
||||
// upsets things in confusing ways :) We avoid this by explicitly telling the
|
||||
// pref service to use a file in our profile dir.
|
||||
let prefFile = do_get_profile();
|
||||
prefFile.append("prefs.js");
|
||||
Services.prefs.savePrefFile(prefFile);
|
||||
Services.prefs.readUserPrefs(prefFile);
|
||||
|
||||
let store = Service.engineManager.get("prefs")._store;
|
||||
let prefs = new Preferences();
|
||||
try {
|
||||
|
||||
_("Test fixtures.");
|
||||
Svc.Prefs.set("prefs.sync.testing.int", true);
|
||||
Svc.Prefs.set("prefs.sync.testing.string", true);
|
||||
Svc.Prefs.set("prefs.sync.testing.bool", true);
|
||||
Svc.Prefs.set("prefs.sync.testing.dont.change", true);
|
||||
Svc.Prefs.set("prefs.sync.testing.turned.off", false);
|
||||
Svc.Prefs.set("prefs.sync.testing.nonexistent", true);
|
||||
|
||||
prefs.set("testing.int", 123);
|
||||
prefs.set("testing.string", "ohai");
|
||||
prefs.set("testing.bool", true);
|
||||
prefs.set("testing.dont.change", "Please don't change me.");
|
||||
prefs.set("testing.turned.off", "I won't get synced.");
|
||||
prefs.set("testing.not.turned.on", "I won't get synced either!");
|
||||
|
||||
_("The GUID corresponds to XUL App ID.");
|
||||
let allIDs = store.getAllIDs();
|
||||
let ids = Object.keys(allIDs);
|
||||
|
|
@ -58,22 +61,17 @@ function run_test() {
|
|||
do_check_eq(record.value["testing.int"], 123);
|
||||
do_check_eq(record.value["testing.string"], "ohai");
|
||||
do_check_eq(record.value["testing.bool"], true);
|
||||
// non-existing prefs get null as the value
|
||||
do_check_eq(record.value["testing.nonexistent"], null);
|
||||
// as do prefs that have a default value.
|
||||
do_check_eq(record.value["testing.default"], null);
|
||||
do_check_false("testing.turned.off" in record.value);
|
||||
do_check_false("testing.not.turned.on" in record.value);
|
||||
|
||||
_("Prefs record contains non-default pref sync prefs too.");
|
||||
do_check_eq(record.value["services.sync.prefs.sync.testing.int"], null);
|
||||
do_check_eq(record.value["services.sync.prefs.sync.testing.string"], null);
|
||||
do_check_eq(record.value["services.sync.prefs.sync.testing.bool"], null);
|
||||
do_check_eq(record.value["services.sync.prefs.sync.testing.dont.change"], null);
|
||||
// but this one is a user_pref so *will* be synced.
|
||||
_("Prefs record contains pref sync prefs too.");
|
||||
do_check_eq(record.value["services.sync.prefs.sync.testing.int"], true);
|
||||
do_check_eq(record.value["services.sync.prefs.sync.testing.string"], true);
|
||||
do_check_eq(record.value["services.sync.prefs.sync.testing.bool"], true);
|
||||
do_check_eq(record.value["services.sync.prefs.sync.testing.dont.change"], true);
|
||||
do_check_eq(record.value["services.sync.prefs.sync.testing.turned.off"], false);
|
||||
do_check_eq(record.value["services.sync.prefs.sync.testing.nonexistent"], null);
|
||||
do_check_eq(record.value["services.sync.prefs.sync.testing.default"], null);
|
||||
do_check_eq(record.value["services.sync.prefs.sync.testing.nonexistent"], true);
|
||||
|
||||
_("Update some prefs, including one that's to be reset/deleted.");
|
||||
Svc.Prefs.set("testing.deleteme", "I'm going to be deleted!");
|
||||
|
|
@ -99,28 +97,28 @@ function run_test() {
|
|||
// Ensure we don't go to the network to fetch personas and end up leaking
|
||||
// stuff.
|
||||
Services.io.offline = true;
|
||||
do_check_false(!!prefs.get("lightweightThemes.selectedThemeID"));
|
||||
do_check_false(!!prefs.get("lightweightThemes.isThemeSelected"));
|
||||
do_check_eq(LightweightThemeManager.currentTheme, null);
|
||||
|
||||
let persona1 = makePersona();
|
||||
let persona2 = makePersona();
|
||||
let usedThemes = JSON.stringify([persona1, persona2]);
|
||||
record.value = {
|
||||
"lightweightThemes.selectedThemeID": persona1.id,
|
||||
"lightweightThemes.isThemeSelected": true,
|
||||
"lightweightThemes.usedThemes": usedThemes
|
||||
};
|
||||
store.update(record);
|
||||
do_check_eq(prefs.get("lightweightThemes.selectedThemeID"), persona1.id);
|
||||
do_check_true(prefs.get("lightweightThemes.isThemeSelected"));
|
||||
do_check_true(Utils.deepEquals(LightweightThemeManager.currentTheme,
|
||||
persona1));
|
||||
|
||||
_("Disable persona");
|
||||
record.value = {
|
||||
"lightweightThemes.selectedThemeID": null,
|
||||
"lightweightThemes.isThemeSelected": false,
|
||||
"lightweightThemes.usedThemes": usedThemes
|
||||
};
|
||||
store.update(record);
|
||||
do_check_false(!!prefs.get("lightweightThemes.selectedThemeID"));
|
||||
do_check_false(prefs.get("lightweightThemes.isThemeSelected"));
|
||||
do_check_eq(LightweightThemeManager.currentTheme, null);
|
||||
|
||||
_("Only the current app's preferences are applied.");
|
||||
|
|
@ -131,37 +129,6 @@ function run_test() {
|
|||
store.update(record);
|
||||
do_check_eq(prefs.get("testing.int"), 42);
|
||||
|
||||
_("The light-weight theme preference is handled correctly.");
|
||||
let lastThemeID = undefined;
|
||||
let orig_updateLightWeightTheme = store._updateLightWeightTheme;
|
||||
store._updateLightWeightTheme = function(themeID) {
|
||||
lastThemeID = themeID;
|
||||
}
|
||||
try {
|
||||
record = new PrefRec("prefs", PREFS_GUID);
|
||||
record.value = {
|
||||
"testing.int": 42,
|
||||
};
|
||||
store.update(record);
|
||||
do_check_true(lastThemeID === undefined,
|
||||
"should not have tried to change the theme with an unrelated pref.");
|
||||
Services.prefs.setCharPref("lightweightThemes.selectedThemeID", "foo");
|
||||
record.value = {
|
||||
"lightweightThemes.selectedThemeID": "foo",
|
||||
};
|
||||
store.update(record);
|
||||
do_check_true(lastThemeID === undefined,
|
||||
"should not have tried to change the theme when the incoming pref matches current value.");
|
||||
|
||||
record.value = {
|
||||
"lightweightThemes.selectedThemeID": "bar",
|
||||
};
|
||||
store.update(record);
|
||||
do_check_eq(lastThemeID, "bar",
|
||||
"should have tried to change the theme when the incoming pref was different.");
|
||||
} finally {
|
||||
store._updateLightWeightTheme = orig_updateLightWeightTheme;
|
||||
}
|
||||
} finally {
|
||||
prefs.resetBranch("");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ Cu.import("resource://services-sync/service.js");
|
|||
Cu.import("resource://services-sync/util.js");
|
||||
Cu.import("resource://testing-common/services/sync/utils.js");
|
||||
|
||||
var cryptoWrap;
|
||||
let cryptoWrap;
|
||||
|
||||
function crypted_resource_handler(metadata, response) {
|
||||
let obj = {id: "resource",
|
||||
|
|
@ -148,32 +148,6 @@ function run_test() {
|
|||
do_check_eq(bookmarkItem.decrypt(Service.collectionKeys.keyForCollection("bookmarks")).stuff,
|
||||
"my payload here");
|
||||
|
||||
do_check_true(Service.collectionKeys.hasKeysFor(["bookmarks"]));
|
||||
|
||||
// Add a key for some new collection and verify that it isn't the
|
||||
// default key.
|
||||
do_check_false(Service.collectionKeys.hasKeysFor(["forms"]));
|
||||
do_check_false(Service.collectionKeys.hasKeysFor(["bookmarks", "forms"]));
|
||||
let oldFormsKey = Service.collectionKeys.keyForCollection("forms");
|
||||
do_check_eq(oldFormsKey, Service.collectionKeys._default);
|
||||
let newKeys = Service.collectionKeys.ensureKeysFor(["forms"]);
|
||||
do_check_true(newKeys.hasKeysFor(["forms"]));
|
||||
do_check_true(newKeys.hasKeysFor(["bookmarks", "forms"]));
|
||||
let newFormsKey = newKeys.keyForCollection("forms");
|
||||
do_check_neq(newFormsKey, oldFormsKey);
|
||||
|
||||
// Verify that this doesn't overwrite keys
|
||||
let regetKeys = newKeys.ensureKeysFor(["forms"]);
|
||||
do_check_eq(regetKeys.keyForCollection("forms"), newFormsKey);
|
||||
|
||||
const emptyKeys = new CollectionKeyManager();
|
||||
payload = {
|
||||
default: Service.collectionKeys._default.keyPairB64,
|
||||
collections: {}
|
||||
};
|
||||
// Verify that not passing `modified` doesn't throw
|
||||
emptyKeys.setContents(payload, null);
|
||||
|
||||
log.info("Done!");
|
||||
}
|
||||
finally {
|
||||
|
|
|
|||
|
|
@ -7,9 +7,9 @@ Cu.import("resource://services-sync/identity.js");
|
|||
Cu.import("resource://services-sync/resource.js");
|
||||
Cu.import("resource://services-sync/util.js");
|
||||
|
||||
var logger;
|
||||
let logger;
|
||||
|
||||
var fetched = false;
|
||||
let fetched = false;
|
||||
function server_open(metadata, response) {
|
||||
let body;
|
||||
if (metadata.method == "GET") {
|
||||
|
|
@ -45,7 +45,7 @@ function server_404(metadata, response) {
|
|||
response.bodyOutputStream.write(body, body.length);
|
||||
}
|
||||
|
||||
var pacFetched = false;
|
||||
let pacFetched = false;
|
||||
function server_pac(metadata, response) {
|
||||
pacFetched = true;
|
||||
let body = 'function FindProxyForURL(url, host) { return "DIRECT"; }';
|
||||
|
|
@ -55,7 +55,7 @@ function server_pac(metadata, response) {
|
|||
}
|
||||
|
||||
|
||||
var sample_data = {
|
||||
let sample_data = {
|
||||
some: "sample_data",
|
||||
injson: "format",
|
||||
number: 42
|
||||
|
|
@ -140,7 +140,7 @@ function server_headers(metadata, response) {
|
|||
header_names = header_names.sort();
|
||||
|
||||
headers = {};
|
||||
for (let header of header_names) {
|
||||
for each (let header in header_names) {
|
||||
headers[header] = metadata.getHeader(header);
|
||||
}
|
||||
let body = JSON.stringify(headers);
|
||||
|
|
@ -442,8 +442,6 @@ function run_test() {
|
|||
// It throws and logs.
|
||||
do_check_eq(error.result, Cr.NS_ERROR_MALFORMED_URI);
|
||||
do_check_eq(error, "Error: NS_ERROR_MALFORMED_URI");
|
||||
// Note the strings haven't been formatted yet, but that's OK for this test.
|
||||
do_check_eq(warnings.pop(), "${action} request to ${url} failed: ${ex}");
|
||||
do_check_eq(warnings.pop(),
|
||||
"Got exception calling onProgress handler during fetch of " +
|
||||
server.baseURI + "/json");
|
||||
|
|
@ -467,7 +465,6 @@ function run_test() {
|
|||
// It throws and logs.
|
||||
do_check_eq(error.result, Cr.NS_ERROR_XPC_JS_THREW_STRING);
|
||||
do_check_eq(error, "Error: NS_ERROR_XPC_JS_THREW_STRING");
|
||||
do_check_eq(warnings.pop(), "${action} request to ${url} failed: ${ex}");
|
||||
do_check_eq(warnings.pop(),
|
||||
"Got exception calling onProgress handler during fetch of " +
|
||||
server.baseURI + "/json");
|
||||
|
|
|
|||
|
|
@ -7,9 +7,9 @@ Cu.import("resource://services-sync/identity.js");
|
|||
Cu.import("resource://services-sync/resource.js");
|
||||
Cu.import("resource://services-sync/util.js");
|
||||
|
||||
var logger;
|
||||
let logger;
|
||||
|
||||
var fetched = false;
|
||||
let fetched = false;
|
||||
function server_open(metadata, response) {
|
||||
let body;
|
||||
if (metadata.method == "GET") {
|
||||
|
|
@ -45,7 +45,7 @@ function server_404(metadata, response) {
|
|||
response.bodyOutputStream.write(body, body.length);
|
||||
}
|
||||
|
||||
var pacFetched = false;
|
||||
let pacFetched = false;
|
||||
function server_pac(metadata, response) {
|
||||
_("Invoked PAC handler.");
|
||||
pacFetched = true;
|
||||
|
|
@ -55,7 +55,7 @@ function server_pac(metadata, response) {
|
|||
response.bodyOutputStream.write(body, body.length);
|
||||
}
|
||||
|
||||
var sample_data = {
|
||||
let sample_data = {
|
||||
some: "sample_data",
|
||||
injson: "format",
|
||||
number: 42
|
||||
|
|
@ -140,7 +140,7 @@ function server_headers(metadata, response) {
|
|||
header_names = header_names.sort();
|
||||
|
||||
headers = {};
|
||||
for (let header of header_names) {
|
||||
for each (let header in header_names) {
|
||||
headers[header] = metadata.getHeader(header);
|
||||
}
|
||||
let body = JSON.stringify(headers);
|
||||
|
|
@ -148,7 +148,7 @@ function server_headers(metadata, response) {
|
|||
response.bodyOutputStream.write(body, body.length);
|
||||
}
|
||||
|
||||
var quotaValue;
|
||||
let quotaValue;
|
||||
Observers.add("weave:service:quota:remaining",
|
||||
function (subject) { quotaValue = subject; });
|
||||
|
||||
|
|
@ -221,7 +221,7 @@ add_test(function test_new_channel() {
|
|||
});
|
||||
|
||||
|
||||
var server;
|
||||
let server;
|
||||
|
||||
add_test(function setup() {
|
||||
server = httpd_setup({
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ function run_test() {
|
|||
run_next_test();
|
||||
}
|
||||
|
||||
var httpServer = new HttpServer();
|
||||
let httpServer = new HttpServer();
|
||||
httpServer.registerPathHandler("/content", contentHandler);
|
||||
httpServer.start(-1);
|
||||
|
||||
|
|
@ -20,8 +20,8 @@ const TEST_URL = "http://localhost:" + HTTP_PORT + "/content";
|
|||
const BODY = "response body";
|
||||
|
||||
// Keep headers for later inspection.
|
||||
var auth = null;
|
||||
var foo = null;
|
||||
let auth = null;
|
||||
let foo = null;
|
||||
function contentHandler(metadata, response) {
|
||||
_("Handling request.");
|
||||
auth = metadata.getHeader("Authorization");
|
||||
|
|
|
|||
|
|
@ -7,18 +7,15 @@ Cu.import("resource://services-sync/service.js");
|
|||
Cu.import("resource://services-sync/util.js");
|
||||
Cu.import("resource://testing-common/services/sync/utils.js");
|
||||
|
||||
var httpProtocolHandler = Cc["@mozilla.org/network/protocol;1?name=http"]
|
||||
.getService(Ci.nsIHttpProtocolHandler);
|
||||
|
||||
// Tracking info/collections.
|
||||
var collectionsHelper = track_collections_helper();
|
||||
var collections = collectionsHelper.collections;
|
||||
let collectionsHelper = track_collections_helper();
|
||||
let collections = collectionsHelper.collections;
|
||||
|
||||
var meta_global;
|
||||
var server;
|
||||
let meta_global;
|
||||
let server;
|
||||
|
||||
var expectedUA;
|
||||
var ua;
|
||||
let expectedUA;
|
||||
let ua;
|
||||
function uaHandler(f) {
|
||||
return function(request, response) {
|
||||
ua = request.getHeader("User-Agent");
|
||||
|
|
@ -40,10 +37,7 @@ function run_test() {
|
|||
Service.clusterURL = server.baseURI + "/";
|
||||
_("Server URL: " + server.baseURI);
|
||||
|
||||
// Note this string is missing the trailing ".destkop" as the test
|
||||
// adjusts the "client.type" pref where that portion comes from.
|
||||
expectedUA = Services.appinfo.name + "/" + Services.appinfo.version +
|
||||
" (" + httpProtocolHandler.oscpu + ")" +
|
||||
" FxSync/" + WEAVE_VERSION + "." +
|
||||
Services.appinfo.appBuildID;
|
||||
|
||||
|
|
|
|||
|
|
@ -12,13 +12,13 @@ Cu.import("resource://testing-common/services/sync/utils.js");
|
|||
|
||||
Service.engineManager.clear();
|
||||
Service.engineManager.register(RotaryEngine);
|
||||
var engine = Service.engineManager.get("rotary");
|
||||
var tracker = engine._tracker;
|
||||
let engine = Service.engineManager.get("rotary");
|
||||
let tracker = engine._tracker;
|
||||
engine.enabled = true;
|
||||
|
||||
// Tracking info/collections.
|
||||
var collectionsHelper = track_collections_helper();
|
||||
var upd = collectionsHelper.with_updated_collection;
|
||||
let collectionsHelper = track_collections_helper();
|
||||
let upd = collectionsHelper.with_updated_collection;
|
||||
|
||||
function sync_httpd_setup() {
|
||||
let handlers = {};
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ function test_urls() {
|
|||
|
||||
Service.serverURL = "http://weave.server/";
|
||||
Service.clusterURL = "http://weave.cluster/";
|
||||
do_check_eq(Svc.Prefs.get("clusterURL"), "http://weave.cluster/");
|
||||
|
||||
do_check_eq(Service.userBaseURL, "http://weave.cluster/1.1/johndoe/");
|
||||
do_check_eq(Service.infoURL,
|
||||
|
|
@ -62,11 +63,11 @@ function test_urls() {
|
|||
_("The 'serverURL' attributes updates/resets preferences.");
|
||||
// Identical value doesn't do anything
|
||||
Service.serverURL = Service.serverURL;
|
||||
do_check_eq(Service.clusterURL, "http://weave.cluster/");
|
||||
do_check_eq(Svc.Prefs.get("clusterURL"), "http://weave.cluster/");
|
||||
|
||||
Service.serverURL = "http://different.auth.node/";
|
||||
do_check_eq(Svc.Prefs.get("serverURL"), "http://different.auth.node/");
|
||||
do_check_eq(Service.clusterURL, "");
|
||||
do_check_eq(Svc.Prefs.get("clusterURL"), undefined);
|
||||
|
||||
} finally {
|
||||
Svc.Prefs.resetBranch("");
|
||||
|
|
@ -83,12 +84,12 @@ function test_syncID() {
|
|||
do_check_eq(Svc.Prefs.get("client.syncID"), undefined);
|
||||
|
||||
// Performing the first get on the attribute will generate a new GUID.
|
||||
do_check_eq(Service.syncID, "fake-guid-00");
|
||||
do_check_eq(Svc.Prefs.get("client.syncID"), "fake-guid-00");
|
||||
do_check_eq(Service.syncID, "fake-guid-0");
|
||||
do_check_eq(Svc.Prefs.get("client.syncID"), "fake-guid-0");
|
||||
|
||||
Svc.Prefs.set("client.syncID", Utils.makeGUID());
|
||||
do_check_eq(Svc.Prefs.get("client.syncID"), "fake-guid-01");
|
||||
do_check_eq(Service.syncID, "fake-guid-01");
|
||||
do_check_eq(Svc.Prefs.get("client.syncID"), "fake-guid-1");
|
||||
do_check_eq(Service.syncID, "fake-guid-1");
|
||||
} finally {
|
||||
Svc.Prefs.resetBranch("");
|
||||
new FakeGUIDService();
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ add_test(function v4_upgrade() {
|
|||
}]}]};
|
||||
delete Svc.Session;
|
||||
Svc.Session = {
|
||||
getBrowserState: () => JSON.stringify(myTabs)
|
||||
getBrowserState: function () JSON.stringify(myTabs)
|
||||
};
|
||||
|
||||
Service.status.resetSync();
|
||||
|
|
@ -229,7 +229,7 @@ add_test(function v5_upgrade() {
|
|||
}]}]};
|
||||
delete Svc.Session;
|
||||
Svc.Session = {
|
||||
getBrowserState: () => JSON.stringify(myTabs)
|
||||
getBrowserState: function () JSON.stringify(myTabs)
|
||||
};
|
||||
|
||||
Service.status.resetSync();
|
||||
|
|
|
|||
|
|
@ -7,10 +7,7 @@ Cu.import("resource://services-sync/service.js");
|
|||
Cu.import("resource://services-sync/util.js");
|
||||
Cu.import("resource://testing-common/services/sync/utils.js");
|
||||
|
||||
var httpProtocolHandler = Cc["@mozilla.org/network/protocol;1?name=http"]
|
||||
.getService(Ci.nsIHttpProtocolHandler);
|
||||
|
||||
var collections = {steam: 65.11328,
|
||||
let collections = {steam: 65.11328,
|
||||
petrol: 82.488281,
|
||||
diesel: 2.25488281};
|
||||
|
||||
|
|
@ -40,7 +37,6 @@ add_test(function test_success() {
|
|||
Service.identity.username,
|
||||
Service.identity.basicPassword));
|
||||
let expectedUA = Services.appinfo.name + "/" + Services.appinfo.version +
|
||||
" (" + httpProtocolHandler.oscpu + ")" +
|
||||
" FxSync/" + WEAVE_VERSION + "." +
|
||||
Services.appinfo.appBuildID + ".desktop";
|
||||
do_check_eq(handler.request.getHeader("User-Agent"), expectedUA);
|
||||
|
|
|
|||
|
|
@ -183,7 +183,7 @@ add_test(function test_login_on_sync() {
|
|||
// This test exercises these two branches.
|
||||
|
||||
_("We're ready to sync if locked.");
|
||||
Service.enabled = true;
|
||||
Svc.Prefs.set("enabled", true);
|
||||
Services.io.offline = false;
|
||||
Service.scheduler.checkSyncStatus();
|
||||
do_check_true(scheduleCalled);
|
||||
|
|
|
|||
|
|
@ -11,13 +11,13 @@ const APPLES = "\uf8ff\uf8ff\uf8ff\uf8ff";
|
|||
const LOWBYTES = "\xff\xff\xff\xff";
|
||||
|
||||
// Poor man's /etc/passwd. Static since there's no btoa()/atob() in xpcshell.
|
||||
var basicauth = {};
|
||||
let basicauth = {};
|
||||
basicauth[LOWBYTES] = "Basic am9obmRvZTr/////";
|
||||
basicauth[Utils.encodeUTF8(JAPANESE)] = "Basic am9obmRvZTrjk7/jl7/jm7/jn78=";
|
||||
|
||||
// Global var for the server password, read by info_collections(),
|
||||
// modified by change_password().
|
||||
var server_password;
|
||||
let server_password;
|
||||
|
||||
function login_handling(handler) {
|
||||
return function (request, response) {
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ function run_test() {
|
|||
run_next_test();
|
||||
}
|
||||
|
||||
add_identity_test(this, function* test_resetLocalData() {
|
||||
add_identity_test(this, function test_resetLocalData() {
|
||||
yield configureIdentity();
|
||||
Service.status.enforceBackoff = true;
|
||||
Service.status.backoffInterval = 42;
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ Svc.Prefs.set("registerEngines", "Tab,Bookmarks,Form,History");
|
|||
Cu.import("resource://services-sync/service.js");
|
||||
|
||||
function run_test() {
|
||||
validate_all_future_pings();
|
||||
_("When imported, Service.onStartup is called");
|
||||
initTestLogging("Trace");
|
||||
|
||||
|
|
@ -21,7 +20,7 @@ function run_test() {
|
|||
|
||||
// Test fixtures
|
||||
Service.identity.username = "johndoe";
|
||||
do_check_true(xps.enabled);
|
||||
do_check_false(xps.enabled);
|
||||
|
||||
Cu.import("resource://services-sync/service.js");
|
||||
|
||||
|
|
@ -30,7 +29,7 @@ function run_test() {
|
|||
|
||||
_("Engines are registered.");
|
||||
let engines = Service.engineManager.getAll();
|
||||
do_check_true(Utils.deepEquals(engines.map(engine => engine.name),
|
||||
do_check_true(Utils.deepEquals([engine.name for each (engine in engines)],
|
||||
['tabs', 'bookmarks', 'forms', 'history']));
|
||||
|
||||
_("Observers are notified of startup");
|
||||
|
|
@ -46,4 +45,10 @@ function run_test() {
|
|||
Svc.Prefs.resetBranch("");
|
||||
do_test_finished();
|
||||
});
|
||||
|
||||
do_check_false(xps.enabled);
|
||||
|
||||
Service.identity.account = "johndoe";
|
||||
Service.clusterURL = "http://localhost/";
|
||||
do_check_true(xps.enabled);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,17 +5,14 @@ Cu.import("resource://services-sync/service.js");
|
|||
Cu.import("resource://services-sync/util.js");
|
||||
|
||||
function run_test() {
|
||||
validate_all_future_pings();
|
||||
let debug = [];
|
||||
let info = [];
|
||||
|
||||
function augmentLogger(old) {
|
||||
let d = old.debug;
|
||||
let i = old.info;
|
||||
// For the purposes of this test we don't need to do full formatting
|
||||
// of the 2nd param, as the ones we care about are always strings.
|
||||
old.debug = function(m, p) { debug.push(p ? m + ": " + p : m); d.call(old, m, p); }
|
||||
old.info = function(m, p) { info.push(p ? m + ": " + p : m); i.call(old, m, p); }
|
||||
old.debug = function(m) { debug.push(m); d.call(old, m); }
|
||||
old.info = function(m) { info.push(m); i.call(old, m); }
|
||||
return old;
|
||||
}
|
||||
|
||||
|
|
@ -31,7 +28,9 @@ function run_test() {
|
|||
Service.sync();
|
||||
Service._locked = false;
|
||||
|
||||
do_check_true(debug[debug.length - 2].startsWith("Exception calling WrappedLock: Could not acquire lock. Label: \"service.js: login\"."));
|
||||
do_check_eq(info[info.length - 1], "Cannot start sync: already syncing?");
|
||||
do_check_eq(debug[debug.length - 2],
|
||||
"Exception: Could not acquire lock. Label: \"service.js: login\". No traceback available");
|
||||
do_check_eq(info[info.length - 1],
|
||||
"Cannot start sync: already syncing?");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ Cu.import("resource://testing-common/services/sync/fakeservices.js");
|
|||
Cu.import("resource://testing-common/services/sync/utils.js");
|
||||
|
||||
function run_test() {
|
||||
validate_all_future_pings();
|
||||
let logger = Log.repository.rootLogger;
|
||||
Log.repository.rootLogger.addAppender(new Log.DumpAppender());
|
||||
|
||||
|
|
@ -54,27 +53,15 @@ function run_test() {
|
|||
return_timestamp(request, response, ts);
|
||||
}
|
||||
|
||||
const GLOBAL_PATH = "/1.1/johndoe/storage/meta/global";
|
||||
const INFO_PATH = "/1.1/johndoe/info/collections";
|
||||
|
||||
let handlers = {
|
||||
let server = httpd_setup({
|
||||
"/1.1/johndoe/storage": storageHandler,
|
||||
"/1.1/johndoe/storage/crypto/keys": upd("crypto", keysWBO.handler()),
|
||||
"/1.1/johndoe/storage/crypto": upd("crypto", cryptoColl.handler()),
|
||||
"/1.1/johndoe/storage/clients": upd("clients", clients.handler()),
|
||||
"/1.1/johndoe/storage/meta": upd("meta", wasCalledHandler(metaColl)),
|
||||
"/1.1/johndoe/storage/meta/global": upd("meta", wasCalledHandler(meta_global)),
|
||||
"/1.1/johndoe/storage/meta": upd("meta", wasCalledHandler(metaColl)),
|
||||
"/1.1/johndoe/info/collections": collectionsHelper.handler
|
||||
};
|
||||
|
||||
function mockHandler(path, mock) {
|
||||
server.registerPathHandler(path, mock(handlers[path]));
|
||||
return {
|
||||
restore() { server.registerPathHandler(path, handlers[path]); }
|
||||
}
|
||||
}
|
||||
|
||||
let server = httpd_setup(handlers);
|
||||
});
|
||||
|
||||
try {
|
||||
_("Log in.");
|
||||
|
|
@ -102,63 +89,6 @@ function run_test() {
|
|||
Service.recordManager.get(Service.metaURL).payload.syncID = "foobar";
|
||||
do_check_true(Service._remoteSetup());
|
||||
|
||||
let returnStatusCode = (method, code) => (oldMethod) => (req, res) => {
|
||||
if (req.method === method) {
|
||||
res.setStatusLine(req.httpVersion, code, "");
|
||||
} else {
|
||||
oldMethod(req, res);
|
||||
}
|
||||
};
|
||||
|
||||
let mock = mockHandler(GLOBAL_PATH, returnStatusCode("GET", 401));
|
||||
Service.recordManager.del(Service.metaURL);
|
||||
_("Checking that remoteSetup returns false on 401 on first get /meta/global.");
|
||||
do_check_false(Service._remoteSetup());
|
||||
mock.restore();
|
||||
|
||||
Service.login("johndoe", "ilovejane", syncKey);
|
||||
mock = mockHandler(GLOBAL_PATH, returnStatusCode("GET", 503));
|
||||
Service.recordManager.del(Service.metaURL);
|
||||
_("Checking that remoteSetup returns false on 503 on first get /meta/global.");
|
||||
do_check_false(Service._remoteSetup());
|
||||
do_check_eq(Service.status.sync, METARECORD_DOWNLOAD_FAIL);
|
||||
mock.restore();
|
||||
|
||||
mock = mockHandler(GLOBAL_PATH, returnStatusCode("GET", 404));
|
||||
Service.recordManager.del(Service.metaURL);
|
||||
_("Checking that remoteSetup recovers on 404 on first get /meta/global.");
|
||||
do_check_true(Service._remoteSetup());
|
||||
mock.restore();
|
||||
|
||||
let makeOutdatedMeta = () => {
|
||||
Service.metaModified = 0;
|
||||
let infoResponse = Service._fetchInfo();
|
||||
return {
|
||||
status: infoResponse.status,
|
||||
obj: {
|
||||
crypto: infoResponse.obj.crypto,
|
||||
clients: infoResponse.obj.clients,
|
||||
meta: 1
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
_("Checking that remoteSetup recovers on 404 on get /meta/global after clear cached one.");
|
||||
mock = mockHandler(GLOBAL_PATH, returnStatusCode("GET", 404));
|
||||
Service.recordManager.set(Service.metaURL, { isNew: false });
|
||||
do_check_true(Service._remoteSetup(makeOutdatedMeta()));
|
||||
mock.restore();
|
||||
|
||||
_("Checking that remoteSetup returns false on 503 on get /meta/global after clear cached one.");
|
||||
mock = mockHandler(GLOBAL_PATH, returnStatusCode("GET", 503));
|
||||
Service.status.sync = "";
|
||||
Service.recordManager.set(Service.metaURL, { isNew: false });
|
||||
do_check_false(Service._remoteSetup(makeOutdatedMeta()));
|
||||
do_check_eq(Service.status.sync, "");
|
||||
mock.restore();
|
||||
|
||||
metaColl.delete({});
|
||||
|
||||
_("Do an initial sync.");
|
||||
let beforeSync = Date.now()/1000;
|
||||
Service.sync();
|
||||
|
|
@ -230,6 +160,7 @@ function run_test() {
|
|||
|
||||
do_check_false(Service.verifyAndFetchSymmetricKeys());
|
||||
do_check_eq(Service.status.login, LOGIN_FAILED_INVALID_PASSPHRASE);
|
||||
|
||||
} finally {
|
||||
Svc.Prefs.resetBranch("");
|
||||
server.stop(do_test_finished);
|
||||
|
|
|
|||
|
|
@ -1,160 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
Cu.import("resource://services-sync/constants.js");
|
||||
Cu.import("resource://services-sync/engines.js");
|
||||
Cu.import("resource://services-sync/engines/clients.js");
|
||||
Cu.import("resource://services-sync/record.js");
|
||||
Cu.import("resource://services-sync/service.js");
|
||||
Cu.import("resource://services-sync/util.js");
|
||||
Cu.import("resource://testing-common/services/sync/utils.js");
|
||||
|
||||
initTestLogging();
|
||||
Service.engineManager.clear();
|
||||
|
||||
let syncedEngines = []
|
||||
|
||||
function SteamEngine() {
|
||||
SyncEngine.call(this, "Steam", Service);
|
||||
}
|
||||
SteamEngine.prototype = {
|
||||
__proto__: SyncEngine.prototype,
|
||||
_sync: function _sync() {
|
||||
syncedEngines.push(this.name);
|
||||
}
|
||||
};
|
||||
Service.engineManager.register(SteamEngine);
|
||||
|
||||
function StirlingEngine() {
|
||||
SyncEngine.call(this, "Stirling", Service);
|
||||
}
|
||||
StirlingEngine.prototype = {
|
||||
__proto__: SteamEngine.prototype,
|
||||
_sync: function _sync() {
|
||||
syncedEngines.push(this.name);
|
||||
}
|
||||
};
|
||||
Service.engineManager.register(StirlingEngine);
|
||||
|
||||
// Tracking info/collections.
|
||||
var collectionsHelper = track_collections_helper();
|
||||
var upd = collectionsHelper.with_updated_collection;
|
||||
|
||||
function sync_httpd_setup(handlers) {
|
||||
|
||||
handlers["/1.1/johndoe/info/collections"] = collectionsHelper.handler;
|
||||
delete collectionsHelper.collections.crypto;
|
||||
delete collectionsHelper.collections.meta;
|
||||
|
||||
let cr = new ServerWBO("keys");
|
||||
handlers["/1.1/johndoe/storage/crypto/keys"] =
|
||||
upd("crypto", cr.handler());
|
||||
|
||||
let cl = new ServerCollection();
|
||||
handlers["/1.1/johndoe/storage/clients"] =
|
||||
upd("clients", cl.handler());
|
||||
|
||||
return httpd_setup(handlers);
|
||||
}
|
||||
|
||||
function setUp() {
|
||||
syncedEngines = [];
|
||||
let engine = Service.engineManager.get("steam");
|
||||
engine.enabled = true;
|
||||
engine.syncPriority = 1;
|
||||
|
||||
engine = Service.engineManager.get("stirling");
|
||||
engine.enabled = true;
|
||||
engine.syncPriority = 2;
|
||||
|
||||
let server = sync_httpd_setup({
|
||||
"/1.1/johndoe/storage/meta/global": new ServerWBO("global", {}).handler(),
|
||||
});
|
||||
new SyncTestingInfrastructure(server, "johndoe", "ilovejane",
|
||||
"abcdeabcdeabcdeabcdeabcdea");
|
||||
return server;
|
||||
}
|
||||
|
||||
function run_test() {
|
||||
initTestLogging("Trace");
|
||||
validate_all_future_pings();
|
||||
Log.repository.getLogger("Sync.Service").level = Log.Level.Trace;
|
||||
Log.repository.getLogger("Sync.ErrorHandler").level = Log.Level.Trace;
|
||||
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_test(function test_noEngines() {
|
||||
_("Test: An empty array of engines to sync does nothing.");
|
||||
let server = setUp();
|
||||
|
||||
try {
|
||||
_("Sync with no engines specified.");
|
||||
Service.sync([]);
|
||||
deepEqual(syncedEngines, [], "no engines were synced");
|
||||
|
||||
} finally {
|
||||
Service.startOver();
|
||||
server.stop(run_next_test);
|
||||
}
|
||||
});
|
||||
|
||||
add_test(function test_oneEngine() {
|
||||
_("Test: Only one engine is synced.");
|
||||
let server = setUp();
|
||||
|
||||
try {
|
||||
|
||||
_("Sync with 1 engine specified.");
|
||||
Service.sync(["steam"]);
|
||||
deepEqual(syncedEngines, ["steam"])
|
||||
|
||||
} finally {
|
||||
Service.startOver();
|
||||
server.stop(run_next_test);
|
||||
}
|
||||
});
|
||||
|
||||
add_test(function test_bothEnginesSpecified() {
|
||||
_("Test: All engines are synced when specified in the correct order (1).");
|
||||
let server = setUp();
|
||||
|
||||
try {
|
||||
_("Sync with both engines specified.");
|
||||
Service.sync(["steam", "stirling"]);
|
||||
deepEqual(syncedEngines, ["steam", "stirling"])
|
||||
|
||||
} finally {
|
||||
Service.startOver();
|
||||
server.stop(run_next_test);
|
||||
}
|
||||
});
|
||||
|
||||
add_test(function test_bothEnginesSpecified() {
|
||||
_("Test: All engines are synced when specified in the correct order (2).");
|
||||
let server = setUp();
|
||||
|
||||
try {
|
||||
_("Sync with both engines specified.");
|
||||
Service.sync(["stirling", "steam"]);
|
||||
deepEqual(syncedEngines, ["stirling", "steam"])
|
||||
|
||||
} finally {
|
||||
Service.startOver();
|
||||
server.stop(run_next_test);
|
||||
}
|
||||
});
|
||||
|
||||
add_test(function test_bothEnginesDefault() {
|
||||
_("Test: All engines are synced when nothing is specified.");
|
||||
let server = setUp();
|
||||
|
||||
try {
|
||||
Service.sync();
|
||||
deepEqual(syncedEngines, ["steam", "stirling"])
|
||||
|
||||
} finally {
|
||||
Service.startOver();
|
||||
server.stop(run_next_test);
|
||||
}
|
||||
});
|
||||
|
|
@ -41,15 +41,13 @@ function StirlingEngine() {
|
|||
StirlingEngine.prototype = {
|
||||
__proto__: SteamEngine.prototype,
|
||||
// This engine's enabled state is the same as the SteamEngine's.
|
||||
get prefName() {
|
||||
return "steam";
|
||||
}
|
||||
get prefName() "steam"
|
||||
};
|
||||
Service.engineManager.register(StirlingEngine);
|
||||
|
||||
// Tracking info/collections.
|
||||
var collectionsHelper = track_collections_helper();
|
||||
var upd = collectionsHelper.with_updated_collection;
|
||||
let collectionsHelper = track_collections_helper();
|
||||
let upd = collectionsHelper.with_updated_collection;
|
||||
|
||||
function sync_httpd_setup(handlers) {
|
||||
|
||||
|
|
@ -86,7 +84,6 @@ function run_test() {
|
|||
initTestLogging("Trace");
|
||||
Log.repository.getLogger("Sync.Service").level = Log.Level.Trace;
|
||||
Log.repository.getLogger("Sync.ErrorHandler").level = Log.Level.Trace;
|
||||
validate_all_future_pings();
|
||||
|
||||
run_next_test();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ FakeCollection.prototype = {
|
|||
}
|
||||
};
|
||||
|
||||
function* setUpTestFixtures(server) {
|
||||
function setUpTestFixtures(server) {
|
||||
let cryptoService = new FakeCryptoService();
|
||||
|
||||
Service.serverURL = server.baseURI + "/";
|
||||
|
|
@ -52,7 +52,7 @@ function promiseStopServer(server) {
|
|||
return deferred.promise;
|
||||
}
|
||||
|
||||
add_identity_test(this, function* test_wipeServer_list_success() {
|
||||
add_identity_test(this, function test_wipeServer_list_success() {
|
||||
_("Service.wipeServer() deletes collections given as argument.");
|
||||
|
||||
let steam_coll = new FakeCollection();
|
||||
|
|
@ -86,7 +86,7 @@ add_identity_test(this, function* test_wipeServer_list_success() {
|
|||
}
|
||||
});
|
||||
|
||||
add_identity_test(this, function* test_wipeServer_list_503() {
|
||||
add_identity_test(this, function test_wipeServer_list_503() {
|
||||
_("Service.wipeServer() deletes collections given as argument.");
|
||||
|
||||
let steam_coll = new FakeCollection();
|
||||
|
|
@ -127,7 +127,7 @@ add_identity_test(this, function* test_wipeServer_list_503() {
|
|||
}
|
||||
});
|
||||
|
||||
add_identity_test(this, function* test_wipeServer_all_success() {
|
||||
add_identity_test(this, function test_wipeServer_all_success() {
|
||||
_("Service.wipeServer() deletes all the things.");
|
||||
|
||||
/**
|
||||
|
|
@ -157,7 +157,7 @@ add_identity_test(this, function* test_wipeServer_all_success() {
|
|||
Svc.Prefs.resetBranch("");
|
||||
});
|
||||
|
||||
add_identity_test(this, function* test_wipeServer_all_404() {
|
||||
add_identity_test(this, function test_wipeServer_all_404() {
|
||||
_("Service.wipeServer() accepts a 404.");
|
||||
|
||||
/**
|
||||
|
|
@ -189,7 +189,7 @@ add_identity_test(this, function* test_wipeServer_all_404() {
|
|||
Svc.Prefs.resetBranch("");
|
||||
});
|
||||
|
||||
add_identity_test(this, function* test_wipeServer_all_503() {
|
||||
add_identity_test(this, function test_wipeServer_all_503() {
|
||||
_("Service.wipeServer() throws if it encounters a non-200/404 response.");
|
||||
|
||||
/**
|
||||
|
|
@ -221,7 +221,7 @@ add_identity_test(this, function* test_wipeServer_all_503() {
|
|||
Svc.Prefs.resetBranch("");
|
||||
});
|
||||
|
||||
add_identity_test(this, function* test_wipeServer_all_connectionRefused() {
|
||||
add_identity_test(this, function test_wipeServer_all_connectionRefused() {
|
||||
_("Service.wipeServer() throws if it encounters a network problem.");
|
||||
let server = httpd_setup({});
|
||||
yield setUpTestFixtures(server);
|
||||
|
|
|
|||
|
|
@ -18,9 +18,9 @@ function run_test() {
|
|||
|
||||
|
||||
// Check login status
|
||||
for (let code of [LOGIN_FAILED_NO_USERNAME,
|
||||
LOGIN_FAILED_NO_PASSWORD,
|
||||
LOGIN_FAILED_NO_PASSPHRASE]) {
|
||||
for each (let code in [LOGIN_FAILED_NO_USERNAME,
|
||||
LOGIN_FAILED_NO_PASSWORD,
|
||||
LOGIN_FAILED_NO_PASSPHRASE]) {
|
||||
Status.login = code;
|
||||
do_check_eq(Status.login, code);
|
||||
do_check_eq(Status.service, CLIENT_NOT_CONFIGURED);
|
||||
|
|
|
|||
|
|
@ -1,221 +0,0 @@
|
|||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
|
||||
* vim:set ts=2 sw=2 sts=2 et:
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
Cu.import("resource://services-sync/main.js");
|
||||
Cu.import("resource://services-sync/SyncedTabs.jsm");
|
||||
Cu.import("resource://gre/modules/Log.jsm");
|
||||
|
||||
const faviconService = Cc["@mozilla.org/browser/favicon-service;1"]
|
||||
.getService(Ci.nsIFaviconService);
|
||||
|
||||
Log.repository.getLogger("Sync.RemoteTabs").addAppender(new Log.DumpAppender());
|
||||
|
||||
// A mock "Tabs" engine which the SyncedTabs module will use instead of the real
|
||||
// engine. We pass a constructor that Sync creates.
|
||||
function MockTabsEngine() {
|
||||
this.clients = {}; // We'll set this dynamically
|
||||
}
|
||||
|
||||
MockTabsEngine.prototype = {
|
||||
name: "tabs",
|
||||
enabled: true,
|
||||
|
||||
getAllClients() {
|
||||
return this.clients;
|
||||
},
|
||||
|
||||
getOpenURLs() {
|
||||
return new Set();
|
||||
},
|
||||
}
|
||||
|
||||
// A clients engine that doesn't need to be a constructor.
|
||||
let MockClientsEngine = {
|
||||
clientSettings: null, // Set in `configureClients`.
|
||||
|
||||
isMobile(guid) {
|
||||
if (!guid.endsWith("desktop") && !guid.endsWith("mobile")) {
|
||||
throw new Error("this module expected guids to end with 'desktop' or 'mobile'");
|
||||
}
|
||||
return guid.endsWith("mobile");
|
||||
},
|
||||
remoteClientExists(id) {
|
||||
return this.clientSettings[id] !== false;
|
||||
},
|
||||
getClientName(id) {
|
||||
if (this.clientSettings[id]) {
|
||||
return this.clientSettings[id];
|
||||
}
|
||||
let engine = Weave.Service.engineManager.get("tabs");
|
||||
return engine.clients[id].clientName;
|
||||
},
|
||||
}
|
||||
|
||||
// Configure Sync with our mock tabs engine and force it to become initialized.
|
||||
Services.prefs.setCharPref("services.sync.username", "someone@somewhere.com");
|
||||
|
||||
Weave.Service.engineManager.unregister("tabs");
|
||||
Weave.Service.engineManager.register(MockTabsEngine);
|
||||
Weave.Service.clientsEngine = MockClientsEngine;
|
||||
|
||||
// Tell the Sync XPCOM service it is initialized.
|
||||
let weaveXPCService = Cc["@mozilla.org/weave/service;1"]
|
||||
.getService(Ci.nsISupports)
|
||||
.wrappedJSObject;
|
||||
weaveXPCService.ready = true;
|
||||
|
||||
function configureClients(clients, clientSettings = {}) {
|
||||
// Configure the instance Sync created.
|
||||
let engine = Weave.Service.engineManager.get("tabs");
|
||||
// each client record is expected to have an id.
|
||||
for (let [guid, client] of Object.entries(clients)) {
|
||||
client.id = guid;
|
||||
}
|
||||
engine.clients = clients;
|
||||
// Apply clients collection overrides.
|
||||
MockClientsEngine.clientSettings = clientSettings;
|
||||
// Send an observer that pretends the engine just finished a sync.
|
||||
Services.obs.notifyObservers(null, "weave:engine:sync:finish", "tabs");
|
||||
}
|
||||
|
||||
// The tests.
|
||||
add_task(function* test_noClients() {
|
||||
// no clients, can't be tabs.
|
||||
yield configureClients({});
|
||||
|
||||
let tabs = yield SyncedTabs.getTabClients();
|
||||
equal(Object.keys(tabs).length, 0);
|
||||
});
|
||||
|
||||
add_task(function* test_clientWithTabs() {
|
||||
yield configureClients({
|
||||
guid_desktop: {
|
||||
clientName: "My Desktop",
|
||||
tabs: [
|
||||
{
|
||||
urlHistory: ["http://foo.com/"],
|
||||
icon: "http://foo.com/favicon",
|
||||
}],
|
||||
},
|
||||
guid_mobile: {
|
||||
clientName: "My Phone",
|
||||
tabs: [],
|
||||
}
|
||||
});
|
||||
|
||||
let clients = yield SyncedTabs.getTabClients();
|
||||
equal(clients.length, 2);
|
||||
clients.sort((a, b) => { return a.name.localeCompare(b.name);});
|
||||
equal(clients[0].tabs.length, 1);
|
||||
equal(clients[0].tabs[0].url, "http://foo.com/");
|
||||
equal(clients[0].tabs[0].icon, "http://foo.com/favicon");
|
||||
// second client has no tabs.
|
||||
equal(clients[1].tabs.length, 0);
|
||||
});
|
||||
|
||||
add_task(function* test_staleClientWithTabs() {
|
||||
yield configureClients({
|
||||
guid_desktop: {
|
||||
clientName: "My Desktop",
|
||||
tabs: [
|
||||
{
|
||||
urlHistory: ["http://foo.com/"],
|
||||
icon: "http://foo.com/favicon",
|
||||
}],
|
||||
},
|
||||
guid_mobile: {
|
||||
clientName: "My Phone",
|
||||
tabs: [],
|
||||
},
|
||||
guid_stale_mobile: {
|
||||
clientName: "My Deleted Phone",
|
||||
tabs: [],
|
||||
},
|
||||
guid_stale_desktop: {
|
||||
clientName: "My Deleted Laptop",
|
||||
tabs: [
|
||||
{
|
||||
urlHistory: ["https://bar.com/"],
|
||||
icon: "https://bar.com/favicon",
|
||||
}],
|
||||
},
|
||||
guid_stale_name_desktop: {
|
||||
clientName: "My Generic Device",
|
||||
tabs: [
|
||||
{
|
||||
urlHistory: ["https://example.edu/"],
|
||||
icon: "https://example.edu/favicon",
|
||||
}],
|
||||
},
|
||||
}, {
|
||||
guid_stale_mobile: false,
|
||||
guid_stale_desktop: false,
|
||||
// We should always use the device name from the clients collection, instead
|
||||
// of the possibly stale tabs collection.
|
||||
guid_stale_name_desktop: "My Laptop",
|
||||
});
|
||||
let clients = yield SyncedTabs.getTabClients();
|
||||
clients.sort((a, b) => { return a.name.localeCompare(b.name);});
|
||||
equal(clients.length, 3);
|
||||
equal(clients[0].name, "My Desktop");
|
||||
equal(clients[0].tabs.length, 1);
|
||||
equal(clients[0].tabs[0].url, "http://foo.com/");
|
||||
equal(clients[1].name, "My Laptop");
|
||||
equal(clients[1].tabs.length, 1);
|
||||
equal(clients[1].tabs[0].url, "https://example.edu/");
|
||||
equal(clients[2].name, "My Phone");
|
||||
equal(clients[2].tabs.length, 0);
|
||||
});
|
||||
|
||||
add_task(function* test_clientWithTabsIconsDisabled() {
|
||||
Services.prefs.setBoolPref("services.sync.syncedTabs.showRemoteIcons", false);
|
||||
yield configureClients({
|
||||
guid_desktop: {
|
||||
clientName: "My Desktop",
|
||||
tabs: [
|
||||
{
|
||||
urlHistory: ["http://foo.com/"],
|
||||
icon: "http://foo.com/favicon",
|
||||
}],
|
||||
},
|
||||
});
|
||||
|
||||
let clients = yield SyncedTabs.getTabClients();
|
||||
equal(clients.length, 1);
|
||||
clients.sort((a, b) => { return a.name.localeCompare(b.name);});
|
||||
equal(clients[0].tabs.length, 1);
|
||||
equal(clients[0].tabs[0].url, "http://foo.com/");
|
||||
// expect the default favicon (empty string) due to the pref being false.
|
||||
equal(clients[0].tabs[0].icon, "");
|
||||
Services.prefs.clearUserPref("services.sync.syncedTabs.showRemoteIcons");
|
||||
});
|
||||
|
||||
add_task(function* test_filter() {
|
||||
// Nothing matches.
|
||||
yield configureClients({
|
||||
guid_desktop: {
|
||||
clientName: "My Desktop",
|
||||
tabs: [
|
||||
{
|
||||
urlHistory: ["http://foo.com/"],
|
||||
title: "A test page.",
|
||||
},
|
||||
{
|
||||
urlHistory: ["http://bar.com/"],
|
||||
title: "Another page.",
|
||||
}],
|
||||
},
|
||||
});
|
||||
|
||||
let clients = yield SyncedTabs.getTabClients("foo");
|
||||
equal(clients.length, 1);
|
||||
equal(clients[0].tabs.length, 1);
|
||||
equal(clients[0].tabs[0].url, "http://foo.com/");
|
||||
// check it matches the title.
|
||||
clients = yield SyncedTabs.getTabClients("test");
|
||||
equal(clients.length, 1);
|
||||
equal(clients[0].tabs.length, 1);
|
||||
equal(clients[0].tabs[0].url, "http://foo.com/");
|
||||
});
|
||||
|
|
@ -10,7 +10,7 @@ function makeSteamEngine() {
|
|||
return new SyncEngine('Steam', Service);
|
||||
}
|
||||
|
||||
var server;
|
||||
let server;
|
||||
|
||||
function test_url_attributes() {
|
||||
_("SyncEngine url attributes");
|
||||
|
|
@ -35,12 +35,12 @@ function test_syncID() {
|
|||
do_check_eq(Svc.Prefs.get("steam.syncID"), undefined);
|
||||
|
||||
// Performing the first get on the attribute will generate a new GUID.
|
||||
do_check_eq(engine.syncID, "fake-guid-00");
|
||||
do_check_eq(Svc.Prefs.get("steam.syncID"), "fake-guid-00");
|
||||
do_check_eq(engine.syncID, "fake-guid-0");
|
||||
do_check_eq(Svc.Prefs.get("steam.syncID"), "fake-guid-0");
|
||||
|
||||
Svc.Prefs.set("steam.syncID", Utils.makeGUID());
|
||||
do_check_eq(Svc.Prefs.get("steam.syncID"), "fake-guid-01");
|
||||
do_check_eq(engine.syncID, "fake-guid-01");
|
||||
do_check_eq(Svc.Prefs.get("steam.syncID"), "fake-guid-1");
|
||||
do_check_eq(engine.syncID, "fake-guid-1");
|
||||
} finally {
|
||||
Svc.Prefs.resetBranch("");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,22 +15,13 @@ function makeRotaryEngine() {
|
|||
return new RotaryEngine(Service);
|
||||
}
|
||||
|
||||
function clean() {
|
||||
function cleanAndGo(server) {
|
||||
Svc.Prefs.resetBranch("");
|
||||
Svc.Prefs.set("log.logger.engine.rotary", "Trace");
|
||||
Service.recordManager.clearCache();
|
||||
}
|
||||
|
||||
function cleanAndGo(server) {
|
||||
clean();
|
||||
server.stop(run_next_test);
|
||||
}
|
||||
|
||||
function promiseClean(server) {
|
||||
clean();
|
||||
return new Promise(resolve => server.stop(resolve));
|
||||
}
|
||||
|
||||
function configureService(server, username, password) {
|
||||
Service.clusterURL = server.baseURI;
|
||||
|
||||
|
|
@ -181,7 +172,7 @@ add_test(function test_syncStartup_syncIDMismatchResetsClient() {
|
|||
try {
|
||||
|
||||
// Confirm initial environment
|
||||
do_check_eq(engine.syncID, 'fake-guid-00');
|
||||
do_check_eq(engine.syncID, 'fake-guid-0');
|
||||
do_check_eq(engine._tracker.changedIDs["rekolok"], undefined);
|
||||
|
||||
engine.lastSync = Date.now() / 1000;
|
||||
|
|
@ -676,7 +667,7 @@ add_test(function test_processIncoming_mobile_batchSize() {
|
|||
});
|
||||
|
||||
|
||||
add_task(function *test_processIncoming_store_toFetch() {
|
||||
add_test(function test_processIncoming_store_toFetch() {
|
||||
_("If processIncoming fails in the middle of a batch on mobile, state is saved in toFetch and lastSync.");
|
||||
Service.identity.username = "foo";
|
||||
Svc.Prefs.set("client.type", "mobile");
|
||||
|
|
@ -723,10 +714,11 @@ add_task(function *test_processIncoming_store_toFetch() {
|
|||
|
||||
let error;
|
||||
try {
|
||||
yield sync_engine_and_validate_telem(engine, true);
|
||||
engine.sync();
|
||||
} catch (ex) {
|
||||
error = ex;
|
||||
}
|
||||
do_check_true(!!error);
|
||||
|
||||
// Only the first two batches have been applied.
|
||||
do_check_eq(Object.keys(engine._store.items).length,
|
||||
|
|
@ -738,7 +730,7 @@ add_task(function *test_processIncoming_store_toFetch() {
|
|||
do_check_eq(engine.lastSync, collection.wbo("record-no-99").modified);
|
||||
|
||||
} finally {
|
||||
yield promiseClean(server);
|
||||
cleanAndGo(server);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -1229,7 +1221,7 @@ add_test(function test_processIncoming_failed_records() {
|
|||
});
|
||||
|
||||
|
||||
add_task(function *test_processIncoming_decrypt_failed() {
|
||||
add_test(function test_processIncoming_decrypt_failed() {
|
||||
_("Ensure that records failing to decrypt are either replaced or refetched.");
|
||||
|
||||
Service.identity.username = "foo";
|
||||
|
|
@ -1288,10 +1280,7 @@ add_task(function *test_processIncoming_decrypt_failed() {
|
|||
});
|
||||
|
||||
engine.lastSync = collection.wbo("nojson").modified - 1;
|
||||
let ping = yield sync_engine_and_validate_telem(engine, true);
|
||||
do_check_eq(ping.engines[0].incoming.applied, 2);
|
||||
do_check_eq(ping.engines[0].incoming.failed, 4);
|
||||
do_check_eq(ping.engines[0].incoming.newFailed, 4);
|
||||
engine.sync();
|
||||
|
||||
do_check_eq(engine.previousFailed.length, 4);
|
||||
do_check_eq(engine.previousFailed[0], "nojson");
|
||||
|
|
@ -1305,7 +1294,7 @@ add_task(function *test_processIncoming_decrypt_failed() {
|
|||
do_check_eq(observerSubject.failed, 4);
|
||||
|
||||
} finally {
|
||||
yield promiseClean(server);
|
||||
cleanAndGo(server);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -1369,7 +1358,7 @@ add_test(function test_uploadOutgoing_toEmptyServer() {
|
|||
});
|
||||
|
||||
|
||||
add_task(function *test_uploadOutgoing_failed() {
|
||||
add_test(function test_uploadOutgoing_failed() {
|
||||
_("SyncEngine._uploadOutgoing doesn't clear the tracker of objects that failed to upload.");
|
||||
|
||||
Service.identity.username = "foo";
|
||||
|
|
@ -1412,7 +1401,7 @@ add_task(function *test_uploadOutgoing_failed() {
|
|||
do_check_eq(engine._tracker.changedIDs['peppercorn'], PEPPERCORN_CHANGED);
|
||||
|
||||
engine.enabled = true;
|
||||
yield sync_engine_and_validate_telem(engine, true);
|
||||
engine.sync();
|
||||
|
||||
// Local timestamp has been set.
|
||||
do_check_true(engine.lastSyncLocal > 0);
|
||||
|
|
@ -1427,14 +1416,11 @@ add_task(function *test_uploadOutgoing_failed() {
|
|||
do_check_eq(engine._tracker.changedIDs['peppercorn'], PEPPERCORN_CHANGED);
|
||||
|
||||
} finally {
|
||||
yield promiseClean(server);
|
||||
cleanAndGo(server);
|
||||
}
|
||||
});
|
||||
|
||||
/* A couple of "functional" tests to ensure we split records into appropriate
|
||||
POST requests. More comprehensive unit-tests for this "batching" are in
|
||||
test_postqueue.js.
|
||||
*/
|
||||
|
||||
add_test(function test_uploadOutgoing_MAX_UPLOAD_RECORDS() {
|
||||
_("SyncEngine._uploadOutgoing uploads in batches of MAX_UPLOAD_RECORDS");
|
||||
|
||||
|
|
@ -1444,18 +1430,9 @@ add_test(function test_uploadOutgoing_MAX_UPLOAD_RECORDS() {
|
|||
// Let's count how many times the client posts to the server
|
||||
var noOfUploads = 0;
|
||||
collection.post = (function(orig) {
|
||||
return function(data, request) {
|
||||
// This test doesn't arrange for batch semantics - so we expect the
|
||||
// first request to come in with batch=true and the others to have no
|
||||
// batch related headers at all (as the first response did not provide
|
||||
// a batch ID)
|
||||
if (noOfUploads == 0) {
|
||||
do_check_eq(request.queryString, "batch=true");
|
||||
} else {
|
||||
do_check_eq(request.queryString, "");
|
||||
}
|
||||
return function() {
|
||||
noOfUploads++;
|
||||
return orig.call(this, data, request);
|
||||
return orig.apply(this, arguments);
|
||||
};
|
||||
}(collection.post));
|
||||
|
||||
|
|
@ -1500,44 +1477,6 @@ add_test(function test_uploadOutgoing_MAX_UPLOAD_RECORDS() {
|
|||
}
|
||||
});
|
||||
|
||||
add_test(function test_uploadOutgoing_largeRecords() {
|
||||
_("SyncEngine._uploadOutgoing throws on records larger than MAX_UPLOAD_BYTES");
|
||||
|
||||
Service.identity.username = "foo";
|
||||
let collection = new ServerCollection();
|
||||
|
||||
let engine = makeRotaryEngine();
|
||||
engine.allowSkippedRecord = false;
|
||||
engine._store.items["large-item"] = "Y".repeat(MAX_UPLOAD_BYTES*2);
|
||||
engine._tracker.addChangedID("large-item", 0);
|
||||
collection.insert("large-item");
|
||||
|
||||
|
||||
let meta_global = Service.recordManager.set(engine.metaURL,
|
||||
new WBORecord(engine.metaURL));
|
||||
meta_global.payload.engines = {rotary: {version: engine.version,
|
||||
syncID: engine.syncID}};
|
||||
|
||||
let server = sync_httpd_setup({
|
||||
"/1.1/foo/storage/rotary": collection.handler()
|
||||
});
|
||||
|
||||
let syncTesting = new SyncTestingInfrastructure(server);
|
||||
|
||||
try {
|
||||
engine._syncStartup();
|
||||
let error = null;
|
||||
try {
|
||||
engine._uploadOutgoing();
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
||||
ok(!!error);
|
||||
} finally {
|
||||
cleanAndGo(server);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
add_test(function test_syncFinish_noDelete() {
|
||||
_("SyncEngine._syncFinish resets tracker's score");
|
||||
|
|
@ -1667,7 +1606,7 @@ add_test(function test_syncFinish_deleteLotsInBatches() {
|
|||
});
|
||||
|
||||
|
||||
add_task(function *test_sync_partialUpload() {
|
||||
add_test(function test_sync_partialUpload() {
|
||||
_("SyncEngine.sync() keeps changedIDs that couldn't be uploaded.");
|
||||
|
||||
Service.identity.username = "foo";
|
||||
|
|
@ -1715,12 +1654,11 @@ add_task(function *test_sync_partialUpload() {
|
|||
engine.enabled = true;
|
||||
let error;
|
||||
try {
|
||||
yield sync_engine_and_validate_telem(engine, true);
|
||||
engine.sync();
|
||||
} catch (ex) {
|
||||
error = ex;
|
||||
}
|
||||
|
||||
ok(!!error);
|
||||
do_check_true(!!error);
|
||||
|
||||
// The timestamp has been updated.
|
||||
do_check_true(engine.lastSyncLocal > 456);
|
||||
|
|
@ -1738,7 +1676,7 @@ add_task(function *test_sync_partialUpload() {
|
|||
}
|
||||
|
||||
} finally {
|
||||
yield promiseClean(server);
|
||||
cleanAndGo(server);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue