diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /browser/extensions/formautofill/test/unit/test_creditCardRecords.js | |
parent | Initial commit. (diff) | |
download | firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'browser/extensions/formautofill/test/unit/test_creditCardRecords.js')
-rw-r--r-- | browser/extensions/formautofill/test/unit/test_creditCardRecords.js | 926 |
1 files changed, 926 insertions, 0 deletions
diff --git a/browser/extensions/formautofill/test/unit/test_creditCardRecords.js b/browser/extensions/formautofill/test/unit/test_creditCardRecords.js new file mode 100644 index 0000000000..3011fe885f --- /dev/null +++ b/browser/extensions/formautofill/test/unit/test_creditCardRecords.js @@ -0,0 +1,926 @@ +/** + * Tests FormAutofillStorage object with creditCards records. + */ + +"use strict"; + +ChromeUtils.defineESModuleGetters(this, { + Preferences: "resource://gre/modules/Preferences.sys.mjs", +}); +const { CreditCard } = ChromeUtils.importESModule( + "resource://gre/modules/CreditCard.sys.mjs" +); + +let FormAutofillStorage; +let CREDIT_CARD_SCHEMA_VERSION; +add_setup(async () => { + ({ FormAutofillStorage } = ChromeUtils.importESModule( + "resource://autofill/FormAutofillStorage.sys.mjs" + )); + ({ CREDIT_CARD_SCHEMA_VERSION } = ChromeUtils.importESModule( + "resource://autofill/FormAutofillStorageBase.sys.mjs" + )); +}); + +const TEST_STORE_FILE_NAME = "test-credit-card.json"; +const COLLECTION_NAME = "creditCards"; + +const TEST_CREDIT_CARD_1 = { + "cc-name": "John Doe", + "cc-number": "4929001587121045", + "cc-exp-month": 4, + "cc-exp-year": 2017, +}; + +const TEST_CREDIT_CARD_2 = { + "cc-name": "Timothy Berners-Lee", + "cc-number": "5103059495477870", + "cc-exp-month": 12, + "cc-exp-year": 2022, +}; + +const TEST_CREDIT_CARD_3 = { + "cc-number": "3589993783099582", + "cc-exp-month": 1, + "cc-exp-year": 2000, +}; + +const TEST_CREDIT_CARD_4 = { + "cc-name": "Foo Bar", + "cc-number": "3589993783099582", +}; + +const TEST_CREDIT_CARD_WITH_BILLING_ADDRESS = { + "cc-name": "J. Smith", + "cc-number": "4111111111111111", + billingAddressGUID: "9m6hf4gfr6ge", +}; + +const TEST_CREDIT_CARD_WITH_EMPTY_FIELD = { + billingAddressGUID: "", + "cc-name": "", + "cc-number": "344060747836806", + "cc-exp-month": 1, +}; + +const TEST_CREDIT_CARD_WITH_EMPTY_COMPUTED_FIELD = { + "cc-given-name": "", + "cc-additional-name": "", + "cc-family-name": "", + "cc-exp": "", + "cc-number": "5415425865751454", +}; + +const TEST_CREDIT_CARD_WITH_2_DIGITS_YEAR = { + "cc-number": "344060747836806", + "cc-exp-month": 1, + "cc-exp-year": 12, +}; + +const TEST_CREDIT_CARD_WITH_INVALID_FIELD = { + "cc-name": "John Doe", + "cc-number": "344060747836806", + "cc-type": { invalid: "invalid" }, +}; + +const TEST_CREDIT_CARD_WITH_INVALID_EXPIRY_DATE = { + "cc-name": "John Doe", + "cc-number": "5103059495477870", + "cc-exp-month": 13, + "cc-exp-year": -3, +}; + +const TEST_CREDIT_CARD_WITH_SPACES_BETWEEN_DIGITS = { + "cc-name": "John Doe", + "cc-number": "5103 0594 9547 7870", +}; + +const TEST_CREDIT_CARD_EMPTY_AFTER_NORMALIZE = { + "cc-exp-month": 13, +}; + +const TEST_CREDIT_CARD_EMPTY_AFTER_UPDATE_CREDIT_CARD_1 = { + "cc-name": "", + "cc-number": "", + "cc-exp-month": 13, + "cc-exp-year": "", +}; + +const MERGE_TESTCASES = [ + { + description: "Merge a superset", + creditCardInStorage: { + "cc-number": "4929001587121045", + "cc-exp-month": 4, + "cc-exp-year": 2017, + "unknown-1": "an unknown field from another client", + }, + creditCardToMerge: { + "cc-name": "John Doe", + "cc-number": "4929001587121045", + "cc-exp-month": 4, + "cc-exp-year": 2017, + "unknown-1": "an unknown field from another client", + }, + expectedCreditCard: { + "cc-name": "John Doe", + "cc-number": "4929001587121045", + "cc-exp-month": 4, + "cc-exp-year": 2017, + "unknown-1": "an unknown field from another client", + }, + }, + { + description: "Merge a superset with billingAddressGUID", + creditCardInStorage: { + "cc-number": "4929001587121045", + }, + creditCardToMerge: { + "cc-number": "4929001587121045", + billingAddressGUID: "ijsnbhfr", + }, + expectedCreditCard: { + "cc-number": "4929001587121045", + billingAddressGUID: "ijsnbhfr", + }, + }, + { + description: "Merge a subset", + creditCardInStorage: { + "cc-name": "John Doe", + "cc-number": "4929001587121045", + "cc-exp-month": 4, + "cc-exp-year": 2017, + }, + creditCardToMerge: { + "cc-number": "4929001587121045", + "cc-exp-month": 4, + "cc-exp-year": 2017, + }, + expectedCreditCard: { + "cc-name": "John Doe", + "cc-number": "4929001587121045", + "cc-exp-month": 4, + "cc-exp-year": 2017, + }, + noNeedToUpdate: true, + }, + { + description: "Merge a subset with billingAddressGUID", + creditCardInStorage: { + "cc-number": "4929001587121045", + billingAddressGUID: "8fhdb3ug6", + }, + creditCardToMerge: { + "cc-number": "4929001587121045", + }, + expectedCreditCard: { + billingAddressGUID: "8fhdb3ug6", + "cc-number": "4929001587121045", + }, + noNeedToUpdate: true, + }, + { + description: "Merge an creditCard with partial overlaps", + creditCardInStorage: { + "cc-name": "John Doe", + "cc-number": "4929001587121045", + }, + creditCardToMerge: { + "cc-number": "4929001587121045", + "cc-exp-month": 4, + "cc-exp-year": 2017, + }, + expectedCreditCard: { + "cc-name": "John Doe", + "cc-number": "4929001587121045", + "cc-exp-month": 4, + "cc-exp-year": 2017, + }, + }, +]; + +let prepareTestCreditCards = async function (path) { + let profileStorage = new FormAutofillStorage(path); + await profileStorage.initialize(); + + let onChanged = TestUtils.topicObserved( + "formautofill-storage-changed", + (subject, data) => + data == "add" && + subject.wrappedJSObject.guid && + subject.wrappedJSObject.collectionName == COLLECTION_NAME + ); + Assert.ok(await profileStorage.creditCards.add(TEST_CREDIT_CARD_1)); + await onChanged; + Assert.ok(await profileStorage.creditCards.add(TEST_CREDIT_CARD_2)); + await onChanged; + await profileStorage._saveImmediately(); +}; + +let reCCNumber = /^(\*+)(.{4})$/; + +let do_check_credit_card_matches = (creditCardWithMeta, creditCard) => { + for (let key in creditCard) { + if (key == "cc-number") { + let matches = reCCNumber.exec(creditCardWithMeta["cc-number"]); + Assert.notEqual(matches, null); + Assert.equal( + creditCardWithMeta["cc-number"].length, + creditCard["cc-number"].length + ); + Assert.equal(creditCard["cc-number"].endsWith(matches[2]), true); + Assert.notEqual(creditCard["cc-number-encrypted"], ""); + } else { + Assert.equal(creditCardWithMeta[key], creditCard[key], "Testing " + key); + } + } +}; + +add_task(async function test_initialize() { + let path = getTempFile(TEST_STORE_FILE_NAME).path; + let profileStorage = new FormAutofillStorage(path); + await profileStorage.initialize(); + + Assert.equal(profileStorage._store.data.version, 1); + Assert.equal(profileStorage._store.data.creditCards.length, 0); + + let data = profileStorage._store.data; + Assert.deepEqual(data.creditCards, []); + + await profileStorage._saveImmediately(); + + profileStorage = new FormAutofillStorage(path); + await profileStorage.initialize(); + + Assert.deepEqual(profileStorage._store.data, data); +}); + +add_task(async function test_getAll() { + let path = getTempFile(TEST_STORE_FILE_NAME).path; + await prepareTestCreditCards(path); + + let profileStorage = new FormAutofillStorage(path); + await profileStorage.initialize(); + + let creditCards = await profileStorage.creditCards.getAll(); + + Assert.equal(creditCards.length, 2); + do_check_credit_card_matches(creditCards[0], TEST_CREDIT_CARD_1); + do_check_credit_card_matches(creditCards[1], TEST_CREDIT_CARD_2); + + // Check computed fields. + Assert.equal(creditCards[0]["cc-given-name"], "John"); + Assert.equal(creditCards[0]["cc-family-name"], "Doe"); + Assert.equal(creditCards[0]["cc-exp"], "2017-04"); + + // Test with rawData set. + creditCards = await profileStorage.creditCards.getAll({ rawData: true }); + Assert.equal(creditCards[0]["cc-given-name"], undefined); + Assert.equal(creditCards[0]["cc-family-name"], undefined); + Assert.equal(creditCards[0]["cc-exp"], undefined); + + // Modifying output shouldn't affect the storage. + creditCards[0]["cc-name"] = "test"; + do_check_credit_card_matches( + (await profileStorage.creditCards.getAll())[0], + TEST_CREDIT_CARD_1 + ); +}); + +add_task(async function test_get() { + let path = getTempFile(TEST_STORE_FILE_NAME).path; + await prepareTestCreditCards(path); + + let profileStorage = new FormAutofillStorage(path); + await profileStorage.initialize(); + + let creditCards = await profileStorage.creditCards.getAll(); + let guid = creditCards[0].guid; + + let creditCard = await profileStorage.creditCards.get(guid); + do_check_credit_card_matches(creditCard, TEST_CREDIT_CARD_1); + + // Modifying output shouldn't affect the storage. + creditCards[0]["cc-name"] = "test"; + do_check_credit_card_matches( + await profileStorage.creditCards.get(guid), + TEST_CREDIT_CARD_1 + ); + + Assert.equal(await profileStorage.creditCards.get("INVALID_GUID"), null); +}); + +add_task(async function test_add() { + let path = getTempFile(TEST_STORE_FILE_NAME).path; + await prepareTestCreditCards(path); + + let profileStorage = new FormAutofillStorage(path); + await profileStorage.initialize(); + + let creditCards = await profileStorage.creditCards.getAll(); + + Assert.equal(creditCards.length, 2); + + do_check_credit_card_matches(creditCards[0], TEST_CREDIT_CARD_1); + do_check_credit_card_matches(creditCards[1], TEST_CREDIT_CARD_2); + + Assert.notEqual(creditCards[0].guid, undefined); + Assert.equal(creditCards[0].version, CREDIT_CARD_SCHEMA_VERSION); + Assert.notEqual(creditCards[0].timeCreated, undefined); + Assert.equal(creditCards[0].timeLastModified, creditCards[0].timeCreated); + Assert.equal(creditCards[0].timeLastUsed, 0); + Assert.equal(creditCards[0].timesUsed, 0); + + // Empty string should be deleted before saving. + await profileStorage.creditCards.add(TEST_CREDIT_CARD_WITH_EMPTY_FIELD); + let creditCard = profileStorage.creditCards._data[2]; + Assert.equal( + creditCard["cc-exp-month"], + TEST_CREDIT_CARD_WITH_EMPTY_FIELD["cc-exp-month"] + ); + Assert.equal(creditCard["cc-name"], undefined); + Assert.equal(creditCard.billingAddressGUID, undefined); + + // Empty computed fields shouldn't cause any problem. + await profileStorage.creditCards.add( + TEST_CREDIT_CARD_WITH_EMPTY_COMPUTED_FIELD + ); + creditCard = profileStorage.creditCards._data[3]; + Assert.equal( + creditCard["cc-number"], + CreditCard.getLongMaskedNumber( + TEST_CREDIT_CARD_WITH_EMPTY_COMPUTED_FIELD["cc-number"] + ) + ); + + await Assert.rejects( + profileStorage.creditCards.add(TEST_CREDIT_CARD_WITH_INVALID_FIELD), + /"cc-type" contains invalid data type: object/ + ); + + await Assert.rejects( + profileStorage.creditCards.add({}), + /Record contains no valid field\./ + ); + + await Assert.rejects( + profileStorage.creditCards.add(TEST_CREDIT_CARD_EMPTY_AFTER_NORMALIZE), + /Record contains no valid field\./ + ); +}); + +add_task(async function test_addWithBillingAddress() { + let path = getTempFile(TEST_STORE_FILE_NAME).path; + let profileStorage = new FormAutofillStorage(path); + await profileStorage.initialize(); + + let creditCards = await profileStorage.creditCards.getAll(); + + Assert.equal(creditCards.length, 0); + + await profileStorage.creditCards.add(TEST_CREDIT_CARD_WITH_BILLING_ADDRESS); + + creditCards = await profileStorage.creditCards.getAll(); + Assert.equal(creditCards.length, 1); + do_check_credit_card_matches( + creditCards[0], + TEST_CREDIT_CARD_WITH_BILLING_ADDRESS + ); +}); + +add_task(async function test_update() { + // Test assumes that when an entry is saved a second time, it's last modified date will + // be different from the first. With high values of precision reduction, we execute too + // fast for that to be true. + let timerPrecision = Preferences.get("privacy.reduceTimerPrecision"); + Preferences.set("privacy.reduceTimerPrecision", false); + + registerCleanupFunction(function () { + Preferences.set("privacy.reduceTimerPrecision", timerPrecision); + }); + + let path = getTempFile(TEST_STORE_FILE_NAME).path; + await prepareTestCreditCards(path); + + let profileStorage = new FormAutofillStorage(path); + await profileStorage.initialize(); + + let creditCards = await profileStorage.creditCards.getAll(); + let guid = creditCards[1].guid; + let timeLastModified = creditCards[1].timeLastModified; + + let onChanged = TestUtils.topicObserved( + "formautofill-storage-changed", + (subject, data) => + data == "update" && + subject.wrappedJSObject.guid == guid && + subject.wrappedJSObject.collectionName == COLLECTION_NAME + ); + + Assert.notEqual(creditCards[1]["cc-name"], undefined); + await profileStorage.creditCards.update(guid, TEST_CREDIT_CARD_3); + await onChanged; + await profileStorage._saveImmediately(); + + profileStorage = new FormAutofillStorage(path); + await profileStorage.initialize(); + + let creditCard = await profileStorage.creditCards.get(guid); + + Assert.equal(creditCard["cc-name"], undefined); + Assert.notEqual(creditCard.timeLastModified, timeLastModified); + do_check_credit_card_matches(creditCard, TEST_CREDIT_CARD_3); + + // Empty string should be deleted while updating. + await profileStorage.creditCards.update( + profileStorage.creditCards._data[0].guid, + TEST_CREDIT_CARD_WITH_EMPTY_FIELD + ); + creditCard = profileStorage.creditCards._data[0]; + Assert.equal( + creditCard["cc-exp-month"], + TEST_CREDIT_CARD_WITH_EMPTY_FIELD["cc-exp-month"] + ); + Assert.equal(creditCard["cc-name"], undefined); + Assert.equal(creditCard["cc-type"], "amex"); + Assert.equal(creditCard.billingAddressGUID, undefined); + + // Empty computed fields shouldn't cause any problem. + await profileStorage.creditCards.update( + profileStorage.creditCards._data[0].guid, + TEST_CREDIT_CARD_WITH_EMPTY_COMPUTED_FIELD, + false + ); + creditCard = profileStorage.creditCards._data[0]; + Assert.equal( + creditCard["cc-number"], + CreditCard.getLongMaskedNumber( + TEST_CREDIT_CARD_WITH_EMPTY_COMPUTED_FIELD["cc-number"] + ) + ); + await profileStorage.creditCards.update( + profileStorage.creditCards._data[1].guid, + TEST_CREDIT_CARD_WITH_EMPTY_COMPUTED_FIELD, + true + ); + creditCard = profileStorage.creditCards._data[1]; + Assert.equal( + creditCard["cc-number"], + CreditCard.getLongMaskedNumber( + TEST_CREDIT_CARD_WITH_EMPTY_COMPUTED_FIELD["cc-number"] + ) + ); + + // Decryption failure of existing record should not prevent it from being updated. + creditCard = profileStorage.creditCards._data[0]; + creditCard["cc-number-encrypted"] = "INVALID"; + await profileStorage.creditCards.update( + profileStorage.creditCards._data[0].guid, + TEST_CREDIT_CARD_WITH_EMPTY_COMPUTED_FIELD, + false + ); + creditCard = profileStorage.creditCards._data[0]; + Assert.equal( + creditCard["cc-number"], + CreditCard.getLongMaskedNumber( + TEST_CREDIT_CARD_WITH_EMPTY_COMPUTED_FIELD["cc-number"] + ) + ); + + await Assert.rejects( + profileStorage.creditCards.update("INVALID_GUID", TEST_CREDIT_CARD_3), + /No matching record\./ + ); + + await Assert.rejects( + profileStorage.creditCards.update( + guid, + TEST_CREDIT_CARD_WITH_INVALID_FIELD + ), + /"cc-type" contains invalid data type: object/ + ); + + await Assert.rejects( + profileStorage.creditCards.update(guid, {}), + /Record contains no valid field\./ + ); + + await Assert.rejects( + profileStorage.creditCards.update( + guid, + TEST_CREDIT_CARD_EMPTY_AFTER_NORMALIZE + ), + /Record contains no valid field\./ + ); + + await profileStorage.creditCards.update(guid, TEST_CREDIT_CARD_1); + await Assert.rejects( + profileStorage.creditCards.update( + guid, + TEST_CREDIT_CARD_EMPTY_AFTER_UPDATE_CREDIT_CARD_1 + ), + /Record contains no valid field\./ + ); +}); + +add_task(async function test_validate() { + let path = getTempFile(TEST_STORE_FILE_NAME).path; + + let profileStorage = new FormAutofillStorage(path); + await profileStorage.initialize(); + + await profileStorage.creditCards.add( + TEST_CREDIT_CARD_WITH_INVALID_EXPIRY_DATE + ); + await profileStorage.creditCards.add(TEST_CREDIT_CARD_WITH_2_DIGITS_YEAR); + await profileStorage.creditCards.add( + TEST_CREDIT_CARD_WITH_SPACES_BETWEEN_DIGITS + ); + let creditCards = await profileStorage.creditCards.getAll(); + + Assert.equal(creditCards[0]["cc-exp-month"], undefined); + Assert.equal(creditCards[0]["cc-exp-year"], undefined); + Assert.equal(creditCards[0]["cc-exp"], undefined); + + let month = TEST_CREDIT_CARD_WITH_2_DIGITS_YEAR["cc-exp-month"]; + let year = + parseInt(TEST_CREDIT_CARD_WITH_2_DIGITS_YEAR["cc-exp-year"], 10) + 2000; + Assert.equal(creditCards[1]["cc-exp-month"], month); + Assert.equal(creditCards[1]["cc-exp-year"], year); + Assert.equal( + creditCards[1]["cc-exp"], + year + "-" + month.toString().padStart(2, "0") + ); + + Assert.equal(creditCards[2]["cc-number"].length, 16); +}); + +add_task(async function test_notifyUsed() { + let path = getTempFile(TEST_STORE_FILE_NAME).path; + await prepareTestCreditCards(path); + + let profileStorage = new FormAutofillStorage(path); + await profileStorage.initialize(); + + let creditCards = await profileStorage.creditCards.getAll(); + let guid = creditCards[1].guid; + let timeLastUsed = creditCards[1].timeLastUsed; + let timesUsed = creditCards[1].timesUsed; + + let onChanged = TestUtils.topicObserved( + "formautofill-storage-changed", + (subject, data) => + data == "notifyUsed" && + subject.wrappedJSObject.collectionName == COLLECTION_NAME && + subject.wrappedJSObject.guid == guid + ); + + profileStorage.creditCards.notifyUsed(guid); + await onChanged; + await profileStorage._saveImmediately(); + + profileStorage = new FormAutofillStorage(path); + await profileStorage.initialize(); + + let creditCard = await profileStorage.creditCards.get(guid); + + Assert.equal(creditCard.timesUsed, timesUsed + 1); + Assert.notEqual(creditCard.timeLastUsed, timeLastUsed); + + Assert.throws( + () => profileStorage.creditCards.notifyUsed("INVALID_GUID"), + /No matching record\./ + ); +}); + +add_task(async function test_remove() { + let path = getTempFile(TEST_STORE_FILE_NAME).path; + await prepareTestCreditCards(path); + + let profileStorage = new FormAutofillStorage(path); + await profileStorage.initialize(); + + let creditCards = await profileStorage.creditCards.getAll(); + let guid = creditCards[1].guid; + + let onChanged = TestUtils.topicObserved( + "formautofill-storage-changed", + (subject, data) => + data == "remove" && + subject.wrappedJSObject.guid == guid && + subject.wrappedJSObject.collectionName == COLLECTION_NAME + ); + + Assert.equal(creditCards.length, 2); + + profileStorage.creditCards.remove(guid); + await onChanged; + await profileStorage._saveImmediately(); + + profileStorage = new FormAutofillStorage(path); + await profileStorage.initialize(); + + creditCards = await profileStorage.creditCards.getAll(); + + Assert.equal(creditCards.length, 1); + + Assert.equal(await profileStorage.creditCards.get(guid), null); +}); + +MERGE_TESTCASES.forEach(testcase => { + add_task(async function test_merge() { + info("Starting testcase: " + testcase.description); + let profileStorage = await initProfileStorage( + TEST_STORE_FILE_NAME, + [testcase.creditCardInStorage], + "creditCards" + ); + let creditCards = await profileStorage.creditCards.getAll(); + let guid = creditCards[0].guid; + let timeLastModified = creditCards[0].timeLastModified; + // Merge creditCard and verify the guid in notifyObservers subject + let onMerged = TestUtils.topicObserved( + "formautofill-storage-changed", + (subject, data) => + data == "update" && + subject.wrappedJSObject.guid == guid && + subject.wrappedJSObject.collectionName == COLLECTION_NAME + ); + // Force to create sync metadata. + profileStorage.creditCards.pullSyncChanges(); + Assert.equal(getSyncChangeCounter(profileStorage.creditCards, guid), 1); + Assert.ok( + await profileStorage.creditCards.mergeIfPossible( + guid, + testcase.creditCardToMerge + ) + ); + if (!testcase.noNeedToUpdate) { + await onMerged; + } + creditCards = await profileStorage.creditCards.getAll(); + Assert.equal(creditCards.length, 1); + do_check_credit_card_matches(creditCards[0], testcase.expectedCreditCard); + if (!testcase.noNeedToUpdate) { + // Record merging should update timeLastModified and bump the change counter. + Assert.notEqual(creditCards[0].timeLastModified, timeLastModified); + Assert.equal(getSyncChangeCounter(profileStorage.creditCards, guid), 2); + } else { + // Subset record merging should not update timeLastModified and the change + // counter is still the same. + Assert.equal(creditCards[0].timeLastModified, timeLastModified); + Assert.equal(getSyncChangeCounter(profileStorage.creditCards, guid), 1); + } + }); +}); + +add_task(async function test_merge_unable_merge() { + let profileStorage = await initProfileStorage( + TEST_STORE_FILE_NAME, + [TEST_CREDIT_CARD_1], + "creditCards" + ); + + let creditCards = await profileStorage.creditCards.getAll(); + let guid = creditCards[0].guid; + // Force to create sync metadata. + profileStorage.creditCards.pullSyncChanges(); + Assert.equal(getSyncChangeCounter(profileStorage.creditCards, guid), 1); + + // Unable to merge because of conflict + let anotherCreditCard = profileStorage.creditCards._clone(TEST_CREDIT_CARD_1); + anotherCreditCard["cc-name"] = "Foo Bar"; + Assert.equal( + await profileStorage.creditCards.mergeIfPossible(guid, anotherCreditCard), + false + ); + // The change counter is unchanged. + Assert.equal(getSyncChangeCounter(profileStorage.creditCards, guid), 1); + + // Unable to merge because no credit card number + anotherCreditCard = profileStorage.creditCards._clone(TEST_CREDIT_CARD_1); + anotherCreditCard["cc-number"] = ""; + Assert.equal( + await profileStorage.creditCards.mergeIfPossible(guid, anotherCreditCard), + false + ); + // The change counter is still unchanged. + Assert.equal(getSyncChangeCounter(profileStorage.creditCards, guid), 1); +}); + +add_task(async function test_mergeToStorage() { + let profileStorage = await initProfileStorage( + TEST_STORE_FILE_NAME, + [TEST_CREDIT_CARD_3, TEST_CREDIT_CARD_4], + "creditCards" + ); + // Merge a creditCard to storage + let anotherCreditCard = profileStorage.creditCards._clone(TEST_CREDIT_CARD_3); + anotherCreditCard["cc-name"] = "Foo Bar"; + Assert.equal( + (await profileStorage.creditCards.mergeToStorage(anotherCreditCard)).length, + 2 + ); + Assert.equal( + (await profileStorage.creditCards.getAll())[0]["cc-name"], + "Foo Bar" + ); + Assert.equal( + (await profileStorage.creditCards.getAll())[0]["cc-exp"], + "2000-01" + ); + Assert.equal( + (await profileStorage.creditCards.getAll())[1]["cc-name"], + "Foo Bar" + ); + Assert.equal( + (await profileStorage.creditCards.getAll())[1]["cc-exp"], + "2000-01" + ); + + // Empty computed fields shouldn't cause any problem. + Assert.equal( + ( + await profileStorage.creditCards.mergeToStorage( + TEST_CREDIT_CARD_WITH_EMPTY_COMPUTED_FIELD + ) + ).length, + 0 + ); +}); + +add_task(async function test_getDuplicateRecords() { + let profileStorage = await initProfileStorage( + TEST_STORE_FILE_NAME, + [TEST_CREDIT_CARD_3], + "creditCards" + ); + let guid = profileStorage.creditCards._data[0].guid; + + // Absolutely a duplicate. + let getDuplicateRecords = + profileStorage.creditCards.getDuplicateRecords(TEST_CREDIT_CARD_3); + let dupe = (await getDuplicateRecords.next()).value; + Assert.equal(dupe.guid, guid); + + // Absolutely not a duplicate. + getDuplicateRecords = + profileStorage.creditCards.getDuplicateRecords(TEST_CREDIT_CARD_1); + dupe = (await getDuplicateRecords.next()).value; + Assert.equal(dupe, null); + + // Subset with the same number is a duplicate. + let record = Object.assign({}, TEST_CREDIT_CARD_3); + delete record["cc-exp-month"]; + getDuplicateRecords = profileStorage.creditCards.getDuplicateRecords(record); + dupe = (await getDuplicateRecords.next()).value; + Assert.equal(dupe.guid, guid); + + // Superset with the same number is a duplicate. + record = Object.assign({}, TEST_CREDIT_CARD_3); + record["cc-name"] = "John Doe"; + getDuplicateRecords = profileStorage.creditCards.getDuplicateRecords(record); + dupe = (await getDuplicateRecords.next()).value; + Assert.equal(dupe.guid, guid); + + // Numbers with the same last 4 digits shouldn't be treated as a duplicate. + record = Object.assign({}, TEST_CREDIT_CARD_3); + let last4Digits = record["cc-number"].substr(-4); + getDuplicateRecords = profileStorage.creditCards.getDuplicateRecords(record); + dupe = (await getDuplicateRecords.next()).value; + Assert.equal(dupe.guid, guid); + + // This number differs from TEST_CREDIT_CARD_3 by swapping the order of the + // 09 and 90 adjacent digits, which is still a valid credit card number. + record["cc-number"] = "358999378390" + last4Digits; + + // We don't treat numbers with the same last 4 digits as a duplicate. + getDuplicateRecords = profileStorage.creditCards.getDuplicateRecords(record); + dupe = (await getDuplicateRecords.next()).value; + Assert.equal(dupe, null); +}); + +add_task(async function test_getDuplicateRecordsMatch() { + let profileStorage = await initProfileStorage( + TEST_STORE_FILE_NAME, + [TEST_CREDIT_CARD_2], + "creditCards" + ); + let guid = profileStorage.creditCards._data[0].guid; + + // Absolutely a duplicate. + let getDuplicateRecords = + profileStorage.creditCards.getDuplicateRecords(TEST_CREDIT_CARD_2); + let dupe = (await getDuplicateRecords.next()).value; + Assert.equal(dupe.guid, guid); + + // Absolutely not a duplicate. + getDuplicateRecords = + profileStorage.creditCards.getDuplicateRecords(TEST_CREDIT_CARD_1); + dupe = (await getDuplicateRecords.next()).value; + Assert.equal(dupe, null); + + record = Object.assign({}, TEST_CREDIT_CARD_2); + + // We change month from `1` to `2` + record["cc-exp-month"] = 2; + getDuplicateRecords = profileStorage.creditCards.getDuplicateRecords(record); + dupe = (await getDuplicateRecords.next()).value; + Assert.equal(dupe.guid, guid); + + // We change year from `2000` to `2001` + record["cc-exp-year"] = 2001; + getDuplicateRecords = profileStorage.creditCards.getDuplicateRecords(record); + dupe = (await getDuplicateRecords.next()).value; + Assert.equal(dupe.guid, guid); + + // New name, same card + record["cc-name"] = "John Doe"; + getDuplicateRecords = profileStorage.creditCards.getDuplicateRecords(record); + dupe = (await getDuplicateRecords.next()).value; + Assert.equal(dupe.guid, guid); +}); + +add_task(async function test_getMatchRecord() { + let profileStorage = await initProfileStorage( + TEST_STORE_FILE_NAME, + [TEST_CREDIT_CARD_2], + "creditCards" + ); + let guid = profileStorage.creditCards._data[0].guid; + + const TEST_FIELDS = { + "cc-name": "John Doe", + "cc-exp-month": 10, + "cc-exp-year": 2001, + }; + + // Absolutely a match. + let getMatchRecords = + profileStorage.creditCards.getMatchRecords(TEST_CREDIT_CARD_2); + let match = (await getMatchRecords.next()).value; + Assert.equal(match.guid, guid); + + // Subset with the same number is a match. + for (const field of Object.keys(TEST_FIELDS)) { + let record = Object.assign({}, TEST_CREDIT_CARD_2); + delete record[field]; + getMatchRecords = profileStorage.creditCards.getMatchRecords(record); + match = (await getMatchRecords.next()).value; + Assert.equal(match.guid, guid); + } + + // Subset with different number is not a match. + for (const field of Object.keys(TEST_FIELDS)) { + let record = Object.assign({}, TEST_CREDIT_CARD_2, { + "cc-number": TEST_CREDIT_CARD_1["cc-number"], + }); + delete record[field]; + getMatchRecords = profileStorage.creditCards.getMatchRecords(record); + match = (await getMatchRecords.next()).value; + Assert.equal(match, null); + } + + // Superset with the same number is not a match. + for (const [field, value] of Object.entries(TEST_FIELDS)) { + let record = Object.assign({}, TEST_CREDIT_CARD_2); + record[field] = value; + getMatchRecords = profileStorage.creditCards.getMatchRecords(record); + match = (await getMatchRecords.next()).value; + Assert.equal(match, null); + } + + // Superset with different number is not a match. + for (const [field, value] of Object.entries(TEST_FIELDS)) { + let record = Object.assign({}, TEST_CREDIT_CARD_2, { + "cc-number": TEST_CREDIT_CARD_1["cc-number"], + }); + record[field] = value; + getMatchRecords = profileStorage.creditCards.getMatchRecords(record); + match = (await getMatchRecords.next()).value; + Assert.equal(match, null); + } +}); + +add_task(async function test_creditCardFillDisabled() { + Services.prefs.setBoolPref( + "extensions.formautofill.creditCards.enabled", + false + ); + + let path = getTempFile(TEST_STORE_FILE_NAME).path; + let profileStorage = new FormAutofillStorage(path); + await profileStorage.initialize(); + + Assert.equal( + !!profileStorage.creditCards, + true, + "credit card records initialized and available." + ); + + Services.prefs.setBoolPref( + "extensions.formautofill.creditCards.enabled", + true + ); +}); |