test_bookmark_order.js (13080B)
1 /* Any copyright is dedicated to the Public Domain. 2 http://creativecommons.org/publicdomain/zero/1.0/ */ 3 4 _( 5 "Making sure after processing incoming bookmarks, they show up in the right order" 6 ); 7 const { Bookmark, BookmarkFolder } = ChromeUtils.importESModule( 8 "resource://services-sync/engines/bookmarks.sys.mjs" 9 ); 10 const { Weave } = ChromeUtils.importESModule( 11 "resource://services-sync/main.sys.mjs" 12 ); 13 const { Service } = ChromeUtils.importESModule( 14 "resource://services-sync/service.sys.mjs" 15 ); 16 17 async function serverForFoo(engine) { 18 await generateNewKeys(Service.collectionKeys); 19 20 let clientsEngine = Service.clientsEngine; 21 let clientsSyncID = await clientsEngine.resetLocalSyncID(); 22 let engineSyncID = await engine.resetLocalSyncID(); 23 return serverForUsers( 24 { foo: "password" }, 25 { 26 meta: { 27 global: { 28 syncID: Service.syncID, 29 storageVersion: STORAGE_VERSION, 30 engines: { 31 clients: { 32 version: clientsEngine.version, 33 syncID: clientsSyncID, 34 }, 35 [engine.name]: { 36 version: engine.version, 37 syncID: engineSyncID, 38 }, 39 }, 40 }, 41 }, 42 crypto: { 43 keys: encryptPayload({ 44 id: "keys", 45 // Generate a fake default key bundle to avoid resetting the client 46 // before the first sync. 47 default: [ 48 await Weave.Crypto.generateRandomKey(), 49 await Weave.Crypto.generateRandomKey(), 50 ], 51 }), 52 }, 53 [engine.name]: {}, 54 } 55 ); 56 } 57 58 async function resolveConflict( 59 engine, 60 collection, 61 timestamp, 62 buildTree, 63 message 64 ) { 65 let guids = { 66 // These items don't exist on the server. 67 fx: Utils.makeGUID(), 68 nightly: Utils.makeGUID(), 69 support: Utils.makeGUID(), 70 customize: Utils.makeGUID(), 71 72 // These exist on the server, but in a different order, and `res` 73 // has completely different children. 74 res: Utils.makeGUID(), 75 tb: Utils.makeGUID(), 76 77 // These don't exist locally. 78 bz: Utils.makeGUID(), 79 irc: Utils.makeGUID(), 80 mdn: Utils.makeGUID(), 81 }; 82 83 await PlacesUtils.bookmarks.insertTree({ 84 guid: PlacesUtils.bookmarks.menuGuid, 85 children: [ 86 { 87 guid: guids.fx, 88 title: "Get Firefox!", 89 url: "http://getfirefox.com/", 90 }, 91 { 92 guid: guids.res, 93 title: "Resources", 94 type: PlacesUtils.bookmarks.TYPE_FOLDER, 95 children: [ 96 { 97 guid: guids.nightly, 98 title: "Nightly", 99 url: "https://nightly.mozilla.org/", 100 }, 101 { 102 guid: guids.support, 103 title: "Support", 104 url: "https://support.mozilla.org/", 105 }, 106 { 107 guid: guids.customize, 108 title: "Customize", 109 url: "https://mozilla.org/firefox/customize/", 110 }, 111 ], 112 }, 113 { 114 title: "Get Thunderbird!", 115 guid: guids.tb, 116 url: "http://getthunderbird.com/", 117 }, 118 ], 119 }); 120 121 let serverRecords = [ 122 { 123 id: "menu", 124 type: "folder", 125 title: "Bookmarks Menu", 126 parentid: "places", 127 children: [guids.tb, guids.res], 128 }, 129 { 130 id: guids.tb, 131 type: "bookmark", 132 parentid: "menu", 133 bmkUri: "http://getthunderbird.com/", 134 title: "Get Thunderbird!", 135 }, 136 { 137 id: guids.res, 138 type: "folder", 139 parentid: "menu", 140 title: "Resources", 141 children: [guids.irc, guids.bz, guids.mdn], 142 }, 143 { 144 id: guids.bz, 145 type: "bookmark", 146 parentid: guids.res, 147 bmkUri: "https://bugzilla.mozilla.org/", 148 title: "Bugzilla", 149 }, 150 { 151 id: guids.mdn, 152 type: "bookmark", 153 parentid: guids.res, 154 bmkUri: "https://developer.mozilla.org/", 155 title: "MDN", 156 }, 157 { 158 id: guids.irc, 159 type: "bookmark", 160 parentid: guids.res, 161 bmkUri: "ircs://irc.mozilla.org/nightly", 162 title: "IRC", 163 }, 164 ]; 165 for (let record of serverRecords) { 166 collection.insert(record.id, encryptPayload(record), timestamp); 167 } 168 169 engine.lastModified = collection.timestamp; 170 await sync_engine_and_validate_telem(engine, false); 171 172 let expectedTree = buildTree(guids); 173 await assertBookmarksTreeMatches( 174 PlacesUtils.bookmarks.menuGuid, 175 expectedTree, 176 message 177 ); 178 } 179 180 async function get_engine() { 181 return Service.engineManager.get("bookmarks"); 182 } 183 184 add_task(async function test_local_order_newer() { 185 let engine = await get_engine(); 186 let server = await serverForFoo(engine); 187 await SyncTestingInfrastructure(server); 188 189 try { 190 let collection = server.user("foo").collection("bookmarks"); 191 let serverModified = Date.now() / 1000 - 120; 192 await resolveConflict( 193 engine, 194 collection, 195 serverModified, 196 guids => [ 197 { 198 guid: guids.fx, 199 index: 0, 200 }, 201 { 202 guid: guids.res, 203 index: 1, 204 children: [ 205 { 206 guid: guids.nightly, 207 index: 0, 208 }, 209 { 210 guid: guids.support, 211 index: 1, 212 }, 213 { 214 guid: guids.customize, 215 index: 2, 216 }, 217 { 218 guid: guids.irc, 219 index: 3, 220 }, 221 { 222 guid: guids.bz, 223 index: 4, 224 }, 225 { 226 guid: guids.mdn, 227 index: 5, 228 }, 229 ], 230 }, 231 { 232 guid: guids.tb, 233 index: 2, 234 }, 235 ], 236 "Should use local order as base if remote is older" 237 ); 238 } finally { 239 await engine.wipeClient(); 240 await Service.startOver(); 241 await promiseStopServer(server); 242 } 243 }); 244 245 add_task(async function test_remote_order_newer() { 246 let engine = await get_engine(); 247 let server = await serverForFoo(engine); 248 await SyncTestingInfrastructure(server); 249 250 try { 251 let collection = server.user("foo").collection("bookmarks"); 252 let serverModified = Date.now() / 1000 + 120; 253 await resolveConflict( 254 engine, 255 collection, 256 serverModified, 257 guids => [ 258 { 259 guid: guids.tb, 260 index: 0, 261 }, 262 { 263 guid: guids.res, 264 index: 1, 265 children: [ 266 { 267 guid: guids.irc, 268 index: 0, 269 }, 270 { 271 guid: guids.bz, 272 index: 1, 273 }, 274 { 275 guid: guids.mdn, 276 index: 2, 277 }, 278 { 279 guid: guids.nightly, 280 index: 3, 281 }, 282 { 283 guid: guids.support, 284 index: 4, 285 }, 286 { 287 guid: guids.customize, 288 index: 5, 289 }, 290 ], 291 }, 292 { 293 guid: guids.fx, 294 index: 2, 295 }, 296 ], 297 "Should use remote order as base if local is older" 298 ); 299 } finally { 300 await engine.wipeClient(); 301 await Service.startOver(); 302 await promiseStopServer(server); 303 } 304 }); 305 306 add_task(async function test_bookmark_order() { 307 let engine = await get_engine(); 308 let store = engine._store; 309 _("Starting with a clean slate of no bookmarks"); 310 await store.wipe(); 311 await assertBookmarksTreeMatches( 312 "", 313 [ 314 { 315 guid: PlacesUtils.bookmarks.menuGuid, 316 index: 0, 317 }, 318 { 319 guid: PlacesUtils.bookmarks.toolbarGuid, 320 index: 1, 321 }, 322 { 323 // Index 2 is the tags root. (Root indices depend on the order of the 324 // `CreateRoot` calls in `Database::CreateBookmarkRoots`). 325 guid: PlacesUtils.bookmarks.unfiledGuid, 326 index: 3, 327 }, 328 { 329 guid: PlacesUtils.bookmarks.mobileGuid, 330 index: 4, 331 }, 332 ], 333 "clean slate" 334 ); 335 336 function bookmark(name, parent) { 337 let bm = new Bookmark("http://weave.server/my-bookmark"); 338 bm.id = name; 339 bm.title = name; 340 bm.bmkUri = "http://uri/"; 341 bm.parentid = parent || "unfiled"; 342 bm.tags = []; 343 return bm; 344 } 345 346 function folder(name, parent, children) { 347 let bmFolder = new BookmarkFolder("http://weave.server/my-bookmark-folder"); 348 bmFolder.id = name; 349 bmFolder.title = name; 350 bmFolder.parentid = parent || "unfiled"; 351 bmFolder.children = children; 352 return bmFolder; 353 } 354 355 async function apply(records) { 356 for (record of records) { 357 await store.applyIncoming(record); 358 } 359 await engine._apply(); 360 } 361 let id10 = "10_aaaaaaaaa"; 362 _("basic add first bookmark"); 363 await apply([bookmark(id10, "")]); 364 await assertBookmarksTreeMatches( 365 "", 366 [ 367 { 368 guid: PlacesUtils.bookmarks.menuGuid, 369 index: 0, 370 }, 371 { 372 guid: PlacesUtils.bookmarks.toolbarGuid, 373 index: 1, 374 }, 375 { 376 guid: PlacesUtils.bookmarks.unfiledGuid, 377 index: 3, 378 children: [ 379 { 380 guid: id10, 381 index: 0, 382 }, 383 ], 384 }, 385 { 386 guid: PlacesUtils.bookmarks.mobileGuid, 387 index: 4, 388 }, 389 ], 390 "basic add first bookmark" 391 ); 392 let id20 = "20_aaaaaaaaa"; 393 _("basic append behind 10"); 394 await apply([bookmark(id20, "")]); 395 await assertBookmarksTreeMatches( 396 "", 397 [ 398 { 399 guid: PlacesUtils.bookmarks.menuGuid, 400 index: 0, 401 }, 402 { 403 guid: PlacesUtils.bookmarks.toolbarGuid, 404 index: 1, 405 }, 406 { 407 guid: PlacesUtils.bookmarks.unfiledGuid, 408 index: 3, 409 children: [ 410 { 411 guid: id10, 412 index: 0, 413 }, 414 { 415 guid: id20, 416 index: 1, 417 }, 418 ], 419 }, 420 { 421 guid: PlacesUtils.bookmarks.mobileGuid, 422 index: 4, 423 }, 424 ], 425 "basic append behind 10" 426 ); 427 428 let id31 = "31_aaaaaaaaa"; 429 let id30 = "f30_aaaaaaaa"; 430 _("basic create in folder"); 431 let b31 = bookmark(id31, id30); 432 let f30 = folder(id30, "", [id31]); 433 await apply([b31, f30]); 434 await assertBookmarksTreeMatches( 435 "", 436 [ 437 { 438 guid: PlacesUtils.bookmarks.menuGuid, 439 index: 0, 440 }, 441 { 442 guid: PlacesUtils.bookmarks.toolbarGuid, 443 index: 1, 444 }, 445 { 446 guid: PlacesUtils.bookmarks.unfiledGuid, 447 index: 3, 448 children: [ 449 { 450 guid: id10, 451 index: 0, 452 }, 453 { 454 guid: id20, 455 index: 1, 456 }, 457 { 458 guid: id30, 459 index: 2, 460 children: [ 461 { 462 guid: id31, 463 index: 0, 464 }, 465 ], 466 }, 467 ], 468 }, 469 { 470 guid: PlacesUtils.bookmarks.mobileGuid, 471 index: 4, 472 }, 473 ], 474 "basic create in folder" 475 ); 476 477 let id41 = "41_aaaaaaaaa"; 478 let id40 = "f40_aaaaaaaa"; 479 _("insert missing parent -> append to unfiled"); 480 await apply([bookmark(id41, id40)]); 481 await assertBookmarksTreeMatches( 482 "", 483 [ 484 { 485 guid: PlacesUtils.bookmarks.menuGuid, 486 index: 0, 487 }, 488 { 489 guid: PlacesUtils.bookmarks.toolbarGuid, 490 index: 1, 491 }, 492 { 493 guid: PlacesUtils.bookmarks.unfiledGuid, 494 index: 3, 495 children: [ 496 { 497 guid: id10, 498 index: 0, 499 }, 500 { 501 guid: id20, 502 index: 1, 503 }, 504 { 505 guid: id30, 506 index: 2, 507 children: [ 508 { 509 guid: id31, 510 index: 0, 511 }, 512 ], 513 }, 514 { 515 guid: id41, 516 index: 3, 517 }, 518 ], 519 }, 520 { 521 guid: PlacesUtils.bookmarks.mobileGuid, 522 index: 4, 523 }, 524 ], 525 "insert missing parent -> append to unfiled" 526 ); 527 528 let id42 = "42_aaaaaaaaa"; 529 530 _("insert another missing parent -> append"); 531 await apply([bookmark(id42, id40)]); 532 await assertBookmarksTreeMatches( 533 "", 534 [ 535 { 536 guid: PlacesUtils.bookmarks.menuGuid, 537 index: 0, 538 }, 539 { 540 guid: PlacesUtils.bookmarks.toolbarGuid, 541 index: 1, 542 }, 543 { 544 guid: PlacesUtils.bookmarks.unfiledGuid, 545 index: 3, 546 children: [ 547 { 548 guid: id10, 549 index: 0, 550 }, 551 { 552 guid: id20, 553 index: 1, 554 }, 555 { 556 guid: id30, 557 index: 2, 558 children: [ 559 { 560 guid: id31, 561 index: 0, 562 }, 563 ], 564 }, 565 { 566 guid: id41, 567 index: 3, 568 }, 569 { 570 guid: id42, 571 index: 4, 572 }, 573 ], 574 }, 575 { 576 guid: PlacesUtils.bookmarks.mobileGuid, 577 index: 4, 578 }, 579 ], 580 "insert another missing parent -> append" 581 ); 582 583 await engine.wipeClient(); 584 await Service.startOver(); 585 await engine.finalize(); 586 });