services/sync/tests/unit/test_bookmark_engine.js

Wed, 31 Dec 2014 07:22:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:22:50 +0100
branch
TOR_BUG_3246
changeset 4
fc2d59ddac77
permissions
-rw-r--r--

Correct previous dual key logic pending first delivery installment.

     1 /* Any copyright is dedicated to the Public Domain.
     2    http://creativecommons.org/publicdomain/zero/1.0/ */
     4 Cu.import("resource://gre/modules/PlacesUtils.jsm");
     5 Cu.import("resource://gre/modules/BookmarkJSONUtils.jsm");
     6 Cu.import("resource://services-common/async.js");
     7 Cu.import("resource://gre/modules/Log.jsm");
     8 Cu.import("resource://services-sync/engines.js");
     9 Cu.import("resource://services-sync/engines/bookmarks.js");
    10 Cu.import("resource://services-sync/service.js");
    11 Cu.import("resource://services-sync/util.js");
    12 Cu.import("resource://testing-common/services/sync/utils.js");
    13 Cu.import("resource://gre/modules/Promise.jsm");
    15 Service.engineManager.register(BookmarksEngine);
    17 add_test(function bad_record_allIDs() {
    18   let server = new SyncServer();
    19   server.start();
    20   let syncTesting = new SyncTestingInfrastructure(server.server);
    22   _("Ensure that bad Places queries don't cause an error in getAllIDs.");
    23   let engine = new BookmarksEngine(Service);
    24   let store = engine._store;
    25   let badRecordID = PlacesUtils.bookmarks.insertBookmark(
    26       PlacesUtils.bookmarks.toolbarFolder,
    27       Utils.makeURI("place:folder=1138"),
    28       PlacesUtils.bookmarks.DEFAULT_INDEX,
    29       null);
    31   do_check_true(badRecordID > 0);
    32   _("Record is " + badRecordID);
    33   _("Type: " + PlacesUtils.bookmarks.getItemType(badRecordID));
    35   _("Fetching children.");
    36   store._getChildren("toolbar", {});
    38   _("Fetching all IDs.");
    39   let all = store.getAllIDs();
    41   _("All IDs: " + JSON.stringify(all));
    42   do_check_true("menu" in all);
    43   do_check_true("toolbar" in all);
    45   _("Clean up.");
    46   PlacesUtils.bookmarks.removeItem(badRecordID);
    47   server.stop(run_next_test);
    48 });
    50 add_test(function test_ID_caching() {
    51   let server = new SyncServer();
    52   server.start();
    53   let syncTesting = new SyncTestingInfrastructure(server.server);
    55   _("Ensure that Places IDs are not cached.");
    56   let engine = new BookmarksEngine(Service);
    57   let store = engine._store;
    58   _("All IDs: " + JSON.stringify(store.getAllIDs()));
    60   let mobileID = store.idForGUID("mobile");
    61   _("Change the GUID for that item, and drop the mobile anno.");
    62   store._setGUID(mobileID, "abcdefghijkl");
    63   PlacesUtils.annotations.removeItemAnnotation(mobileID, "mobile/bookmarksRoot");
    65   let err;
    66   let newMobileID;
    68   // With noCreate, we don't find an entry.
    69   try {
    70     newMobileID = store.idForGUID("mobile", true);
    71     _("New mobile ID: " + newMobileID);
    72   } catch (ex) {
    73     err = ex;
    74     _("Error: " + Utils.exceptionStr(err));
    75   }
    77   do_check_true(!err);
    79   // With !noCreate, lookup works, and it's different.
    80   newMobileID = store.idForGUID("mobile", false);
    81   _("New mobile ID: " + newMobileID);
    82   do_check_true(!!newMobileID);
    83   do_check_neq(newMobileID, mobileID);
    85   // And it's repeatable, even with creation enabled.
    86   do_check_eq(newMobileID, store.idForGUID("mobile", false));
    88   do_check_eq(store.GUIDForId(mobileID), "abcdefghijkl");
    89   server.stop(run_next_test);
    90 });
    92 function serverForFoo(engine) {
    93   return serverForUsers({"foo": "password"}, {
    94     meta: {global: {engines: {bookmarks: {version: engine.version,
    95                                           syncID: engine.syncID}}}},
    96     bookmarks: {}
    97   });
    98 }
   100 add_test(function test_processIncoming_error_orderChildren() {
   101   _("Ensure that _orderChildren() is called even when _processIncoming() throws an error.");
   103   let engine = new BookmarksEngine(Service);
   104   let store  = engine._store;
   105   let server = serverForFoo(engine);
   106   new SyncTestingInfrastructure(server.server);
   108   let collection = server.user("foo").collection("bookmarks");
   110   try {
   112     let folder1_id = PlacesUtils.bookmarks.createFolder(
   113       PlacesUtils.bookmarks.toolbarFolder, "Folder 1", 0);
   114     let folder1_guid = store.GUIDForId(folder1_id);
   116     let fxuri = Utils.makeURI("http://getfirefox.com/");
   117     let tburi = Utils.makeURI("http://getthunderbird.com/");
   119     let bmk1_id = PlacesUtils.bookmarks.insertBookmark(
   120       folder1_id, fxuri, PlacesUtils.bookmarks.DEFAULT_INDEX, "Get Firefox!");
   121     let bmk1_guid = store.GUIDForId(bmk1_id);
   122     let bmk2_id = PlacesUtils.bookmarks.insertBookmark(
   123       folder1_id, tburi, PlacesUtils.bookmarks.DEFAULT_INDEX, "Get Thunderbird!");
   124     let bmk2_guid = store.GUIDForId(bmk2_id);
   126     // Create a server record for folder1 where we flip the order of
   127     // the children.
   128     let folder1_payload = store.createRecord(folder1_guid).cleartext;
   129     folder1_payload.children.reverse();
   130     collection.insert(folder1_guid, encryptPayload(folder1_payload));
   132     // Create a bogus record that when synced down will provoke a
   133     // network error which in turn provokes an exception in _processIncoming.
   134     const BOGUS_GUID = "zzzzzzzzzzzz";
   135     let bogus_record = collection.insert(BOGUS_GUID, "I'm a bogus record!");
   136     bogus_record.get = function get() {
   137       throw "Sync this!";
   138     };
   140     // Make the 10 minutes old so it will only be synced in the toFetch phase.
   141     bogus_record.modified = Date.now() / 1000 - 60 * 10;
   142     engine.lastSync = Date.now() / 1000 - 60;
   143     engine.toFetch = [BOGUS_GUID];
   145     let error;
   146     try {
   147       engine.sync();
   148     } catch(ex) {
   149       error = ex;
   150     }
   151     do_check_true(!!error);
   153     // Verify that the bookmark order has been applied.
   154     let new_children = store.createRecord(folder1_guid).children;
   155     do_check_eq(new_children.length, 2);
   156     do_check_eq(new_children[0], folder1_payload.children[0]);
   157     do_check_eq(new_children[1], folder1_payload.children[1]);
   159     do_check_eq(PlacesUtils.bookmarks.getItemIndex(bmk1_id), 1);
   160     do_check_eq(PlacesUtils.bookmarks.getItemIndex(bmk2_id), 0);
   162   } finally {
   163     store.wipe();
   164     Svc.Prefs.resetBranch("");
   165     Service.recordManager.clearCache();
   166     server.stop(run_next_test);
   167   }
   168 });
   170 add_task(function test_restorePromptsReupload() {
   171   _("Ensure that restoring from a backup will reupload all records.");
   172   let engine = new BookmarksEngine(Service);
   173   let store  = engine._store;
   174   let server = serverForFoo(engine);
   175   new SyncTestingInfrastructure(server.server);
   177   let collection = server.user("foo").collection("bookmarks");
   179   Svc.Obs.notify("weave:engine:start-tracking");   // We skip usual startup...
   181   try {
   183     let folder1_id = PlacesUtils.bookmarks.createFolder(
   184       PlacesUtils.bookmarks.toolbarFolder, "Folder 1", 0);
   185     let folder1_guid = store.GUIDForId(folder1_id);
   186     _("Folder 1: " + folder1_id + ", " + folder1_guid);
   188     let fxuri = Utils.makeURI("http://getfirefox.com/");
   189     let tburi = Utils.makeURI("http://getthunderbird.com/");
   191     _("Create a single record.");
   192     let bmk1_id = PlacesUtils.bookmarks.insertBookmark(
   193       folder1_id, fxuri, PlacesUtils.bookmarks.DEFAULT_INDEX, "Get Firefox!");
   194     let bmk1_guid = store.GUIDForId(bmk1_id);
   195     _("Get Firefox!: " + bmk1_id + ", " + bmk1_guid);
   198     let dirSvc = Cc["@mozilla.org/file/directory_service;1"]
   199       .getService(Ci.nsIProperties);
   201     let backupFile = dirSvc.get("TmpD", Ci.nsILocalFile);
   203     _("Make a backup.");
   204     backupFile.append("t_b_e_" + Date.now() + ".json");
   206     _("Backing up to file " + backupFile.path);
   207     backupFile.create(Ci.nsILocalFile.NORMAL_FILE_TYPE, 0600);
   208     yield BookmarkJSONUtils.exportToFile(backupFile);
   210     _("Create a different record and sync.");
   211     let bmk2_id = PlacesUtils.bookmarks.insertBookmark(
   212       folder1_id, tburi, PlacesUtils.bookmarks.DEFAULT_INDEX, "Get Thunderbird!");
   213     let bmk2_guid = store.GUIDForId(bmk2_id);
   214     _("Get Thunderbird!: " + bmk2_id + ", " + bmk2_guid);
   216     PlacesUtils.bookmarks.removeItem(bmk1_id);
   218     let error;
   219     try {
   220       engine.sync();
   221     } catch(ex) {
   222       error = ex;
   223       _("Got error: " + Utils.exceptionStr(ex));
   224     }
   225     do_check_true(!error);
   227     _("Verify that there's only one bookmark on the server, and it's Thunderbird.");
   228     // Of course, there's also the Bookmarks Toolbar and Bookmarks Menu...
   229     let wbos = collection.keys(function (id) {
   230       return ["menu", "toolbar", "mobile", folder1_guid].indexOf(id) == -1;
   231     });
   232     do_check_eq(wbos.length, 1);
   233     do_check_eq(wbos[0], bmk2_guid);
   235     _("Now restore from a backup.");
   236     yield BookmarkJSONUtils.importFromFile(backupFile, true);
   238     _("Ensure we have the bookmarks we expect locally.");
   239     let guids = store.getAllIDs();
   240     _("GUIDs: " + JSON.stringify(guids));
   241     let found = false;
   242     let count = 0;
   243     let newFX;
   244     for (let guid in guids) {
   245       count++;
   246       let id = store.idForGUID(guid, true);
   247       // Only one bookmark, so _all_ should be Firefox!
   248       if (PlacesUtils.bookmarks.getItemType(id) == PlacesUtils.bookmarks.TYPE_BOOKMARK) {
   249         let uri = PlacesUtils.bookmarks.getBookmarkURI(id);
   250         _("Found URI " + uri.spec + " for GUID " + guid);
   251         do_check_eq(uri.spec, fxuri.spec);
   252         newFX = guid;   // Save the new GUID after restore.
   253         found = true;   // Only runs if the above check passes.
   254       }
   255     }
   256     _("We found it: " + found);
   257     do_check_true(found);
   259     _("Have the correct number of IDs locally, too.");
   260     do_check_eq(count, ["menu", "toolbar", folder1_id, bmk1_id].length);
   262     _("Sync again. This'll wipe bookmarks from the server.");
   263     try {
   264       engine.sync();
   265     } catch(ex) {
   266       error = ex;
   267       _("Got error: " + Utils.exceptionStr(ex));
   268     }
   269     do_check_true(!error);
   271     _("Verify that there's only one bookmark on the server, and it's Firefox.");
   272     // Of course, there's also the Bookmarks Toolbar and Bookmarks Menu...
   273     let payloads     = server.user("foo").collection("bookmarks").payloads();
   274     let bookmarkWBOs = payloads.filter(function (wbo) {
   275                          return wbo.type == "bookmark";
   276                        });
   277     let folderWBOs   = payloads.filter(function (wbo) {
   278                          return ((wbo.type == "folder") &&
   279                                  (wbo.id   != "menu") &&
   280                                  (wbo.id   != "toolbar"));
   281                        });
   283     do_check_eq(bookmarkWBOs.length, 1);
   284     do_check_eq(bookmarkWBOs[0].id, newFX);
   285     do_check_eq(bookmarkWBOs[0].bmkUri, fxuri.spec);
   286     do_check_eq(bookmarkWBOs[0].title, "Get Firefox!");
   288     _("Our old friend Folder 1 is still in play.");
   289     do_check_eq(folderWBOs.length, 1);
   290     do_check_eq(folderWBOs[0].title, "Folder 1");
   292   } finally {
   293     store.wipe();
   294     Svc.Prefs.resetBranch("");
   295     Service.recordManager.clearCache();
   296     let deferred = Promise.defer();
   297     server.stop(deferred.resolve);
   298     yield deferred.promise;
   299   }
   300 });
   302 function FakeRecord(constructor, r) {
   303   constructor.call(this, "bookmarks", r.id);
   304   for (let x in r) {
   305     this[x] = r[x];
   306   }
   307 }
   309 // Bug 632287.
   310 add_test(function test_mismatched_types() {
   311   _("Ensure that handling a record that changes type causes deletion " +
   312     "then re-adding.");
   314   let oldRecord = {
   315     "id": "l1nZZXfB8nC7",
   316     "type":"folder",
   317     "parentName":"Bookmarks Toolbar",
   318     "title":"Innerst i Sneglehode",
   319     "description":null,
   320     "parentid": "toolbar"
   321   };
   323   let newRecord = {
   324     "id": "l1nZZXfB8nC7",
   325     "type":"livemark",
   326     "siteUri":"http://sneglehode.wordpress.com/",
   327     "feedUri":"http://sneglehode.wordpress.com/feed/",
   328     "parentName":"Bookmarks Toolbar",
   329     "title":"Innerst i Sneglehode",
   330     "description":null,
   331     "children":
   332       ["HCRq40Rnxhrd", "YeyWCV1RVsYw", "GCceVZMhvMbP", "sYi2hevdArlF",
   333        "vjbZlPlSyGY8", "UtjUhVyrpeG6", "rVq8WMG2wfZI", "Lx0tcy43ZKhZ",
   334        "oT74WwV8_j4P", "IztsItWVSo3-"],
   335     "parentid": "toolbar"
   336   };
   338   let engine = new BookmarksEngine(Service);
   339   let store  = engine._store;
   340   let server = serverForFoo(engine);
   341   new SyncTestingInfrastructure(server.server);
   343   _("GUID: " + store.GUIDForId(6, true));
   345   try {
   346     let bms = PlacesUtils.bookmarks;
   347     let oldR = new FakeRecord(BookmarkFolder, oldRecord);
   348     let newR = new FakeRecord(Livemark, newRecord);
   349     oldR._parent = PlacesUtils.bookmarks.toolbarFolder;
   350     newR._parent = PlacesUtils.bookmarks.toolbarFolder;
   352     store.applyIncoming(oldR);
   353     _("Applied old. It's a folder.");
   354     let oldID = store.idForGUID(oldR.id);
   355     _("Old ID: " + oldID);
   356     do_check_eq(bms.getItemType(oldID), bms.TYPE_FOLDER);
   357     do_check_false(PlacesUtils.annotations
   358                               .itemHasAnnotation(oldID, PlacesUtils.LMANNO_FEEDURI));
   360     store.applyIncoming(newR);
   361     let newID = store.idForGUID(newR.id);
   362     _("New ID: " + newID);
   364     _("Applied new. It's a livemark.");
   365     do_check_eq(bms.getItemType(newID), bms.TYPE_FOLDER);
   366     do_check_true(PlacesUtils.annotations
   367                              .itemHasAnnotation(newID, PlacesUtils.LMANNO_FEEDURI));
   369   } finally {
   370     store.wipe();
   371     Svc.Prefs.resetBranch("");
   372     Service.recordManager.clearCache();
   373     server.stop(run_next_test);
   374   }
   375 });
   377 add_test(function test_bookmark_guidMap_fail() {
   378   _("Ensure that failures building the GUID map cause early death.");
   380   let engine = new BookmarksEngine(Service);
   381   let store = engine._store;
   383   let store  = engine._store;
   384   let server = serverForFoo(engine);
   385   let coll   = server.user("foo").collection("bookmarks");
   386   new SyncTestingInfrastructure(server.server);
   388   // Add one item to the server.
   389   let itemID = PlacesUtils.bookmarks.createFolder(
   390     PlacesUtils.bookmarks.toolbarFolder, "Folder 1", 0);
   391   let itemGUID    = store.GUIDForId(itemID);
   392   let itemPayload = store.createRecord(itemGUID).cleartext;
   393   coll.insert(itemGUID, encryptPayload(itemPayload));
   395   engine.lastSync = 1;   // So we don't back up.
   397   // Make building the GUID map fail.
   398   store.getAllIDs = function () { throw "Nooo"; };
   400   // Ensure that we throw when accessing _guidMap.
   401   engine._syncStartup();
   402   _("No error.");
   403   do_check_false(engine._guidMapFailed);
   405   _("We get an error if building _guidMap fails in use.");
   406   let err;
   407   try {
   408     _(engine._guidMap);
   409   } catch (ex) {
   410     err = ex;
   411   }
   412   do_check_eq(err.code, Engine.prototype.eEngineAbortApplyIncoming);
   413   do_check_eq(err.cause, "Nooo");
   415   _("We get an error and abort during processIncoming.");
   416   err = undefined;
   417   try {
   418     engine._processIncoming();
   419   } catch (ex) {
   420     err = ex;
   421   }
   422   do_check_eq(err, "Nooo");
   424   server.stop(run_next_test);
   425 });
   427 add_test(function test_bookmark_is_taggable() {
   428   let engine = new BookmarksEngine(Service);
   429   let store = engine._store;
   431   do_check_true(store.isTaggable("bookmark"));
   432   do_check_true(store.isTaggable("microsummary"));
   433   do_check_true(store.isTaggable("query"));
   434   do_check_false(store.isTaggable("folder"));
   435   do_check_false(store.isTaggable("livemark"));
   436   do_check_false(store.isTaggable(null));
   437   do_check_false(store.isTaggable(undefined));
   438   do_check_false(store.isTaggable(""));
   440   run_next_test();
   441 });
   443 add_test(function test_bookmark_tag_but_no_uri() {
   444   _("Ensure that a bookmark record with tags, but no URI, doesn't throw an exception.");
   446   let engine = new BookmarksEngine(Service);
   447   let store = engine._store;
   449   // We're simply checking that no exception is thrown, so
   450   // no actual checks in this test.
   452   store._tagURI(null, ["foo"]);
   453   store._tagURI(null, null);
   454   store._tagURI(Utils.makeURI("about:fake"), null);
   456   let record = {
   457     _parent:     PlacesUtils.bookmarks.toolbarFolder,
   458     id:          Utils.makeGUID(),
   459     description: "",
   460     tags:        ["foo"],
   461     title:       "Taggy tag",
   462     type:        "folder"
   463   };
   465   // Because update() walks the cleartext.
   466   record.cleartext = record;
   468   store.create(record);
   469   record.tags = ["bar"];
   470   store.update(record);
   472   run_next_test();
   473 });
   475 add_test(function test_misreconciled_root() {
   476   _("Ensure that we don't reconcile an arbitrary record with a root.");
   478   let engine = new BookmarksEngine(Service);
   479   let store = engine._store;
   480   let server = serverForFoo(engine);
   482   // Log real hard for this test.
   483   store._log.trace = store._log.debug;
   484   engine._log.trace = engine._log.debug;
   486   engine._syncStartup();
   488   // Let's find out where the toolbar is right now.
   489   let toolbarBefore = store.createRecord("toolbar", "bookmarks");
   490   let toolbarIDBefore = store.idForGUID("toolbar");
   491   do_check_neq(-1, toolbarIDBefore);
   493   let parentGUIDBefore = toolbarBefore.parentid;
   494   let parentIDBefore = store.idForGUID(parentGUIDBefore);
   495   do_check_neq(-1, parentIDBefore);
   496   do_check_eq("string", typeof(parentGUIDBefore));
   498   _("Current parent: " + parentGUIDBefore + " (" + parentIDBefore + ").");
   500   let to_apply = {
   501     id: "zzzzzzzzzzzz",
   502     type: "folder",
   503     title: "Bookmarks Toolbar",
   504     description: "Now you're for it.",
   505     parentName: "",
   506     parentid: "mobile",   // Why not?
   507     children: [],
   508   };
   510   let rec = new FakeRecord(BookmarkFolder, to_apply);
   511   let encrypted = encryptPayload(rec.cleartext);
   512   encrypted.decrypt = function () {
   513     for (let x in rec) {
   514       encrypted[x] = rec[x];
   515     }
   516   };
   518   _("Applying record.");
   519   engine._processIncoming({
   520     get: function () {
   521       this.recordHandler(encrypted);
   522       return {success: true}
   523     },
   524   });
   526   // Ensure that afterwards, toolbar is still there.
   527   // As of 2012-12-05, this only passes because Places doesn't use "toolbar" as
   528   // the real GUID, instead using a generated one. Sync does the translation.
   529   let toolbarAfter = store.createRecord("toolbar", "bookmarks");
   530   let parentGUIDAfter = toolbarAfter.parentid;
   531   let parentIDAfter = store.idForGUID(parentGUIDAfter);
   532   do_check_eq(store.GUIDForId(toolbarIDBefore), "toolbar");
   533   do_check_eq(parentGUIDBefore, parentGUIDAfter);
   534   do_check_eq(parentIDBefore, parentIDAfter);
   536   server.stop(run_next_test);
   537 });
   539 function run_test() {
   540   initTestLogging("Trace");
   541   generateNewKeys(Service.collectionKeys);
   542   run_next_test();
   543 }

mercurial