Bug 1317223 (part 6) - integration tests for the bookmark repair requestor and responder. r?tcsc draft
authorMark Hammond <mhammond@skippinet.com.au>
Mon, 06 Feb 2017 10:54:08 -0800
changeset 486828 9e5d57c24027277f6305830cde4d2d374a976dc6
parent 486827 cfe0ab4baea54219059a0f05fcc23164f794039e
child 546322 f7cfc89903f5433aec7ad96fc78d792baca9cc8c
push id46067
push usermhammond@skippinet.com.au
push dateMon, 20 Feb 2017 02:02:46 +0000
reviewerstcsc
bugs1317223
milestone54.0a1
Bug 1317223 (part 6) - integration tests for the bookmark repair requestor and responder. r?tcsc This is an attempt at a true integration tests without the use of mocks. It's tricky and hacky to do such tests - I suspect this patch is probably misguided and that TPS might be a better environment for these kinds of tests - I'm flagging Thom for review here primarily to see what he things about using TPS instead of this. MozReview-Commit-ID: 8us4v6Y21oM
services/sync/tests/unit/test_bookmark_repair.js
services/sync/tests/unit/xpcshell.ini
new file mode 100644
--- /dev/null
+++ b/services/sync/tests/unit/test_bookmark_repair.js
@@ -0,0 +1,151 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests the bookmark repair requestor and responder end-to-end (ie, without
+// many mocks)
+Cu.import("resource://gre/modules/PlacesUtils.jsm");
+Cu.import("resource://gre/modules/Log.jsm");
+Cu.import("resource://services-sync/constants.js");
+Cu.import("resource://services-sync/service.js");
+Cu.import("resource://services-sync/engines/clients.js");
+Cu.import("resource://services-sync/engines/bookmarks.js");
+Cu.import("resource://testing-common/services/sync/utils.js");
+
+initTestLogging("Trace");
+Log.repository.getLogger("Sync.Engine.Bookmarks").level = Log.Level.Trace
+Log.repository.getLogger("Sync.Engine.Clients").level = Log.Level.Trace
+Log.repository.getLogger("Sqlite").level = Log.Level.Info; // less noisy
+
+const bms = PlacesUtils.bookmarks;
+
+//Service.engineManager.register(BookmarksEngine);
+let clientsEngine = Service.clientsEngine;
+let bookmarksEngine = Service.engineManager.get("bookmarks");
+
+generateNewKeys(Service.collectionKeys);
+
+function createFolder(parentId, title) {
+  let id = bms.createFolder(parentId, title, 0);
+  let guid = bookmarksEngine._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 = bookmarksEngine._store.GUIDForId(id);
+  return { id, guid };
+}
+
+async function promiseValidationDone(expected) {
+  // wait for a validation to complete.
+  let obs = promiseOneObserver("weave:engine:validate:finish");
+  let { subject: validationResult } = await obs;
+  // check the results - anything non-zero is checked against |expected|
+  let summary = validationResult.problems.getSummary();
+  let actual = summary.filter(({name, count}) => count);
+  actual.sort((a, b) => String(a.name).localeCompare(b.name));
+  expected.sort((a, b) => String(a.name).localeCompare(b.name));
+  deepEqual(actual, expected);
+};
+
+async function cleanup(server) {
+  bookmarksEngine._store.wipe();
+  clientsEngine._store.wipe();
+  Svc.Prefs.resetBranch("");
+  Service.recordManager.clearCache();
+  await promiseStopServer(server);
+}
+
+add_task(async function test_something() {
+  _("Ensure that a validation error triggers a repair request.");
+
+  let contents = {
+    meta: {
+      global: {
+        engines: {
+          clients: {
+            version: clientsEngine.version,
+            syncID: clientsEngine.syncID,
+          },
+          bookmarks: {
+            version: bookmarksEngine.version,
+            syncID: bookmarksEngine.syncID,
+          },
+        }
+      }
+    },
+    clients: {},
+    bookmarks: {},
+    crypto: {},
+  };
+  let server = serverForUsers({"foo": "password"}, contents);
+  await SyncTestingInfrastructure(server);
+
+  let user = server.user("foo");
+
+  let initialID = Service.clientsEngine.localID;
+  let remoteID = Utils.makeGUID();
+  try {
+
+    _("Syncing to initialize crypto etc.");
+    Service.sync();
+
+    _("Create remote client record");
+    server.insertWBO("foo", "clients", new ServerWBO(remoteID, encryptPayload({
+      id: remoteID,
+      name: "Remote client",
+      type: "desktop",
+      commands: [],
+      version: "53",
+      protocols: ["1.5"],
+    }), Date.now() / 1000));
+
+    // Create a couple of bookmarks.
+    let folderInfo = createFolder(bms.toolbarFolder, "Folder 1");
+    let bookmarkInfo = createBookmark(folderInfo.id, "http://getfirefox.com/", "Get Firefox!");
+
+    let validationPromise = promiseValidationDone([]);
+    _("Syncing.");
+    Service.sync();
+    // should have 2 clients
+    equal(clientsEngine.stats.numClients, 2)
+    await validationPromise;
+
+    // Now we will reach into the server and hard-delete the bookmark
+    user.collection("bookmarks").wbo(bookmarkInfo.guid).delete();
+    // And delete the bookmark, but cheat by telling places that Sync did
+    // it, so we don't end up with an orphan.
+    // and use SQL to hard-delete the bookmark from the store.
+    await bms.remove(bookmarkInfo.guid, {
+      source: bms.SOURCE_SYNC,
+    });
+    // sanity check we aren't going to sync this removal.
+    do_check_empty(bookmarksEngine.pullNewChanges());
+
+    // sync again - we should have a few problems...
+    _("Syncing again.");
+    validationPromise = promiseValidationDone([
+      {"name":"missingChildren","count":1},
+      {"name":"structuralDifferences","count":1},
+    ]);
+    Service.sync();
+    await validationPromise;
+
+    // We should have started a repair with our second client.
+    equal(clientsEngine.getClientCommands(remoteID).length, 1);
+    _("Syncing so the outgoing client command is sent.");
+    Service.sync();
+    equal(clientsEngine.getClientCommands(remoteID).length, 0);
+
+    // so now let's take over the role of that other client!
+    let remoteClientsEngine = Service.clientsEngine = new ClientEngine(Service);
+    remoteClientsEngine.localID = remoteID;
+    _("what could possibly go wrong?");
+    Service.sync();
+
+    // TODO - make the rest of this work!
+  } finally {
+    await cleanup(server);
+  }
+});
--- a/services/sync/tests/unit/xpcshell.ini
+++ b/services/sync/tests/unit/xpcshell.ini
@@ -131,16 +131,17 @@ tags = addons
 [test_bookmark_duping.js]
 [test_bookmark_engine.js]
 [test_bookmark_invalid.js]
 [test_bookmark_legacy_microsummaries_support.js]
 [test_bookmark_livemarks.js]
 [test_bookmark_order.js]
 [test_bookmark_places_query_rewriting.js]
 [test_bookmark_record.js]
+[test_bookmark_repair.js]
 [test_bookmark_repair_requestor.js]
 [test_bookmark_repair_responder.js]
 [test_bookmark_smart_bookmarks.js]
 [test_bookmark_store.js]
 # Too many intermittent "ASSERTION: thread pool wasn't shutdown: '!mPool'" (bug 804479)
 skip-if = debug
 [test_bookmark_tracker.js]
 requesttimeoutfactor = 4