summaryrefslogtreecommitdiffstats
path: root/services/sync/tests/unit/test_password_store.js
diff options
context:
space:
mode:
Diffstat (limited to 'services/sync/tests/unit/test_password_store.js')
-rw-r--r--services/sync/tests/unit/test_password_store.js398
1 files changed, 398 insertions, 0 deletions
diff --git a/services/sync/tests/unit/test_password_store.js b/services/sync/tests/unit/test_password_store.js
new file mode 100644
index 0000000000..ed393d6241
--- /dev/null
+++ b/services/sync/tests/unit/test_password_store.js
@@ -0,0 +1,398 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const { LoginRec } = ChromeUtils.importESModule(
+ "resource://services-sync/engines/passwords.sys.mjs"
+);
+const { Service } = ChromeUtils.importESModule(
+ "resource://services-sync/service.sys.mjs"
+);
+const { SyncedRecordsTelemetry } = ChromeUtils.importESModule(
+ "resource://services-sync/telemetry.sys.mjs"
+);
+
+async function checkRecord(
+ name,
+ record,
+ expectedCount,
+ timeCreated,
+ expectedTimeCreated,
+ timePasswordChanged,
+ expectedTimePasswordChanged,
+ recordIsUpdated
+) {
+ let engine = Service.engineManager.get("passwords");
+ let store = engine._store;
+
+ let logins = await Services.logins.searchLoginsAsync({
+ origin: record.hostname,
+ formActionOrigin: record.formSubmitURL,
+ });
+
+ _("Record" + name + ":" + JSON.stringify(logins));
+ _("Count" + name + ":" + logins.length);
+
+ Assert.equal(logins.length, expectedCount);
+
+ if (expectedCount > 0) {
+ Assert.ok(!!(await store.getAllIDs())[record.id]);
+ let stored_record = logins[0].QueryInterface(Ci.nsILoginMetaInfo);
+
+ if (timeCreated !== undefined) {
+ Assert.equal(stored_record.timeCreated, expectedTimeCreated);
+ }
+
+ if (timePasswordChanged !== undefined) {
+ if (recordIsUpdated) {
+ Assert.ok(
+ stored_record.timePasswordChanged >= expectedTimePasswordChanged
+ );
+ } else {
+ Assert.equal(
+ stored_record.timePasswordChanged,
+ expectedTimePasswordChanged
+ );
+ }
+ return stored_record.timePasswordChanged;
+ }
+ } else {
+ Assert.ok(!(await store.getAllIDs())[record.id]);
+ }
+ return undefined;
+}
+
+async function changePassword(
+ name,
+ hostname,
+ password,
+ expectedCount,
+ timeCreated,
+ expectedTimeCreated,
+ timePasswordChanged,
+ expectedTimePasswordChanged,
+ insert,
+ recordIsUpdated
+) {
+ const BOGUS_GUID = "zzzzzz" + hostname;
+ let record = new LoginRec("passwords", BOGUS_GUID);
+ record.cleartext = {
+ id: BOGUS_GUID,
+ hostname,
+ formSubmitURL: hostname,
+ username: "john",
+ password,
+ usernameField: "username",
+ passwordField: "password",
+ };
+
+ if (timeCreated !== undefined) {
+ record.timeCreated = timeCreated;
+ }
+
+ if (timePasswordChanged !== undefined) {
+ record.timePasswordChanged = timePasswordChanged;
+ }
+
+ let engine = Service.engineManager.get("passwords");
+ let store = engine._store;
+
+ if (insert) {
+ let countTelemetry = new SyncedRecordsTelemetry();
+ Assert.equal(
+ (await store.applyIncomingBatch([record], countTelemetry)).length,
+ 0
+ );
+ }
+
+ return checkRecord(
+ name,
+ record,
+ expectedCount,
+ timeCreated,
+ expectedTimeCreated,
+ timePasswordChanged,
+ expectedTimePasswordChanged,
+ recordIsUpdated
+ );
+}
+
+async function test_apply_records_with_times(
+ hostname,
+ timeCreated,
+ timePasswordChanged
+) {
+ // The following record is going to be inserted in the store and it needs
+ // to be found there. Then its timestamps are going to be compared to
+ // the expected values.
+ await changePassword(
+ " ",
+ hostname,
+ "password",
+ 1,
+ timeCreated,
+ timeCreated,
+ timePasswordChanged,
+ timePasswordChanged,
+ true
+ );
+}
+
+async function test_apply_multiple_records_with_times() {
+ // The following records are going to be inserted in the store and they need
+ // to be found there. Then their timestamps are going to be compared to
+ // the expected values.
+ await changePassword(
+ "A",
+ "http://foo.a.com",
+ "password",
+ 1,
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ true
+ );
+ await changePassword(
+ "B",
+ "http://foo.b.com",
+ "password",
+ 1,
+ 1000,
+ 1000,
+ undefined,
+ undefined,
+ true
+ );
+ await changePassword(
+ "C",
+ "http://foo.c.com",
+ "password",
+ 1,
+ undefined,
+ undefined,
+ 1000,
+ 1000,
+ true
+ );
+ await changePassword(
+ "D",
+ "http://foo.d.com",
+ "password",
+ 1,
+ 1000,
+ 1000,
+ 1000,
+ 1000,
+ true
+ );
+
+ // The following records are not going to be inserted in the store and they
+ // are not going to be found there.
+ await changePassword(
+ "NotInStoreA",
+ "http://foo.aaaa.com",
+ "password",
+ 0,
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ false
+ );
+ await changePassword(
+ "NotInStoreB",
+ "http://foo.bbbb.com",
+ "password",
+ 0,
+ 1000,
+ 1000,
+ undefined,
+ undefined,
+ false
+ );
+ await changePassword(
+ "NotInStoreC",
+ "http://foo.cccc.com",
+ "password",
+ 0,
+ undefined,
+ undefined,
+ 1000,
+ 1000,
+ false
+ );
+ await changePassword(
+ "NotInStoreD",
+ "http://foo.dddd.com",
+ "password",
+ 0,
+ 1000,
+ 1000,
+ 1000,
+ 1000,
+ false
+ );
+}
+
+async function test_apply_same_record_with_different_times() {
+ // The following record is going to be inserted multiple times in the store
+ // and it needs to be found there. Then its timestamps are going to be
+ // compared to the expected values.
+
+ /* eslint-disable no-unused-vars */
+ /* The eslint linter thinks that timePasswordChanged is unused, even though
+ it is passed as an argument to changePassword. */
+ var timePasswordChanged = 100;
+ timePasswordChanged = await changePassword(
+ "A",
+ "http://a.tn",
+ "password",
+ 1,
+ 100,
+ 100,
+ 100,
+ timePasswordChanged,
+ true
+ );
+ timePasswordChanged = await changePassword(
+ "A",
+ "http://a.tn",
+ "password",
+ 1,
+ 100,
+ 100,
+ 800,
+ timePasswordChanged,
+ true,
+ true
+ );
+ timePasswordChanged = await changePassword(
+ "A",
+ "http://a.tn",
+ "password",
+ 1,
+ 500,
+ 100,
+ 800,
+ timePasswordChanged,
+ true,
+ true
+ );
+ timePasswordChanged = await changePassword(
+ "A",
+ "http://a.tn",
+ "password2",
+ 1,
+ 500,
+ 100,
+ 1536213005222,
+ timePasswordChanged,
+ true,
+ true
+ );
+ timePasswordChanged = await changePassword(
+ "A",
+ "http://a.tn",
+ "password2",
+ 1,
+ 500,
+ 100,
+ 800,
+ timePasswordChanged,
+ true,
+ true
+ );
+ /* eslint-enable no-unused-vars */
+}
+
+async function test_LoginRec_toString(store, recordData) {
+ let rec = await store.createRecord(recordData.id);
+ ok(rec);
+ ok(!rec.toString().includes(rec.password));
+}
+
+add_task(async function run_test() {
+ const BOGUS_GUID_A = "zzzzzzzzzzzz";
+ const BOGUS_GUID_B = "yyyyyyyyyyyy";
+ let recordA = new LoginRec("passwords", BOGUS_GUID_A);
+ let recordB = new LoginRec("passwords", BOGUS_GUID_B);
+ recordA.cleartext = {
+ id: BOGUS_GUID_A,
+ hostname: "http://foo.bar.com",
+ formSubmitURL: "http://foo.bar.com",
+ httpRealm: "secure",
+ username: "john",
+ password: "smith",
+ usernameField: "username",
+ passwordField: "password",
+ };
+ recordB.cleartext = {
+ id: BOGUS_GUID_B,
+ hostname: "http://foo.baz.com",
+ formSubmitURL: "http://foo.baz.com",
+ username: "john",
+ password: "smith",
+ usernameField: "username",
+ passwordField: "password",
+ unknownStr: "an unknown string from another field",
+ };
+
+ let engine = Service.engineManager.get("passwords");
+ let store = engine._store;
+
+ try {
+ let countTelemetry = new SyncedRecordsTelemetry();
+ Assert.equal(
+ (await store.applyIncomingBatch([recordA, recordB], countTelemetry))
+ .length,
+ 0
+ );
+
+ // Only the good record makes it to Services.logins.
+ let badLogins = await Services.logins.searchLoginsAsync({
+ origin: recordA.hostname,
+ formActionOrigin: recordA.formSubmitURL,
+ httpRealm: recordA.httpRealm,
+ });
+ let goodLogins = await Services.logins.searchLoginsAsync({
+ origin: recordB.hostname,
+ formActionOrigin: recordB.formSubmitURL,
+ });
+
+ _("Bad: " + JSON.stringify(badLogins));
+ _("Good: " + JSON.stringify(goodLogins));
+ _("Count: " + badLogins.length + ", " + goodLogins.length);
+
+ Assert.equal(goodLogins.length, 1);
+ Assert.equal(badLogins.length, 0);
+
+ // applyIncoming should've put any unknown fields from the server
+ // into a catch-all unknownFields field
+ Assert.equal(
+ goodLogins[0].unknownFields,
+ JSON.stringify({
+ unknownStr: "an unknown string from another field",
+ })
+ );
+
+ Assert.ok(!!(await store.getAllIDs())[BOGUS_GUID_B]);
+ Assert.ok(!(await store.getAllIDs())[BOGUS_GUID_A]);
+
+ await test_LoginRec_toString(store, recordB);
+
+ await test_apply_records_with_times(
+ "http://afoo.baz.com",
+ undefined,
+ undefined
+ );
+ await test_apply_records_with_times("http://bfoo.baz.com", 1000, undefined);
+ await test_apply_records_with_times("http://cfoo.baz.com", undefined, 2000);
+ await test_apply_records_with_times("http://dfoo.baz.com", 1000, 2000);
+
+ await test_apply_multiple_records_with_times();
+
+ await test_apply_same_record_with_different_times();
+ } finally {
+ await store.wipe();
+ }
+});