tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 });