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 /toolkit/components/passwordmgr/test/unit/test_loginsBackup.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 'toolkit/components/passwordmgr/test/unit/test_loginsBackup.js')
-rw-r--r-- | toolkit/components/passwordmgr/test/unit/test_loginsBackup.js | 218 |
1 files changed, 218 insertions, 0 deletions
diff --git a/toolkit/components/passwordmgr/test/unit/test_loginsBackup.js b/toolkit/components/passwordmgr/test/unit/test_loginsBackup.js new file mode 100644 index 0000000000..775f6f5486 --- /dev/null +++ b/toolkit/components/passwordmgr/test/unit/test_loginsBackup.js @@ -0,0 +1,218 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Tests if logins-backup.json is used correctly in the event that logins.json is missing or corrupt. + */ + +"use strict"; + +ChromeUtils.defineESModuleGetters(this, { + LoginStore: "resource://gre/modules/LoginStore.sys.mjs", +}); +const { TestUtils } = ChromeUtils.importESModule( + "resource://testing-common/TestUtils.sys.mjs" +); +const { TelemetryTestUtils } = ChromeUtils.importESModule( + "resource://testing-common/TelemetryTestUtils.sys.mjs" +); + +const rawLogin1 = { + id: 1, + hostname: "http://www.example.com", + httpRealm: null, + formSubmitURL: "http://www.example.com", + usernameField: "field_" + String.fromCharCode(533, 537, 7570, 345), + passwordField: "field_" + String.fromCharCode(421, 259, 349, 537), + encryptedUsername: "(test)", + encryptedPassword: "(test)", + guid: "(test)", + encType: Ci.nsILoginManagerCrypto.ENCTYPE_SDR, + timeCreated: Date.now(), + timeLastUsed: Date.now(), + timePasswordChanged: Date.now(), + timesUsed: 1, +}; + +const rawLogin2 = { + id: 2, + hostname: "http://www.example2.com", + httpRealm: null, + formSubmitURL: "http://www.example2.com", + usernameField: "field_2" + String.fromCharCode(533, 537, 7570, 345), + passwordField: "field_2" + String.fromCharCode(421, 259, 349, 537), + encryptedUsername: "(test2)", + encryptedPassword: "(test2)", + guid: "(test2)", + encType: Ci.nsILoginManagerCrypto.ENCTYPE_SDR, + timeCreated: Date.now(), + timeLastUsed: Date.now(), + timePasswordChanged: Date.now(), + timesUsed: 1, +}; + +// Enable the collection (during test) for all products so even products +// that don't collect the data will be able to run the test without failure. +Services.prefs.setBoolPref( + "toolkit.telemetry.testing.overrideProductsCheck", + true +); + +/** + * Tests that logins-backup.json can be used by JSONFile.load() when logins.json is missing or cannot be read. + */ +add_task(async function test_logins_store_missing_or_corrupt_with_backup() { + const loginsStorePath = PathUtils.join(PathUtils.profileDir, "logins.json"); + const loginsStoreBackup = PathUtils.join( + PathUtils.profileDir, + "logins-backup.json" + ); + + // Get store.data ready. + let store = new LoginStore(loginsStorePath, loginsStoreBackup); + await store.load(); + + // Files should not exist at start up. + Assert.ok(!(await IOUtils.exists(store.path)), "No store file at start up"); + Assert.ok( + !(await IOUtils.exists(store._options.backupTo)), + "No backup file at start up" + ); + + // Add logins to create logins.json and logins-backup.json. + store.data.logins.push(rawLogin1); + await store._save(); + Assert.ok(await IOUtils.exists(store.path)); + + store.data.logins.push(rawLogin2); + await store._save(); + Assert.ok(await IOUtils.exists(store._options.backupTo)); + + // Remove logins.json and see if logins-backup.json will be used. + await IOUtils.remove(store.path); + store.data.logins = []; + store.dataReady = false; + Assert.ok(!(await IOUtils.exists(store.path))); + Assert.ok(await IOUtils.exists(store._options.backupTo)); + + // Clear any telemetry events recorded in the jsonfile category previously. + Services.telemetry.clearEvents(); + + await store.load(); + + Assert.ok( + await IOUtils.exists(store.path), + "logins.json is restored as expected after it went missing" + ); + + Assert.ok(await IOUtils.exists(store._options.backupTo)); + Assert.equal( + store.data.logins.length, + 1, + "Logins backup was used successfully when logins.json was missing" + ); + + TelemetryTestUtils.assertEvents( + [ + ["jsonfile", "load", "logins"], + ["jsonfile", "load", "logins", "used_backup"], + ], + {}, + { clear: true } + ); + info( + "Telemetry was recorded accurately when logins-backup.json is used when logins.json was missing" + ); + + // Corrupt the logins.json file. + let string = '{"logins":[{"hostname":"http://www.example.com","id":1,'; + await IOUtils.writeUTF8(store.path, string, { + tmpPath: `${store.path}.tmp`, + }); + + // Clear events recorded in the jsonfile category previously. + Services.telemetry.clearEvents(); + + // Try to load the corrupt file. + store.data.logins = []; + store.dataReady = false; + await store.load(); + + Assert.ok( + await IOUtils.exists(`${store.path}.corrupt`), + "logins.json.corrupt created" + ); + Assert.ok( + await IOUtils.exists(store.path), + "logins.json is restored after it was corrupted" + ); + + // Data should be loaded from logins-backup.json. + Assert.ok(await IOUtils.exists(store._options.backupTo)); + Assert.equal( + store.data.logins.length, + 1, + "Logins backup was used successfully when logins.json was corrupt" + ); + + TelemetryTestUtils.assertEvents( + [ + ["jsonfile", "load", "logins", ""], + ["jsonfile", "load", "logins", "invalid_json"], + ["jsonfile", "load", "logins", "used_backup"], + ], + {}, + { clear: true } + ); + info( + "Telemetry was recorded accurately when logins-backup.json is used when logins.json was corrupt" + ); + + // Clean up before we start the second part of the test. + await IOUtils.remove(`${store.path}.corrupt`); + + // Test that the backup file can be used by JSONFile.ensureDataReady() correctly when logins.json is missing. + // Remove logins.json + await IOUtils.remove(store.path); + store.data.logins = []; + store.dataReady = false; + Assert.ok(!(await IOUtils.exists(store.path))); + Assert.ok(await IOUtils.exists(store._options.backupTo)); + + store.ensureDataReady(); + + // Important to check here if logins.json is restored as expected + // after it went missing. + await IOUtils.exists(store.path); + + Assert.ok(await IOUtils.exists(store._options.backupTo)); + await TestUtils.waitForCondition(() => { + return store.data.logins.length == 1; + }); + + // Test that the backup file is used by JSONFile.ensureDataReady() when logins.json is corrupt. + // Corrupt the logins.json file. + await IOUtils.writeUTF8(store.path, string, { + tmpPath: `${store.path}.tmp`, + }); + + // Try to load the corrupt file. + store.data.logins = []; + store.dataReady = false; + store.ensureDataReady(); + + Assert.ok( + await IOUtils.exists(`${store.path}.corrupt`), + "logins.json.corrupt created" + ); + Assert.ok( + await IOUtils.exists(store.path), + "logins.json is restored after it was corrupted" + ); + + // Data should be loaded from logins-backup.json. + Assert.ok(await IOUtils.exists(store._options.backupTo)); + await TestUtils.waitForCondition(() => { + return store.data.logins.length == 1; + }); +}); |