diff options
Diffstat (limited to 'browser/extensions/formautofill/test/unit/test_reconcile.js')
-rw-r--r-- | browser/extensions/formautofill/test/unit/test_reconcile.js | 1176 |
1 files changed, 1176 insertions, 0 deletions
diff --git a/browser/extensions/formautofill/test/unit/test_reconcile.js b/browser/extensions/formautofill/test/unit/test_reconcile.js new file mode 100644 index 0000000000..d9417eef8c --- /dev/null +++ b/browser/extensions/formautofill/test/unit/test_reconcile.js @@ -0,0 +1,1176 @@ +"use strict"; + +const TEST_STORE_FILE_NAME = "test-profile.json"; +const { CREDIT_CARD_SCHEMA_VERSION } = ChromeUtils.importESModule( + "resource://autofill/FormAutofillStorageBase.sys.mjs" +); + +// NOTE: a guide to reading these test-cases: +// parent: What the local record looked like the last time we wrote the +// record to the Sync server. +// local: What the local record looks like now. IOW, the differences between +// 'parent' and 'local' are changes recently made which we wish to sync. +// remote: An incoming record we need to apply (ie, a record that was possibly +// changed on a remote device) +// +// To further help understanding this, a few of the testcases are annotated. +const ADDRESS_RECONCILE_TESTCASES = [ + { + description: "Local change", + parent: { + // So when we last wrote the record to the server, it had these values. + guid: "2bbd2d8fbc6b", + version: 1, + name: "Mark Hammond", + "street-address": "32 Vassar Street", + }, + local: [ + { + // The current local record - by comparing against parent we can see that + // only the name has changed locally. + name: "Skip", + "street-address": "32 Vassar Street", + }, + ], + remote: { + // This is the incoming record. It has the same values as "parent", so + // we can deduce the record hasn't actually been changed remotely so we + // can safely ignore the incoming record and write our local changes. + guid: "2bbd2d8fbc6b", + version: 1, + name: "Mark Hammond", + "street-address": "32 Vassar Street", + }, + reconciled: { + guid: "2bbd2d8fbc6b", + name: "Skip", + "street-address": "32 Vassar Street", + }, + }, + { + description: "Remote change", + parent: { + guid: "e3680e9f890d", + version: 1, + name: "Mark Hammond", + "street-address": "32 Vassar Street", + }, + local: [ + { + name: "Mark Hammond", + "street-address": "32 Vassar Street", + }, + ], + remote: { + guid: "e3680e9f890d", + version: 1, + name: "Skip", + "street-address": "32 Vassar Street", + }, + reconciled: { + guid: "e3680e9f890d", + name: "Skip", + "street-address": "32 Vassar Street", + }, + }, + { + description: "New local field", + parent: { + guid: "0cba738b1be0", + version: 1, + name: "Mark Hammond", + "street-address": "32 Vassar Street", + }, + local: [ + { + name: "Mark Hammond", + "street-address": "32 Vassar Street", + tel: "123456", + }, + ], + remote: { + guid: "0cba738b1be0", + version: 1, + name: "Mark Hammond", + "street-address": "32 Vassar Street", + }, + reconciled: { + guid: "0cba738b1be0", + name: "Mark Hammond", + "street-address": "32 Vassar Street", + tel: "123456", + }, + }, + { + description: "New remote field", + parent: { + guid: "be3ef97f8285", + version: 1, + name: "Mark Hammond", + "street-address": "32 Vassar Street", + }, + local: [ + { + name: "Mark Hammond", + "street-address": "32 Vassar Street", + }, + ], + remote: { + guid: "be3ef97f8285", + version: 1, + name: "Mark Hammond", + "street-address": "32 Vassar Street", + tel: "123456", + }, + reconciled: { + guid: "be3ef97f8285", + name: "Mark Hammond", + "street-address": "32 Vassar Street", + tel: "123456", + }, + }, + { + description: "Deleted field locally", + parent: { + guid: "9627322248ec", + version: 1, + name: "Mark Hammond", + "street-address": "32 Vassar Street", + tel: "123456", + }, + local: [ + { + name: "Mark Hammond", + "street-address": "32 Vassar Street", + }, + ], + remote: { + guid: "9627322248ec", + version: 1, + name: "Mark Hammond", + "street-address": "32 Vassar Street", + tel: "123456", + }, + reconciled: { + guid: "9627322248ec", + name: "Mark Hammond", + "street-address": "32 Vassar Street", + }, + }, + { + description: "Deleted field remotely", + parent: { + guid: "7d7509f3eeb2", + version: 1, + name: "Mark Hammond", + "street-address": "32 Vassar Street", + tel: "123456", + }, + local: [ + { + name: "Mark Hammond", + "street-address": "32 Vassar Street", + tel: "123456", + }, + ], + remote: { + guid: "7d7509f3eeb2", + version: 1, + name: "Mark Hammond", + "street-address": "32 Vassar Street", + }, + reconciled: { + guid: "7d7509f3eeb2", + name: "Mark Hammond", + "street-address": "32 Vassar Street", + }, + }, + { + description: "Local and remote changes to unrelated fields", + parent: { + // The last time we wrote this to the server, country was NZ. + guid: "e087a06dfc57", + version: 1, + name: "Mark Hammond", + "street-address": "32 Vassar Street", + country: "NZ", + // We also had an unknown field we round-tripped + foo: "bar", + }, + local: [ + { + // The current local record - so locally we've changed name to Skip. + name: "Skip", + "street-address": "32 Vassar Street", + country: "NZ", + }, + ], + remote: { + // Remotely, we've changed the country to AU. + guid: "e087a06dfc57", + version: 1, + name: "Mark Hammond", + "street-address": "32 Vassar Street", + country: "AU", + // This is a new unknown field that should send instead! + "unknown-1": "an unknown field from another client", + }, + reconciled: { + guid: "e087a06dfc57", + name: "Skip", + "street-address": "32 Vassar Street", + country: "AU", + // This is a new unknown field that should send instead! + "unknown-1": "an unknown field from another client", + }, + }, + { + description: "Multiple local changes", + parent: { + guid: "340a078c596f", + version: 1, + name: "Mark Hammond", + "street-address": "32 Vassar Street", + tel: "123456", + country: "US", + }, + local: [ + { + name: "Skip", + "street-address": "32 Vassar Street", + country: "US", + }, + { + name: "Skip", + "street-address": "32 Vassar Street", + organization: "Mozilla", + country: "US", + }, + ], + remote: { + guid: "340a078c596f", + version: 1, + name: "Mark Hammond", + "street-address": "32 Vassar Street", + tel: "123456", + country: "AU", + }, + reconciled: { + guid: "340a078c596f", + name: "Skip", + "street-address": "32 Vassar Street", + organization: "Mozilla", + country: "AU", + }, + }, + { + // Local and remote diverged from the shared parent, but the values are the + // same, so we shouldn't fork. + description: "Same change to local and remote", + parent: { + guid: "0b3a72a1bea2", + version: 1, + name: "Mark Hammond", + "street-address": "32 Vassar Street", + // unknown fields we previously roundtripped + foo: "bar", + }, + local: [ + { + name: "Skip", + "street-address": "32 Vassar Street", + }, + ], + remote: { + guid: "0b3a72a1bea2", + version: 1, + name: "Skip", + "street-address": "32 Vassar Street", + // New unknown field that should be the new round trip + "unknown-1": "an unknown field from another client", + }, + reconciled: { + guid: "0b3a72a1bea2", + name: "Skip", + "street-address": "32 Vassar Street", + }, + }, + { + description: "Conflicting changes to single field", + parent: { + // This is what we last wrote to the sync server. + guid: "62068784d089", + version: 1, + name: "Mark Hammond", + "street-address": "32 Vassar Street", + "unknown-1": "an unknown field from another client", + }, + local: [ + { + // The current version of the local record - the name has changed locally. + name: "Skip", + "street-address": "32 Vassar Street", + }, + ], + remote: { + // An incoming record has a different name than any of the above! + guid: "62068784d089", + version: 1, + name: "Kip", + "street-address": "32 Vassar Street", + "unknown-1": "an unknown field from another client", + }, + forked: { + // So we've forked the local record to a new GUID (and the next sync is + // going to write this as a new record) + name: "Skip", + "street-address": "32 Vassar Street", + "unknown-1": "an unknown field from another client", + }, + reconciled: { + // And we've updated the local version of the record to be the remote version. + guid: "62068784d089", + name: "Kip", + "street-address": "32 Vassar Street", + "unknown-1": "an unknown field from another client", + }, + }, + { + description: "Conflicting changes to multiple fields", + parent: { + guid: "244dbb692e94", + version: 1, + name: "Mark Hammond", + "street-address": "32 Vassar Street", + country: "NZ", + }, + local: [ + { + name: "Skip", + "street-address": "32 Vassar Street", + country: "AU", + }, + ], + remote: { + guid: "244dbb692e94", + version: 1, + name: "Kip", + "street-address": "32 Vassar Street", + country: "CA", + }, + forked: { + name: "Skip", + "street-address": "32 Vassar Street", + country: "AU", + }, + reconciled: { + guid: "244dbb692e94", + name: "Kip", + "street-address": "32 Vassar Street", + country: "CA", + }, + }, + { + description: "Field deleted locally, changed remotely", + parent: { + guid: "6fc45e03d19a", + version: 1, + name: "Mark Hammond", + "street-address": "32 Vassar Street", + country: "AU", + }, + local: [ + { + name: "Mark Hammond", + "street-address": "32 Vassar Street", + }, + ], + remote: { + guid: "6fc45e03d19a", + version: 1, + name: "Mark Hammond", + "street-address": "32 Vassar Street", + country: "NZ", + }, + forked: { + name: "Mark Hammond", + "street-address": "32 Vassar Street", + }, + reconciled: { + guid: "6fc45e03d19a", + name: "Mark Hammond", + "street-address": "32 Vassar Street", + country: "NZ", + }, + }, + { + description: "Field changed locally, deleted remotely", + parent: { + guid: "fff9fa27fa18", + version: 1, + name: "Mark Hammond", + "street-address": "32 Vassar Street", + country: "AU", + }, + local: [ + { + name: "Mark Hammond", + "street-address": "32 Vassar Street", + country: "NZ", + }, + ], + remote: { + guid: "fff9fa27fa18", + version: 1, + name: "Mark Hammond", + "street-address": "32 Vassar Street", + }, + forked: { + name: "Mark Hammond", + "street-address": "32 Vassar Street", + country: "NZ", + }, + reconciled: { + guid: "fff9fa27fa18", + name: "Mark Hammond", + "street-address": "32 Vassar Street", + }, + }, + { + // Created, last modified should be synced; last used and times used should + // be local. Remote created time older than local, remote modified time + // newer than local. + description: + "Created, last modified time reconciliation without local changes", + parent: { + guid: "5113f329c42f", + version: 1, + name: "Mark Hammond", + "street-address": "32 Vassar Street", + timeCreated: 1234, + timeLastModified: 5678, + timeLastUsed: 5678, + timesUsed: 6, + }, + local: [], + remote: { + guid: "5113f329c42f", + version: 1, + name: "Mark Hammond", + "street-address": "32 Vassar Street", + timeCreated: 1200, + timeLastModified: 5700, + timeLastUsed: 5700, + timesUsed: 3, + }, + reconciled: { + guid: "5113f329c42f", + name: "Mark Hammond", + "street-address": "32 Vassar Street", + timeCreated: 1200, + timeLastModified: 5700, + timeLastUsed: 5678, + timesUsed: 6, + }, + }, + { + // Local changes, remote created time newer than local, remote modified time + // older than local. + description: + "Created, last modified time reconciliation with local changes", + parent: { + guid: "791e5608b80a", + version: 1, + name: "Mark Hammond", + "street-address": "32 Vassar Street", + timeCreated: 1234, + timeLastModified: 5678, + timeLastUsed: 5678, + timesUsed: 6, + }, + local: [ + { + name: "Skip", + "street-address": "32 Vassar Street", + }, + ], + remote: { + guid: "791e5608b80a", + version: 1, + name: "Mark Hammond", + "street-address": "32 Vassar Street", + timeCreated: 1300, + timeLastModified: 5000, + timeLastUsed: 5000, + timesUsed: 3, + }, + reconciled: { + guid: "791e5608b80a", + name: "Skip", + "street-address": "32 Vassar Street", + timeCreated: 1234, + timeLastUsed: 5678, + timesUsed: 6, + }, + }, +]; + +const CREDIT_CARD_RECONCILE_TESTCASES = [ + { + description: "Local change", + parent: { + // So when we last wrote the record to the server, it had these values. + guid: "2bbd2d8fbc6b", + version: CREDIT_CARD_SCHEMA_VERSION, + "cc-name": "John Doe", + "cc-number": "4111111111111111", + "unknown-1": "an unknown field from another client", + }, + local: [ + { + // The current local record - by comparing against parent we can see that + // only the cc-number has changed locally. + "cc-name": "John Doe", + "cc-number": "4929001587121045", + }, + ], + remote: { + // This is the incoming record. It has the same values as "parent", so + // we can deduce the record hasn't actually been changed remotely so we + // can safely ignore the incoming record and write our local changes. + guid: "2bbd2d8fbc6b", + version: CREDIT_CARD_SCHEMA_VERSION, + "cc-name": "John Doe", + "cc-number": "4111111111111111", + "unknown-2": "a newer unknown field", + }, + reconciled: { + guid: "2bbd2d8fbc6b", + "cc-name": "John Doe", + "cc-number": "4929001587121045", + "unknown-2": "a newer unknown field", + }, + }, + { + description: "Remote change", + parent: { + guid: "e3680e9f890d", + version: CREDIT_CARD_SCHEMA_VERSION, + "cc-name": "John Doe", + "cc-number": "4111111111111111", + "unknown-1": "unknown field", + }, + local: [ + { + "cc-name": "John Doe", + "cc-number": "4111111111111111", + }, + ], + remote: { + guid: "e3680e9f890d", + version: CREDIT_CARD_SCHEMA_VERSION, + "cc-name": "John Doe", + "cc-number": "4929001587121045", + "unknown-1": "unknown field", + }, + reconciled: { + guid: "e3680e9f890d", + "cc-name": "John Doe", + "cc-number": "4929001587121045", + "unknown-1": "unknown field", + }, + }, + + { + description: "New local field", + parent: { + guid: "0cba738b1be0", + version: CREDIT_CARD_SCHEMA_VERSION, + "cc-name": "John Doe", + "cc-number": "4111111111111111", + }, + local: [ + { + "cc-name": "John Doe", + "cc-number": "4111111111111111", + "cc-exp-month": 12, + }, + ], + remote: { + guid: "0cba738b1be0", + version: CREDIT_CARD_SCHEMA_VERSION, + "cc-name": "John Doe", + "cc-number": "4111111111111111", + }, + reconciled: { + guid: "0cba738b1be0", + "cc-name": "John Doe", + "cc-number": "4111111111111111", + "cc-exp-month": 12, + }, + }, + { + description: "New remote field", + parent: { + guid: "be3ef97f8285", + version: CREDIT_CARD_SCHEMA_VERSION, + "cc-name": "John Doe", + "cc-number": "4111111111111111", + }, + local: [ + { + "cc-name": "John Doe", + "cc-number": "4111111111111111", + }, + ], + remote: { + guid: "be3ef97f8285", + version: CREDIT_CARD_SCHEMA_VERSION, + "cc-name": "John Doe", + "cc-number": "4111111111111111", + "cc-exp-month": 12, + }, + reconciled: { + guid: "be3ef97f8285", + "cc-name": "John Doe", + "cc-number": "4111111111111111", + "cc-exp-month": 12, + }, + }, + { + description: "Deleted field locally", + parent: { + guid: "9627322248ec", + version: CREDIT_CARD_SCHEMA_VERSION, + "cc-name": "John Doe", + "cc-number": "4111111111111111", + "cc-exp-month": 12, + }, + local: [ + { + "cc-name": "John Doe", + "cc-number": "4111111111111111", + }, + ], + remote: { + guid: "9627322248ec", + version: CREDIT_CARD_SCHEMA_VERSION, + "cc-name": "John Doe", + "cc-number": "4111111111111111", + "cc-exp-month": 12, + }, + reconciled: { + guid: "9627322248ec", + "cc-name": "John Doe", + "cc-number": "4111111111111111", + }, + }, + { + description: "Deleted field remotely", + parent: { + guid: "7d7509f3eeb2", + version: CREDIT_CARD_SCHEMA_VERSION, + "cc-name": "John Doe", + "cc-number": "4111111111111111", + "cc-exp-month": 12, + }, + local: [ + { + "cc-name": "John Doe", + "cc-number": "4111111111111111", + "cc-exp-month": 12, + }, + ], + remote: { + guid: "7d7509f3eeb2", + version: CREDIT_CARD_SCHEMA_VERSION, + "cc-name": "John Doe", + "cc-number": "4111111111111111", + }, + reconciled: { + guid: "7d7509f3eeb2", + "cc-name": "John Doe", + "cc-number": "4111111111111111", + }, + }, + { + description: "Local and remote changes to unrelated fields", + parent: { + // The last time we wrote this to the server, "cc-exp-month" was 12. + guid: "e087a06dfc57", + version: CREDIT_CARD_SCHEMA_VERSION, + "cc-name": "John Doe", + "cc-number": "4111111111111111", + "cc-exp-month": 12, + "unknown-1": "unknown field", + }, + local: [ + { + // The current local record - so locally we've changed "cc-number". + "cc-name": "John Doe", + "cc-number": "4929001587121045", + "cc-exp-month": 12, + }, + ], + remote: { + // Remotely, we've changed "cc-exp-month" to 1. + guid: "e087a06dfc57", + version: CREDIT_CARD_SCHEMA_VERSION, + "cc-name": "John Doe", + "cc-number": "4111111111111111", + "cc-exp-month": 1, + "unknown-2": "a newer unknown field", + }, + reconciled: { + guid: "e087a06dfc57", + "cc-name": "John Doe", + "cc-number": "4929001587121045", + "cc-exp-month": 1, + "unknown-2": "a newer unknown field", + }, + }, + { + description: "Multiple local changes", + parent: { + guid: "340a078c596f", + version: CREDIT_CARD_SCHEMA_VERSION, + "cc-name": "John Doe", + "cc-number": "4111111111111111", + "unknown-1": "unknown field", + }, + local: [ + { + "cc-name": "Skip", + "cc-number": "4111111111111111", + }, + { + "cc-name": "Skip", + "cc-number": "4111111111111111", + "cc-exp-month": 12, + }, + ], + remote: { + guid: "340a078c596f", + version: CREDIT_CARD_SCHEMA_VERSION, + "cc-name": "John Doe", + "cc-number": "4111111111111111", + "cc-exp-year": 2000, + "unknown-1": "unknown field", + }, + reconciled: { + guid: "340a078c596f", + "cc-name": "Skip", + "cc-number": "4111111111111111", + "cc-exp-month": 12, + "cc-exp-year": 2000, + "unknown-1": "unknown field", + }, + }, + { + // Local and remote diverged from the shared parent, but the values are the + // same, so we shouldn't fork. + description: "Same change to local and remote", + parent: { + guid: "0b3a72a1bea2", + version: CREDIT_CARD_SCHEMA_VERSION, + "cc-name": "John Doe", + "cc-number": "4111111111111111", + }, + local: [ + { + "cc-name": "John Doe", + "cc-number": "4929001587121045", + }, + ], + remote: { + guid: "0b3a72a1bea2", + version: CREDIT_CARD_SCHEMA_VERSION, + "cc-name": "John Doe", + "cc-number": "4929001587121045", + }, + reconciled: { + guid: "0b3a72a1bea2", + "cc-name": "John Doe", + "cc-number": "4929001587121045", + }, + }, + { + description: "Conflicting changes to single field", + parent: { + // This is what we last wrote to the sync server. + guid: "62068784d089", + version: CREDIT_CARD_SCHEMA_VERSION, + "cc-name": "John Doe", + "cc-number": "4111111111111111", + "unknown-1": "unknown field", + }, + local: [ + { + // The current version of the local record - the cc-number has changed locally. + "cc-name": "John Doe", + "cc-number": "5103059495477870", + }, + ], + remote: { + // An incoming record has a different cc-number than any of the above! + guid: "62068784d089", + version: CREDIT_CARD_SCHEMA_VERSION, + "cc-name": "John Doe", + "cc-number": "4929001587121045", + "unknown-1": "unknown field", + }, + forked: { + // So we've forked the local record to a new GUID (and the next sync is + // going to write this as a new record) + "cc-name": "John Doe", + "cc-number": "5103059495477870", + "unknown-1": "unknown field", + }, + reconciled: { + // And we've updated the local version of the record to be the remote version. + guid: "62068784d089", + "cc-name": "John Doe", + "cc-number": "4929001587121045", + "unknown-1": "unknown field", + }, + }, + { + description: "Conflicting changes to multiple fields", + parent: { + guid: "244dbb692e94", + version: CREDIT_CARD_SCHEMA_VERSION, + "cc-name": "John Doe", + "cc-number": "4111111111111111", + "cc-exp-month": 12, + }, + local: [ + { + "cc-name": "John Doe", + "cc-number": "5103059495477870", + "cc-exp-month": 1, + }, + ], + remote: { + guid: "244dbb692e94", + version: CREDIT_CARD_SCHEMA_VERSION, + "cc-name": "John Doe", + "cc-number": "4929001587121045", + "cc-exp-month": 3, + }, + forked: { + "cc-name": "John Doe", + "cc-number": "5103059495477870", + "cc-exp-month": 1, + }, + reconciled: { + guid: "244dbb692e94", + "cc-name": "John Doe", + "cc-number": "4929001587121045", + "cc-exp-month": 3, + }, + }, + { + description: "Field deleted locally, changed remotely", + parent: { + guid: "6fc45e03d19a", + version: CREDIT_CARD_SCHEMA_VERSION, + "cc-name": "John Doe", + "cc-number": "4111111111111111", + "cc-exp-month": 12, + }, + local: [ + { + "cc-name": "John Doe", + "cc-number": "4111111111111111", + }, + ], + remote: { + guid: "6fc45e03d19a", + version: CREDIT_CARD_SCHEMA_VERSION, + "cc-name": "John Doe", + "cc-number": "4111111111111111", + "cc-exp-month": 3, + }, + forked: { + "cc-name": "John Doe", + "cc-number": "4111111111111111", + }, + reconciled: { + guid: "6fc45e03d19a", + "cc-name": "John Doe", + "cc-number": "4111111111111111", + "cc-exp-month": 3, + }, + }, + { + description: "Field changed locally, deleted remotely", + parent: { + guid: "fff9fa27fa18", + version: CREDIT_CARD_SCHEMA_VERSION, + "cc-name": "John Doe", + "cc-number": "4111111111111111", + "cc-exp-month": 12, + }, + local: [ + { + "cc-name": "John Doe", + "cc-number": "4111111111111111", + "cc-exp-month": 3, + }, + ], + remote: { + guid: "fff9fa27fa18", + version: CREDIT_CARD_SCHEMA_VERSION, + "cc-name": "John Doe", + "cc-number": "4111111111111111", + }, + forked: { + "cc-name": "John Doe", + "cc-number": "4111111111111111", + "cc-exp-month": 3, + }, + reconciled: { + guid: "fff9fa27fa18", + "cc-name": "John Doe", + "cc-number": "4111111111111111", + }, + }, + { + // Created, last modified should be synced; last used and times used should + // be local. Remote created time older than local, remote modified time + // newer than local. + description: + "Created, last modified time reconciliation without local changes", + parent: { + guid: "5113f329c42f", + version: CREDIT_CARD_SCHEMA_VERSION, + "cc-name": "John Doe", + "cc-number": "4111111111111111", + timeCreated: 1234, + timeLastModified: 5678, + timeLastUsed: 5678, + timesUsed: 6, + }, + local: [], + remote: { + guid: "5113f329c42f", + version: CREDIT_CARD_SCHEMA_VERSION, + "cc-name": "John Doe", + "cc-number": "4111111111111111", + timeCreated: 1200, + timeLastModified: 5700, + timeLastUsed: 5700, + timesUsed: 3, + }, + reconciled: { + guid: "5113f329c42f", + "cc-name": "John Doe", + "cc-number": "4111111111111111", + timeCreated: 1200, + timeLastModified: 5700, + timeLastUsed: 5678, + timesUsed: 6, + }, + }, + { + // Local changes, remote created time newer than local, remote modified time + // older than local. + description: + "Created, last modified time reconciliation with local changes", + parent: { + guid: "791e5608b80a", + version: CREDIT_CARD_SCHEMA_VERSION, + "cc-name": "John Doe", + "cc-number": "4111111111111111", + timeCreated: 1234, + timeLastModified: 5678, + timeLastUsed: 5678, + timesUsed: 6, + }, + local: [ + { + "cc-name": "John Doe", + "cc-number": "4929001587121045", + }, + ], + remote: { + guid: "791e5608b80a", + version: CREDIT_CARD_SCHEMA_VERSION, + "cc-name": "John Doe", + "cc-number": "4111111111111111", + timeCreated: 1300, + timeLastModified: 5000, + timeLastUsed: 5000, + timesUsed: 3, + }, + reconciled: { + guid: "791e5608b80a", + "cc-name": "John Doe", + "cc-number": "4929001587121045", + timeCreated: 1234, + timeLastUsed: 5678, + timesUsed: 6, + }, + }, +]; + +add_task(async function test_reconcile_unknown_version() { + let profileStorage = await initProfileStorage(TEST_STORE_FILE_NAME); + + // Cross-version reconciliation isn't supported yet. See bug 1377204. + await Assert.rejects( + profileStorage.addresses.reconcile({ + guid: "31d83d2725ec", + version: 3, + name: "Mark Hammond", + "street-address": "32 Vassar Street", + }), + /Got unknown record version/ + ); +}); + +add_task(async function test_reconcile_idempotent() { + let profileStorage = await initProfileStorage(TEST_STORE_FILE_NAME); + + let guid = "de1ba7b094fe"; + await profileStorage.addresses.add( + { + guid, + version: 1, + name: "Mark Hammond", + "street-address": "32 Vassar Street", + // an unknown field from a previous sync + foo: "bar", + }, + { sourceSync: true } + ); + await profileStorage.addresses.update(guid, { + name: "Skip", + "street-address": "32 Vassar Street", + organization: "Mozilla", + }); + + let remote = { + guid, + version: 1, + name: "Mark Hammond", + "street-address": "32 Vassar Street", + tel: "123456", + "unknown-1": "an unknown field from another client", + }; + + { + let { forkedGUID } = await profileStorage.addresses.reconcile(remote); + let updatedRecord = await profileStorage.addresses.get(guid, { + rawData: true, + }); + + ok(!forkedGUID, "First merge should not fork record"); + ok( + objectMatches(updatedRecord, { + guid: "de1ba7b094fe", + name: "Skip", + "street-address": "32 Vassar Street", + organization: "Mozilla", + tel: "123456", + "unknown-1": "an unknown field from another client", + }), + "First merge should merge local and remote changes" + ); + } + + { + let { forkedGUID } = await profileStorage.addresses.reconcile(remote); + let updatedRecord = await profileStorage.addresses.get(guid, { + rawData: true, + }); + + ok(!forkedGUID, "Second merge should not fork record"); + ok( + objectMatches(updatedRecord, { + guid: "de1ba7b094fe", + name: "Skip", + "street-address": "32 Vassar Street", + organization: "Mozilla", + tel: "123456", + "unknown-1": "an unknown field from another client", + }), + "Second merge should not change record" + ); + } +}); + +add_task(async function test_reconcile_three_way_merge() { + let TESTCASES = { + addresses: ADDRESS_RECONCILE_TESTCASES, + creditCards: CREDIT_CARD_RECONCILE_TESTCASES, + }; + + for (let collectionName in TESTCASES) { + info(`Start to test reconcile on ${collectionName}`); + + let profileStorage = await initProfileStorage( + TEST_STORE_FILE_NAME, + null, + collectionName + ); + + for (let test of TESTCASES[collectionName]) { + info(test.description); + + await profileStorage[collectionName].add(test.parent, { + sourceSync: true, + }); + + for (let updatedRecord of test.local) { + await profileStorage[collectionName].update( + test.parent.guid, + updatedRecord + ); + } + + let localRecord = await profileStorage[collectionName].get( + test.parent.guid, + { + rawData: true, + } + ); + + let onReconciled = TestUtils.topicObserved( + "formautofill-storage-changed", + (subject, data) => + data == "reconcile" && + subject.wrappedJSObject.collectionName == collectionName + ); + let { forkedGUID } = await profileStorage[collectionName].reconcile( + test.remote + ); + await onReconciled; + let reconciledRecord = await profileStorage[collectionName].get( + test.parent.guid, + { + rawData: true, + } + ); + if (forkedGUID) { + let forkedRecord = await profileStorage[collectionName].get( + forkedGUID, + { + rawData: true, + } + ); + + notEqual(forkedRecord.guid, reconciledRecord.guid); + equal(forkedRecord.timeLastModified, localRecord.timeLastModified); + ok( + objectMatches(forkedRecord, test.forked), + `${test.description} should fork record` + ); + } else { + ok(!test.forked, `${test.description} should not fork record`); + } + + ok(objectMatches(reconciledRecord, test.reconciled)); + } + } +}); |