test_reconcile.js (30387B)
1 "use strict"; 2 3 const TEST_STORE_FILE_NAME = "test-profile.json"; 4 const { CREDIT_CARD_SCHEMA_VERSION } = ChromeUtils.importESModule( 5 "resource://autofill/FormAutofillStorageBase.sys.mjs" 6 ); 7 8 // NOTE: a guide to reading these test-cases: 9 // parent: What the local record looked like the last time we wrote the 10 // record to the Sync server. 11 // local: What the local record looks like now. IOW, the differences between 12 // 'parent' and 'local' are changes recently made which we wish to sync. 13 // remote: An incoming record we need to apply (ie, a record that was possibly 14 // changed on a remote device) 15 // 16 // To further help understanding this, a few of the testcases are annotated. 17 const ADDRESS_RECONCILE_TESTCASES = [ 18 { 19 description: "Local change", 20 parent: { 21 // So when we last wrote the record to the server, it had these values. 22 guid: "2bbd2d8fbc6b", 23 version: 1, 24 name: "Mark Hammond", 25 "street-address": "32 Vassar Street", 26 }, 27 local: [ 28 { 29 // The current local record - by comparing against parent we can see that 30 // only the name has changed locally. 31 name: "Skip", 32 "street-address": "32 Vassar Street", 33 }, 34 ], 35 remote: { 36 // This is the incoming record. It has the same values as "parent", so 37 // we can deduce the record hasn't actually been changed remotely so we 38 // can safely ignore the incoming record and write our local changes. 39 guid: "2bbd2d8fbc6b", 40 version: 1, 41 name: "Mark Hammond", 42 "street-address": "32 Vassar Street", 43 }, 44 reconciled: { 45 guid: "2bbd2d8fbc6b", 46 name: "Skip", 47 "street-address": "32 Vassar Street", 48 }, 49 }, 50 { 51 description: "Remote change", 52 parent: { 53 guid: "e3680e9f890d", 54 version: 1, 55 name: "Mark Hammond", 56 "street-address": "32 Vassar Street", 57 }, 58 local: [ 59 { 60 name: "Mark Hammond", 61 "street-address": "32 Vassar Street", 62 }, 63 ], 64 remote: { 65 guid: "e3680e9f890d", 66 version: 1, 67 name: "Skip", 68 "street-address": "32 Vassar Street", 69 }, 70 reconciled: { 71 guid: "e3680e9f890d", 72 name: "Skip", 73 "street-address": "32 Vassar Street", 74 }, 75 }, 76 { 77 description: "New local field", 78 parent: { 79 guid: "0cba738b1be0", 80 version: 1, 81 name: "Mark Hammond", 82 "street-address": "32 Vassar Street", 83 }, 84 local: [ 85 { 86 name: "Mark Hammond", 87 "street-address": "32 Vassar Street", 88 tel: "123456", 89 }, 90 ], 91 remote: { 92 guid: "0cba738b1be0", 93 version: 1, 94 name: "Mark Hammond", 95 "street-address": "32 Vassar Street", 96 }, 97 reconciled: { 98 guid: "0cba738b1be0", 99 name: "Mark Hammond", 100 "street-address": "32 Vassar Street", 101 tel: "123456", 102 }, 103 }, 104 { 105 description: "New remote field", 106 parent: { 107 guid: "be3ef97f8285", 108 version: 1, 109 name: "Mark Hammond", 110 "street-address": "32 Vassar Street", 111 }, 112 local: [ 113 { 114 name: "Mark Hammond", 115 "street-address": "32 Vassar Street", 116 }, 117 ], 118 remote: { 119 guid: "be3ef97f8285", 120 version: 1, 121 name: "Mark Hammond", 122 "street-address": "32 Vassar Street", 123 tel: "123456", 124 }, 125 reconciled: { 126 guid: "be3ef97f8285", 127 name: "Mark Hammond", 128 "street-address": "32 Vassar Street", 129 tel: "123456", 130 }, 131 }, 132 { 133 description: "Deleted field locally", 134 parent: { 135 guid: "9627322248ec", 136 version: 1, 137 name: "Mark Hammond", 138 "street-address": "32 Vassar Street", 139 tel: "123456", 140 }, 141 local: [ 142 { 143 name: "Mark Hammond", 144 "street-address": "32 Vassar Street", 145 }, 146 ], 147 remote: { 148 guid: "9627322248ec", 149 version: 1, 150 name: "Mark Hammond", 151 "street-address": "32 Vassar Street", 152 tel: "123456", 153 }, 154 reconciled: { 155 guid: "9627322248ec", 156 name: "Mark Hammond", 157 "street-address": "32 Vassar Street", 158 }, 159 }, 160 { 161 description: "Deleted field remotely", 162 parent: { 163 guid: "7d7509f3eeb2", 164 version: 1, 165 name: "Mark Hammond", 166 "street-address": "32 Vassar Street", 167 tel: "123456", 168 }, 169 local: [ 170 { 171 name: "Mark Hammond", 172 "street-address": "32 Vassar Street", 173 tel: "123456", 174 }, 175 ], 176 remote: { 177 guid: "7d7509f3eeb2", 178 version: 1, 179 name: "Mark Hammond", 180 "street-address": "32 Vassar Street", 181 }, 182 reconciled: { 183 guid: "7d7509f3eeb2", 184 name: "Mark Hammond", 185 "street-address": "32 Vassar Street", 186 }, 187 }, 188 { 189 description: "Local and remote changes to unrelated fields", 190 parent: { 191 // The last time we wrote this to the server, country was NZ. 192 guid: "e087a06dfc57", 193 version: 1, 194 name: "Mark Hammond", 195 "street-address": "32 Vassar Street", 196 country: "NZ", 197 // We also had an unknown field we round-tripped 198 foo: "bar", 199 }, 200 local: [ 201 { 202 // The current local record - so locally we've changed name to Skip. 203 name: "Skip", 204 "street-address": "32 Vassar Street", 205 country: "NZ", 206 }, 207 ], 208 remote: { 209 // Remotely, we've changed the country to AU. 210 guid: "e087a06dfc57", 211 version: 1, 212 name: "Mark Hammond", 213 "street-address": "32 Vassar Street", 214 country: "AU", 215 // This is a new unknown field that should send instead! 216 "unknown-1": "an unknown field from another client", 217 }, 218 reconciled: { 219 guid: "e087a06dfc57", 220 name: "Skip", 221 "street-address": "32 Vassar Street", 222 country: "AU", 223 // This is a new unknown field that should send instead! 224 "unknown-1": "an unknown field from another client", 225 }, 226 }, 227 { 228 description: "Multiple local changes", 229 parent: { 230 guid: "340a078c596f", 231 version: 1, 232 name: "Mark Hammond", 233 "street-address": "32 Vassar Street", 234 tel: "123456", 235 country: "US", 236 }, 237 local: [ 238 { 239 name: "Skip", 240 "street-address": "32 Vassar Street", 241 country: "US", 242 }, 243 { 244 name: "Skip", 245 "street-address": "32 Vassar Street", 246 organization: "Mozilla", 247 country: "US", 248 }, 249 ], 250 remote: { 251 guid: "340a078c596f", 252 version: 1, 253 name: "Mark Hammond", 254 "street-address": "32 Vassar Street", 255 tel: "123456", 256 country: "AU", 257 }, 258 reconciled: { 259 guid: "340a078c596f", 260 name: "Skip", 261 "street-address": "32 Vassar Street", 262 organization: "Mozilla", 263 country: "AU", 264 }, 265 }, 266 { 267 // Local and remote diverged from the shared parent, but the values are the 268 // same, so we shouldn't fork. 269 description: "Same change to local and remote", 270 parent: { 271 guid: "0b3a72a1bea2", 272 version: 1, 273 name: "Mark Hammond", 274 "street-address": "32 Vassar Street", 275 // unknown fields we previously roundtripped 276 foo: "bar", 277 }, 278 local: [ 279 { 280 name: "Skip", 281 "street-address": "32 Vassar Street", 282 }, 283 ], 284 remote: { 285 guid: "0b3a72a1bea2", 286 version: 1, 287 name: "Skip", 288 "street-address": "32 Vassar Street", 289 // New unknown field that should be the new round trip 290 "unknown-1": "an unknown field from another client", 291 }, 292 reconciled: { 293 guid: "0b3a72a1bea2", 294 name: "Skip", 295 "street-address": "32 Vassar Street", 296 }, 297 }, 298 { 299 description: "Conflicting changes to single field", 300 parent: { 301 // This is what we last wrote to the sync server. 302 guid: "62068784d089", 303 version: 1, 304 name: "Mark Hammond", 305 "street-address": "32 Vassar Street", 306 "unknown-1": "an unknown field from another client", 307 }, 308 local: [ 309 { 310 // The current version of the local record - the name has changed locally. 311 name: "Skip", 312 "street-address": "32 Vassar Street", 313 }, 314 ], 315 remote: { 316 // An incoming record has a different name than any of the above! 317 guid: "62068784d089", 318 version: 1, 319 name: "Kip", 320 "street-address": "32 Vassar Street", 321 "unknown-1": "an unknown field from another client", 322 }, 323 forked: { 324 // So we've forked the local record to a new GUID (and the next sync is 325 // going to write this as a new record) 326 name: "Skip", 327 "street-address": "32 Vassar Street", 328 "unknown-1": "an unknown field from another client", 329 }, 330 reconciled: { 331 // And we've updated the local version of the record to be the remote version. 332 guid: "62068784d089", 333 name: "Kip", 334 "street-address": "32 Vassar Street", 335 "unknown-1": "an unknown field from another client", 336 }, 337 }, 338 { 339 description: "Conflicting changes to multiple fields", 340 parent: { 341 guid: "244dbb692e94", 342 version: 1, 343 name: "Mark Hammond", 344 "street-address": "32 Vassar Street", 345 country: "NZ", 346 }, 347 local: [ 348 { 349 name: "Skip", 350 "street-address": "32 Vassar Street", 351 country: "AU", 352 }, 353 ], 354 remote: { 355 guid: "244dbb692e94", 356 version: 1, 357 name: "Kip", 358 "street-address": "32 Vassar Street", 359 country: "CA", 360 }, 361 forked: { 362 name: "Skip", 363 "street-address": "32 Vassar Street", 364 country: "AU", 365 }, 366 reconciled: { 367 guid: "244dbb692e94", 368 name: "Kip", 369 "street-address": "32 Vassar Street", 370 country: "CA", 371 }, 372 }, 373 { 374 description: "Field deleted locally, changed remotely", 375 parent: { 376 guid: "6fc45e03d19a", 377 version: 1, 378 name: "Mark Hammond", 379 "street-address": "32 Vassar Street", 380 country: "AU", 381 }, 382 local: [ 383 { 384 name: "Mark Hammond", 385 "street-address": "32 Vassar Street", 386 }, 387 ], 388 remote: { 389 guid: "6fc45e03d19a", 390 version: 1, 391 name: "Mark Hammond", 392 "street-address": "32 Vassar Street", 393 country: "NZ", 394 }, 395 forked: { 396 name: "Mark Hammond", 397 "street-address": "32 Vassar Street", 398 }, 399 reconciled: { 400 guid: "6fc45e03d19a", 401 name: "Mark Hammond", 402 "street-address": "32 Vassar Street", 403 country: "NZ", 404 }, 405 }, 406 { 407 description: "Field changed locally, deleted remotely", 408 parent: { 409 guid: "fff9fa27fa18", 410 version: 1, 411 name: "Mark Hammond", 412 "street-address": "32 Vassar Street", 413 country: "AU", 414 }, 415 local: [ 416 { 417 name: "Mark Hammond", 418 "street-address": "32 Vassar Street", 419 country: "NZ", 420 }, 421 ], 422 remote: { 423 guid: "fff9fa27fa18", 424 version: 1, 425 name: "Mark Hammond", 426 "street-address": "32 Vassar Street", 427 }, 428 forked: { 429 name: "Mark Hammond", 430 "street-address": "32 Vassar Street", 431 country: "NZ", 432 }, 433 reconciled: { 434 guid: "fff9fa27fa18", 435 name: "Mark Hammond", 436 "street-address": "32 Vassar Street", 437 }, 438 }, 439 { 440 // Created, last modified should be synced; last used and times used should 441 // be local. Remote created time older than local, remote modified time 442 // newer than local. 443 description: 444 "Created, last modified time reconciliation without local changes", 445 parent: { 446 guid: "5113f329c42f", 447 version: 1, 448 name: "Mark Hammond", 449 "street-address": "32 Vassar Street", 450 timeCreated: 1234, 451 timeLastModified: 5678, 452 timeLastUsed: 5678, 453 timesUsed: 6, 454 }, 455 local: [], 456 remote: { 457 guid: "5113f329c42f", 458 version: 1, 459 name: "Mark Hammond", 460 "street-address": "32 Vassar Street", 461 timeCreated: 1200, 462 timeLastModified: 5700, 463 timeLastUsed: 5700, 464 timesUsed: 3, 465 }, 466 reconciled: { 467 guid: "5113f329c42f", 468 name: "Mark Hammond", 469 "street-address": "32 Vassar Street", 470 timeCreated: 1200, 471 timeLastModified: 5700, 472 timeLastUsed: 5678, 473 timesUsed: 6, 474 }, 475 }, 476 { 477 // Local changes, remote created time newer than local, remote modified time 478 // older than local. 479 description: 480 "Created, last modified time reconciliation with local changes", 481 parent: { 482 guid: "791e5608b80a", 483 version: 1, 484 name: "Mark Hammond", 485 "street-address": "32 Vassar Street", 486 timeCreated: 1234, 487 timeLastModified: 5678, 488 timeLastUsed: 5678, 489 timesUsed: 6, 490 }, 491 local: [ 492 { 493 name: "Skip", 494 "street-address": "32 Vassar Street", 495 }, 496 ], 497 remote: { 498 guid: "791e5608b80a", 499 version: 1, 500 name: "Mark Hammond", 501 "street-address": "32 Vassar Street", 502 timeCreated: 1300, 503 timeLastModified: 5000, 504 timeLastUsed: 5000, 505 timesUsed: 3, 506 }, 507 reconciled: { 508 guid: "791e5608b80a", 509 name: "Skip", 510 "street-address": "32 Vassar Street", 511 timeCreated: 1234, 512 timeLastUsed: 5678, 513 timesUsed: 6, 514 }, 515 }, 516 ]; 517 518 const CREDIT_CARD_RECONCILE_TESTCASES = [ 519 { 520 description: "Local change", 521 parent: { 522 // So when we last wrote the record to the server, it had these values. 523 guid: "2bbd2d8fbc6b", 524 version: CREDIT_CARD_SCHEMA_VERSION, 525 "cc-name": "John Doe", 526 "cc-number": "4111111111111111", 527 "unknown-1": "an unknown field from another client", 528 }, 529 local: [ 530 { 531 // The current local record - by comparing against parent we can see that 532 // only the cc-number has changed locally. 533 "cc-name": "John Doe", 534 "cc-number": "4929001587121045", 535 }, 536 ], 537 remote: { 538 // This is the incoming record. It has the same values as "parent", so 539 // we can deduce the record hasn't actually been changed remotely so we 540 // can safely ignore the incoming record and write our local changes. 541 guid: "2bbd2d8fbc6b", 542 version: CREDIT_CARD_SCHEMA_VERSION, 543 "cc-name": "John Doe", 544 "cc-number": "4111111111111111", 545 "unknown-2": "a newer unknown field", 546 }, 547 reconciled: { 548 guid: "2bbd2d8fbc6b", 549 "cc-name": "John Doe", 550 "cc-number": "4929001587121045", 551 "unknown-2": "a newer unknown field", 552 }, 553 }, 554 { 555 description: "Remote change", 556 parent: { 557 guid: "e3680e9f890d", 558 version: CREDIT_CARD_SCHEMA_VERSION, 559 "cc-name": "John Doe", 560 "cc-number": "4111111111111111", 561 "unknown-1": "unknown field", 562 }, 563 local: [ 564 { 565 "cc-name": "John Doe", 566 "cc-number": "4111111111111111", 567 }, 568 ], 569 remote: { 570 guid: "e3680e9f890d", 571 version: CREDIT_CARD_SCHEMA_VERSION, 572 "cc-name": "John Doe", 573 "cc-number": "4929001587121045", 574 "unknown-1": "unknown field", 575 }, 576 reconciled: { 577 guid: "e3680e9f890d", 578 "cc-name": "John Doe", 579 "cc-number": "4929001587121045", 580 "unknown-1": "unknown field", 581 }, 582 }, 583 584 { 585 description: "New local field", 586 parent: { 587 guid: "0cba738b1be0", 588 version: CREDIT_CARD_SCHEMA_VERSION, 589 "cc-name": "John Doe", 590 "cc-number": "4111111111111111", 591 }, 592 local: [ 593 { 594 "cc-name": "John Doe", 595 "cc-number": "4111111111111111", 596 "cc-exp-month": 12, 597 }, 598 ], 599 remote: { 600 guid: "0cba738b1be0", 601 version: CREDIT_CARD_SCHEMA_VERSION, 602 "cc-name": "John Doe", 603 "cc-number": "4111111111111111", 604 }, 605 reconciled: { 606 guid: "0cba738b1be0", 607 "cc-name": "John Doe", 608 "cc-number": "4111111111111111", 609 "cc-exp-month": 12, 610 }, 611 }, 612 { 613 description: "New remote field", 614 parent: { 615 guid: "be3ef97f8285", 616 version: CREDIT_CARD_SCHEMA_VERSION, 617 "cc-name": "John Doe", 618 "cc-number": "4111111111111111", 619 }, 620 local: [ 621 { 622 "cc-name": "John Doe", 623 "cc-number": "4111111111111111", 624 }, 625 ], 626 remote: { 627 guid: "be3ef97f8285", 628 version: CREDIT_CARD_SCHEMA_VERSION, 629 "cc-name": "John Doe", 630 "cc-number": "4111111111111111", 631 "cc-exp-month": 12, 632 }, 633 reconciled: { 634 guid: "be3ef97f8285", 635 "cc-name": "John Doe", 636 "cc-number": "4111111111111111", 637 "cc-exp-month": 12, 638 }, 639 }, 640 { 641 description: "Deleted field locally", 642 parent: { 643 guid: "9627322248ec", 644 version: CREDIT_CARD_SCHEMA_VERSION, 645 "cc-name": "John Doe", 646 "cc-number": "4111111111111111", 647 "cc-exp-month": 12, 648 }, 649 local: [ 650 { 651 "cc-name": "John Doe", 652 "cc-number": "4111111111111111", 653 }, 654 ], 655 remote: { 656 guid: "9627322248ec", 657 version: CREDIT_CARD_SCHEMA_VERSION, 658 "cc-name": "John Doe", 659 "cc-number": "4111111111111111", 660 "cc-exp-month": 12, 661 }, 662 reconciled: { 663 guid: "9627322248ec", 664 "cc-name": "John Doe", 665 "cc-number": "4111111111111111", 666 }, 667 }, 668 { 669 description: "Deleted field remotely", 670 parent: { 671 guid: "7d7509f3eeb2", 672 version: CREDIT_CARD_SCHEMA_VERSION, 673 "cc-name": "John Doe", 674 "cc-number": "4111111111111111", 675 "cc-exp-month": 12, 676 }, 677 local: [ 678 { 679 "cc-name": "John Doe", 680 "cc-number": "4111111111111111", 681 "cc-exp-month": 12, 682 }, 683 ], 684 remote: { 685 guid: "7d7509f3eeb2", 686 version: CREDIT_CARD_SCHEMA_VERSION, 687 "cc-name": "John Doe", 688 "cc-number": "4111111111111111", 689 }, 690 reconciled: { 691 guid: "7d7509f3eeb2", 692 "cc-name": "John Doe", 693 "cc-number": "4111111111111111", 694 }, 695 }, 696 { 697 description: "Local and remote changes to unrelated fields", 698 parent: { 699 // The last time we wrote this to the server, "cc-exp-month" was 12. 700 guid: "e087a06dfc57", 701 version: CREDIT_CARD_SCHEMA_VERSION, 702 "cc-name": "John Doe", 703 "cc-number": "4111111111111111", 704 "cc-exp-month": 12, 705 "unknown-1": "unknown field", 706 }, 707 local: [ 708 { 709 // The current local record - so locally we've changed "cc-number". 710 "cc-name": "John Doe", 711 "cc-number": "4929001587121045", 712 "cc-exp-month": 12, 713 }, 714 ], 715 remote: { 716 // Remotely, we've changed "cc-exp-month" to 1. 717 guid: "e087a06dfc57", 718 version: CREDIT_CARD_SCHEMA_VERSION, 719 "cc-name": "John Doe", 720 "cc-number": "4111111111111111", 721 "cc-exp-month": 1, 722 "unknown-2": "a newer unknown field", 723 }, 724 reconciled: { 725 guid: "e087a06dfc57", 726 "cc-name": "John Doe", 727 "cc-number": "4929001587121045", 728 "cc-exp-month": 1, 729 "unknown-2": "a newer unknown field", 730 }, 731 }, 732 { 733 description: "Multiple local changes", 734 parent: { 735 guid: "340a078c596f", 736 version: CREDIT_CARD_SCHEMA_VERSION, 737 "cc-name": "John Doe", 738 "cc-number": "4111111111111111", 739 "unknown-1": "unknown field", 740 }, 741 local: [ 742 { 743 "cc-name": "Skip", 744 "cc-number": "4111111111111111", 745 }, 746 { 747 "cc-name": "Skip", 748 "cc-number": "4111111111111111", 749 "cc-exp-month": 12, 750 }, 751 ], 752 remote: { 753 guid: "340a078c596f", 754 version: CREDIT_CARD_SCHEMA_VERSION, 755 "cc-name": "John Doe", 756 "cc-number": "4111111111111111", 757 "cc-exp-year": 2000, 758 "unknown-1": "unknown field", 759 }, 760 reconciled: { 761 guid: "340a078c596f", 762 "cc-name": "Skip", 763 "cc-number": "4111111111111111", 764 "cc-exp-month": 12, 765 "cc-exp-year": 2000, 766 "unknown-1": "unknown field", 767 }, 768 }, 769 { 770 // Local and remote diverged from the shared parent, but the values are the 771 // same, so we shouldn't fork. 772 description: "Same change to local and remote", 773 parent: { 774 guid: "0b3a72a1bea2", 775 version: CREDIT_CARD_SCHEMA_VERSION, 776 "cc-name": "John Doe", 777 "cc-number": "4111111111111111", 778 }, 779 local: [ 780 { 781 "cc-name": "John Doe", 782 "cc-number": "4929001587121045", 783 }, 784 ], 785 remote: { 786 guid: "0b3a72a1bea2", 787 version: CREDIT_CARD_SCHEMA_VERSION, 788 "cc-name": "John Doe", 789 "cc-number": "4929001587121045", 790 }, 791 reconciled: { 792 guid: "0b3a72a1bea2", 793 "cc-name": "John Doe", 794 "cc-number": "4929001587121045", 795 }, 796 }, 797 { 798 description: "Conflicting changes to single field", 799 parent: { 800 // This is what we last wrote to the sync server. 801 guid: "62068784d089", 802 version: CREDIT_CARD_SCHEMA_VERSION, 803 "cc-name": "John Doe", 804 "cc-number": "4111111111111111", 805 "unknown-1": "unknown field", 806 }, 807 local: [ 808 { 809 // The current version of the local record - the cc-number has changed locally. 810 "cc-name": "John Doe", 811 "cc-number": "5103059495477870", 812 }, 813 ], 814 remote: { 815 // An incoming record has a different cc-number than any of the above! 816 guid: "62068784d089", 817 version: CREDIT_CARD_SCHEMA_VERSION, 818 "cc-name": "John Doe", 819 "cc-number": "4929001587121045", 820 "unknown-1": "unknown field", 821 }, 822 forked: { 823 // So we've forked the local record to a new GUID (and the next sync is 824 // going to write this as a new record) 825 "cc-name": "John Doe", 826 "cc-number": "5103059495477870", 827 "unknown-1": "unknown field", 828 }, 829 reconciled: { 830 // And we've updated the local version of the record to be the remote version. 831 guid: "62068784d089", 832 "cc-name": "John Doe", 833 "cc-number": "4929001587121045", 834 "unknown-1": "unknown field", 835 }, 836 }, 837 { 838 description: "Conflicting changes to multiple fields", 839 parent: { 840 guid: "244dbb692e94", 841 version: CREDIT_CARD_SCHEMA_VERSION, 842 "cc-name": "John Doe", 843 "cc-number": "4111111111111111", 844 "cc-exp-month": 12, 845 }, 846 local: [ 847 { 848 "cc-name": "John Doe", 849 "cc-number": "5103059495477870", 850 "cc-exp-month": 1, 851 }, 852 ], 853 remote: { 854 guid: "244dbb692e94", 855 version: CREDIT_CARD_SCHEMA_VERSION, 856 "cc-name": "John Doe", 857 "cc-number": "4929001587121045", 858 "cc-exp-month": 3, 859 }, 860 forked: { 861 "cc-name": "John Doe", 862 "cc-number": "5103059495477870", 863 "cc-exp-month": 1, 864 }, 865 reconciled: { 866 guid: "244dbb692e94", 867 "cc-name": "John Doe", 868 "cc-number": "4929001587121045", 869 "cc-exp-month": 3, 870 }, 871 }, 872 { 873 description: "Field deleted locally, changed remotely", 874 parent: { 875 guid: "6fc45e03d19a", 876 version: CREDIT_CARD_SCHEMA_VERSION, 877 "cc-name": "John Doe", 878 "cc-number": "4111111111111111", 879 "cc-exp-month": 12, 880 }, 881 local: [ 882 { 883 "cc-name": "John Doe", 884 "cc-number": "4111111111111111", 885 }, 886 ], 887 remote: { 888 guid: "6fc45e03d19a", 889 version: CREDIT_CARD_SCHEMA_VERSION, 890 "cc-name": "John Doe", 891 "cc-number": "4111111111111111", 892 "cc-exp-month": 3, 893 }, 894 forked: { 895 "cc-name": "John Doe", 896 "cc-number": "4111111111111111", 897 }, 898 reconciled: { 899 guid: "6fc45e03d19a", 900 "cc-name": "John Doe", 901 "cc-number": "4111111111111111", 902 "cc-exp-month": 3, 903 }, 904 }, 905 { 906 description: "Field changed locally, deleted remotely", 907 parent: { 908 guid: "fff9fa27fa18", 909 version: CREDIT_CARD_SCHEMA_VERSION, 910 "cc-name": "John Doe", 911 "cc-number": "4111111111111111", 912 "cc-exp-month": 12, 913 }, 914 local: [ 915 { 916 "cc-name": "John Doe", 917 "cc-number": "4111111111111111", 918 "cc-exp-month": 3, 919 }, 920 ], 921 remote: { 922 guid: "fff9fa27fa18", 923 version: CREDIT_CARD_SCHEMA_VERSION, 924 "cc-name": "John Doe", 925 "cc-number": "4111111111111111", 926 }, 927 forked: { 928 "cc-name": "John Doe", 929 "cc-number": "4111111111111111", 930 "cc-exp-month": 3, 931 }, 932 reconciled: { 933 guid: "fff9fa27fa18", 934 "cc-name": "John Doe", 935 "cc-number": "4111111111111111", 936 }, 937 }, 938 { 939 // Created, last modified should be synced; last used and times used should 940 // be local. Remote created time older than local, remote modified time 941 // newer than local. 942 description: 943 "Created, last modified time reconciliation without local changes", 944 parent: { 945 guid: "5113f329c42f", 946 version: CREDIT_CARD_SCHEMA_VERSION, 947 "cc-name": "John Doe", 948 "cc-number": "4111111111111111", 949 timeCreated: 1234, 950 timeLastModified: 5678, 951 timeLastUsed: 5678, 952 timesUsed: 6, 953 }, 954 local: [], 955 remote: { 956 guid: "5113f329c42f", 957 version: CREDIT_CARD_SCHEMA_VERSION, 958 "cc-name": "John Doe", 959 "cc-number": "4111111111111111", 960 timeCreated: 1200, 961 timeLastModified: 5700, 962 timeLastUsed: 5700, 963 timesUsed: 3, 964 }, 965 reconciled: { 966 guid: "5113f329c42f", 967 "cc-name": "John Doe", 968 "cc-number": "4111111111111111", 969 timeCreated: 1200, 970 timeLastModified: 5700, 971 timeLastUsed: 5678, 972 timesUsed: 6, 973 }, 974 }, 975 { 976 // Local changes, remote created time newer than local, remote modified time 977 // older than local. 978 description: 979 "Created, last modified time reconciliation with local changes", 980 parent: { 981 guid: "791e5608b80a", 982 version: CREDIT_CARD_SCHEMA_VERSION, 983 "cc-name": "John Doe", 984 "cc-number": "4111111111111111", 985 timeCreated: 1234, 986 timeLastModified: 5678, 987 timeLastUsed: 5678, 988 timesUsed: 6, 989 }, 990 local: [ 991 { 992 "cc-name": "John Doe", 993 "cc-number": "4929001587121045", 994 }, 995 ], 996 remote: { 997 guid: "791e5608b80a", 998 version: CREDIT_CARD_SCHEMA_VERSION, 999 "cc-name": "John Doe", 1000 "cc-number": "4111111111111111", 1001 timeCreated: 1300, 1002 timeLastModified: 5000, 1003 timeLastUsed: 5000, 1004 timesUsed: 3, 1005 }, 1006 reconciled: { 1007 guid: "791e5608b80a", 1008 "cc-name": "John Doe", 1009 "cc-number": "4929001587121045", 1010 timeCreated: 1234, 1011 timeLastUsed: 5678, 1012 timesUsed: 6, 1013 }, 1014 }, 1015 ]; 1016 1017 add_task(async function test_reconcile_unknown_version() { 1018 let profileStorage = await initProfileStorage(TEST_STORE_FILE_NAME); 1019 1020 // Cross-version reconciliation isn't supported yet. See bug 1377204. 1021 await Assert.rejects( 1022 profileStorage.addresses.reconcile({ 1023 guid: "31d83d2725ec", 1024 version: 3, 1025 name: "Mark Hammond", 1026 "street-address": "32 Vassar Street", 1027 }), 1028 /Got unknown record version/ 1029 ); 1030 }); 1031 1032 add_task(async function test_reconcile_idempotent() { 1033 let profileStorage = await initProfileStorage(TEST_STORE_FILE_NAME); 1034 1035 let guid = "de1ba7b094fe"; 1036 await profileStorage.addresses.add( 1037 { 1038 guid, 1039 version: 1, 1040 name: "Mark Hammond", 1041 "street-address": "32 Vassar Street", 1042 // an unknown field from a previous sync 1043 foo: "bar", 1044 }, 1045 { sourceSync: true } 1046 ); 1047 await profileStorage.addresses.update(guid, { 1048 name: "Skip", 1049 "street-address": "32 Vassar Street", 1050 organization: "Mozilla", 1051 }); 1052 1053 let remote = { 1054 guid, 1055 version: 1, 1056 name: "Mark Hammond", 1057 "street-address": "32 Vassar Street", 1058 tel: "123456", 1059 "unknown-1": "an unknown field from another client", 1060 }; 1061 1062 { 1063 let { forkedGUID } = await profileStorage.addresses.reconcile(remote); 1064 let updatedRecord = await profileStorage.addresses.get(guid, { 1065 rawData: true, 1066 }); 1067 1068 ok(!forkedGUID, "First merge should not fork record"); 1069 ok( 1070 objectMatches(updatedRecord, { 1071 guid: "de1ba7b094fe", 1072 name: "Skip", 1073 "street-address": "32 Vassar Street", 1074 organization: "Mozilla", 1075 tel: "123456", 1076 "unknown-1": "an unknown field from another client", 1077 }), 1078 "First merge should merge local and remote changes" 1079 ); 1080 } 1081 1082 { 1083 let { forkedGUID } = await profileStorage.addresses.reconcile(remote); 1084 let updatedRecord = await profileStorage.addresses.get(guid, { 1085 rawData: true, 1086 }); 1087 1088 ok(!forkedGUID, "Second merge should not fork record"); 1089 ok( 1090 objectMatches(updatedRecord, { 1091 guid: "de1ba7b094fe", 1092 name: "Skip", 1093 "street-address": "32 Vassar Street", 1094 organization: "Mozilla", 1095 tel: "123456", 1096 "unknown-1": "an unknown field from another client", 1097 }), 1098 "Second merge should not change record" 1099 ); 1100 } 1101 }); 1102 1103 add_task(async function test_reconcile_three_way_merge() { 1104 let TESTCASES = { 1105 addresses: ADDRESS_RECONCILE_TESTCASES, 1106 creditCards: CREDIT_CARD_RECONCILE_TESTCASES, 1107 }; 1108 1109 for (let collectionName in TESTCASES) { 1110 info(`Start to test reconcile on ${collectionName}`); 1111 1112 let profileStorage = await initProfileStorage( 1113 TEST_STORE_FILE_NAME, 1114 null, 1115 collectionName 1116 ); 1117 1118 for (let test of TESTCASES[collectionName]) { 1119 info(test.description); 1120 1121 await profileStorage[collectionName].add(test.parent, { 1122 sourceSync: true, 1123 }); 1124 1125 for (let updatedRecord of test.local) { 1126 await profileStorage[collectionName].update( 1127 test.parent.guid, 1128 updatedRecord 1129 ); 1130 } 1131 1132 let localRecord = await profileStorage[collectionName].get( 1133 test.parent.guid, 1134 { 1135 rawData: true, 1136 } 1137 ); 1138 1139 let onReconciled = TestUtils.topicObserved( 1140 "formautofill-storage-changed", 1141 (subject, data) => 1142 data == "reconcile" && 1143 subject.wrappedJSObject.collectionName == collectionName 1144 ); 1145 let { forkedGUID } = await profileStorage[collectionName].reconcile( 1146 test.remote 1147 ); 1148 await onReconciled; 1149 let reconciledRecord = await profileStorage[collectionName].get( 1150 test.parent.guid, 1151 { 1152 rawData: true, 1153 } 1154 ); 1155 if (forkedGUID) { 1156 let forkedRecord = await profileStorage[collectionName].get( 1157 forkedGUID, 1158 { 1159 rawData: true, 1160 } 1161 ); 1162 1163 notEqual(forkedRecord.guid, reconciledRecord.guid); 1164 equal(forkedRecord.timeLastModified, localRecord.timeLastModified); 1165 ok( 1166 objectMatches(forkedRecord, test.forked), 1167 `${test.description} should fork record` 1168 ); 1169 } else { 1170 ok(!test.forked, `${test.description} should not fork record`); 1171 } 1172 1173 ok(objectMatches(reconciledRecord, test.reconciled)); 1174 } 1175 } 1176 });