diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
commit | 2aa4a82499d4becd2284cdb482213d541b8804dd (patch) | |
tree | b80bf8bf13c3766139fbacc530efd0dd9d54394c /services/sync/tests/unit/test_bookmark_validator.js | |
parent | Initial commit. (diff) | |
download | firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip |
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'services/sync/tests/unit/test_bookmark_validator.js')
-rw-r--r-- | services/sync/tests/unit/test_bookmark_validator.js | 466 |
1 files changed, 466 insertions, 0 deletions
diff --git a/services/sync/tests/unit/test_bookmark_validator.js b/services/sync/tests/unit/test_bookmark_validator.js new file mode 100644 index 0000000000..2b8029d4b5 --- /dev/null +++ b/services/sync/tests/unit/test_bookmark_validator.js @@ -0,0 +1,466 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +const { BookmarkValidator } = ChromeUtils.import( + "resource://services-sync/bookmark_validator.js" +); + +function run_test() { + do_get_profile(); + run_next_test(); +} + +async function inspectServerRecords(data) { + let validator = new BookmarkValidator(); + return validator.inspectServerRecords(data); +} + +async function compareServerWithClient(server, client) { + let validator = new BookmarkValidator(); + return validator.compareServerWithClient(server, client); +} + +add_task(async function test_isr_rootOnServer() { + let c = await inspectServerRecords([ + { + id: "places", + type: "folder", + children: [], + }, + ]); + ok(c.problemData.rootOnServer); +}); + +add_task(async function test_isr_empty() { + let c = await inspectServerRecords([]); + ok(!c.problemData.rootOnServer); + notEqual(c.root, null); +}); + +add_task(async function test_isr_cycles() { + let c = ( + await 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].includes("A")); + ok(c.cycles[0].includes("B")); +}); + +add_task(async function test_isr_orphansMultiParents() { + let c = ( + await 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.includes("B")); + ok(c.multipleParents[0].parents.includes("C")); +}); + +add_task(async function test_isr_orphansMultiParents2() { + let c = ( + await 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); +}); + +add_task(async function test_isr_deletedParents() { + let c = ( + await inspectServerRecords([ + { id: "A", type: "bookmark", parentid: "B" }, + { id: "C", type: "folder", parentid: "places", children: ["A"] }, + { id: "B", type: "item", deleted: true }, + ]) + ).problemData; + deepEqual(c.deletedParents, ["A"]); +}); + +add_task(async function test_isr_badChildren() { + let c = ( + await 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"]); +}); + +add_task(async function test_isr_parentChildMismatches() { + let c = ( + await inspectServerRecords([ + { id: "A", type: "folder", parentid: "places", children: [] }, + { id: "B", type: "bookmark", parentid: "A" }, + ]) + ).problemData; + deepEqual(c.parentChildMismatches, [{ parent: "A", child: "B" }]); +}); + +add_task(async function test_isr_duplicatesAndMissingIDs() { + let c = ( + await 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"]); +}); + +add_task(async function test_isr_duplicateChildren() { + let c = ( + await inspectServerRecords([ + { id: "A", type: "folder", parentid: "places", children: ["B", "B"] }, + { id: "B", type: "bookmark", parentid: "A" }, + ]) + ).problemData; + deepEqual(c.duplicateChildren, ["A"]); +}); + +// 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, + type: "text/x-moz-place", + uri: "place:type=6&sort=14&maxResults=10", + }, + ], + }, + ], + }; + return { server, client }; +} + +add_task(async function test_cswc_valid() { + let { server, client } = getDummyServerAndClient(); + + let c = (await compareServerWithClient(server, client)).problemData; + equal(c.clientMissing.length, 0); + equal(c.serverMissing.length, 0); + equal(c.differences.length, 0); +}); + +add_task(async function test_cswc_serverMissing() { + let { server, client } = getDummyServerAndClient(); + // remove c + server.pop(); + server[0].children.pop(); + + let c = (await compareServerWithClient(server, client)).problemData; + deepEqual(c.serverMissing, ["cccccccccccc"]); + equal(c.clientMissing.length, 0); + deepEqual(c.structuralDifferences, [ + { id: "menu", differences: ["childGUIDs"] }, + ]); +}); + +add_task(async function test_cswc_clientMissing() { + let { server, client } = getDummyServerAndClient(); + client.children[0].children.pop(); + + let c = (await compareServerWithClient(server, client)).problemData; + deepEqual(c.clientMissing, ["cccccccccccc"]); + equal(c.serverMissing.length, 0); + deepEqual(c.structuralDifferences, [ + { id: "menu", differences: ["childGUIDs"] }, + ]); +}); + +add_task(async function test_cswc_differences() { + { + let { server, client } = getDummyServerAndClient(); + client.children[0].children[0].title = "asdf"; + let c = (await 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 = (await compareServerWithClient(server, client)).problemData; + equal(c.clientMissing.length, 0); + equal(c.serverMissing.length, 0); + deepEqual(c.differences, [{ id: "cccccccccccc", differences: ["type"] }]); + } +}); + +add_task(async function test_cswc_differentURLs() { + let { server, client } = getDummyServerAndClient(); + client.children[0].children.push( + { + guid: "dddddddddddd", + title: "Tag query", + type: "text/x-moz-place", + uri: "place:type=7&folder=80", + }, + { + guid: "eeeeeeeeeeee", + title: "Firefox", + type: "text/x-moz-place", + uri: "http://getfirefox.com", + } + ); + server.push( + { + id: "dddddddddddd", + parentid: "menu", + parentName: "foo", + title: "Tag query", + type: "query", + folderName: "taggy", + bmkUri: "place:type=7&folder=90", + }, + { + id: "eeeeeeeeeeee", + parentid: "menu", + parentName: "foo", + title: "Firefox", + type: "bookmark", + bmkUri: "https://mozilla.org/firefox", + } + ); + + let c = (await compareServerWithClient(server, client)).problemData; + equal(c.differences.length, 1); + deepEqual(c.differences, [ + { + id: "eeeeeeeeeeee", + differences: ["bmkUri"], + }, + ]); +}); + +add_task(async function test_cswc_serverUnexpected() { + let { server, client } = getDummyServerAndClient(); + client.children.push({ + guid: "dddddddddddd", + title: "", + id: 2000, + type: "text/x-moz-place-container", + children: [ + { + guid: "eeeeeeeeeeee", + title: "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 = (await compareServerWithClient(server, client)).problemData; + equal(c.clientMissing.length, 0); + equal(c.serverMissing.length, 0); + equal(c.serverUnexpected.length, 2); + deepEqual(c.serverUnexpected, ["dddddddddddd", "eeeeeeeeeeee"]); +}); + +add_task(async function test_cswc_clientCycles() { + await PlacesUtils.bookmarks.insertTree({ + guid: PlacesUtils.bookmarks.menuGuid, + children: [ + { + // A query for the menu, referenced by its local ID instead of + // `BOOKMARKS_MENU`. This should be reported as a cycle. + guid: "dddddddddddd", + url: `place:folder=${PlacesUtils.bookmarksMenuFolderId}`, + title: "Bookmarks Menu", + }, + { + // A query that references the menu, but excludes itself, so it can't + // form a cycle. + guid: "iiiiiiiiiiii", + url: + `place:folder=BOOKMARKS_MENU&folder=UNFILED_BOOKMARKS&` + + `folder=TOOLBAR&queryType=1&sort=12&maxResults=10&` + + `excludeQueries=1`, + title: "Recently Bookmarked", + }, + ], + }); + + await PlacesUtils.bookmarks.insertTree({ + guid: PlacesUtils.bookmarks.toolbarGuid, + children: [ + { + guid: "eeeeeeeeeeee", + type: PlacesUtils.bookmarks.TYPE_FOLDER, + children: [ + { + // A query for the toolbar in a subfolder. This should still be reported + // as a cycle. + guid: "ffffffffffff", + url: "place:folder=TOOLBAR&sort=3", + title: "Bookmarks Toolbar", + }, + ], + }, + ], + }); + + await PlacesUtils.bookmarks.insertTree({ + guid: PlacesUtils.bookmarks.unfiledGuid, + children: [ + { + // A query for the menu. This shouldn't be reported as a cycle, since it + // references a different root. + guid: "gggggggggggg", + url: "place:folder=BOOKMARKS_MENU&sort=5", + title: "Bookmarks Menu", + }, + ], + }); + + await PlacesUtils.bookmarks.insertTree({ + guid: PlacesUtils.bookmarks.mobileGuid, + children: [ + { + // A query referencing multiple roots, one of which forms a cycle by + // referencing mobile. This is extremely unlikely, but it's cheap to + // detect, so we still report it. + guid: "hhhhhhhhhhhh", + url: + "place:folder=TOOLBAR&folder=MOBILE_BOOKMARKS&folder=UNFILED_BOOKMARKS&sort=1", + title: "Toolbar, Mobile, Unfiled", + }, + ], + }); + + let clientTree = await PlacesUtils.promiseBookmarksTree("", { + includeItemIds: true, + }); + + let c = (await compareServerWithClient([], clientTree)).problemData; + deepEqual(c.clientCycles, [ + ["menu", "dddddddddddd"], + ["toolbar", "eeeeeeeeeeee", "ffffffffffff"], + ["mobile", "hhhhhhhhhhhh"], + ]); +}); + +async function validationPing(server, client, duration) { + let pingPromise = wait_for_ping(() => {}, true); // Allow "failing" pings, since having validation info indicates failure. + // 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 { problemData } = await validator.compareServerWithClient(server, client); + let data = { + // We fake duration and version just so that we can verify they"re passed through. + took: duration, + version: validator.version, + checked: server.length, + problems: problemData.getSummary(true), + }; + Svc.Obs.notify("weave:engine:validate:finish", data, "bookmarks"); + Svc.Obs.notify("weave:service:sync:finish"); + return pingPromise; +} + +add_task(async function test_telemetry_integration() { + let { server, client } = getDummyServerAndClient(); + // remove "c" + server.pop(); + server[0].children.pop(); + const duration = 50; + let ping = await 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(a.name).localeCompare(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 }, + ]); +}); |