summaryrefslogtreecommitdiffstats
path: root/toolkit/components/satchel/test/unit/test_history_api.js
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/components/satchel/test/unit/test_history_api.js')
-rw-r--r--toolkit/components/satchel/test/unit/test_history_api.js485
1 files changed, 485 insertions, 0 deletions
diff --git a/toolkit/components/satchel/test/unit/test_history_api.js b/toolkit/components/satchel/test/unit/test_history_api.js
new file mode 100644
index 0000000000..91da24696c
--- /dev/null
+++ b/toolkit/components/satchel/test/unit/test_history_api.js
@@ -0,0 +1,485 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var testnum = 0;
+var dbConnection; // used for deleted table tests
+
+async function countDeletedEntries(expected) {
+ let stmt = "SELECT COUNT(*) AS numEntries FROM moz_deleted_formhistory";
+ try {
+ let requiredRow = await dbConnection.executeCached(stmt);
+ Assert.equal(expected, requiredRow[0].getResultByName("numEntries"));
+ } catch (error) {
+ do_throw("Error occurred counting deleted entries: " + error);
+ }
+}
+
+async function checkTimeDeleted(guid, checkFunction) {
+ let stmt =
+ "SELECT timeDeleted FROM moz_deleted_formhistory WHERE guid = :guid";
+ let params = { guid };
+
+ try {
+ let requiredRow = await dbConnection.executeCached(stmt, params);
+ checkFunction(requiredRow[0].getResultByName("timeDeleted"));
+ } catch (error) {
+ do_throw("Error occurred getting deleted entries: " + error);
+ }
+}
+
+function promiseUpdateEntry(op, name, value) {
+ let change = { op };
+ if (name !== null) {
+ change.fieldname = name;
+ }
+ if (value !== null) {
+ change.value = value;
+ }
+ return promiseUpdate(change);
+}
+
+add_task(async function () {
+ let oldSupportsDeletedTable = FormHistory._supportsDeletedTable;
+ FormHistory._supportsDeletedTable = true;
+
+ try {
+ // ===== test init =====
+ let testfile = do_get_file("formhistory_apitest.sqlite");
+ let profileDir = Services.dirsvc.get("ProfD", Ci.nsIFile);
+
+ // Cleanup from any previous tests or failures.
+ let destFile = profileDir.clone();
+ destFile.append("formhistory.sqlite");
+ if (destFile.exists()) {
+ destFile.remove(false);
+ }
+
+ testfile.copyTo(profileDir, "formhistory.sqlite");
+
+ function checkExists(num) {
+ Assert.ok(num > 0);
+ }
+ function checkNotExists(num) {
+ Assert.ok(num == 0);
+ }
+
+ // ===== 1 =====
+ // Check initial state is as expected
+ testnum++;
+ await promiseCountEntries("name-A", null, checkExists);
+ await promiseCountEntries("name-B", null, checkExists);
+ await promiseCountEntries("name-C", null, checkExists);
+ await promiseCountEntries("name-D", null, checkExists);
+ await promiseCountEntries("name-A", "value-A", checkExists);
+ await promiseCountEntries("name-B", "value-B1", checkExists);
+ await promiseCountEntries("name-B", "value-B2", checkExists);
+ await promiseCountEntries("name-C", "value-C", checkExists);
+ await promiseCountEntries("name-D", "value-D", checkExists);
+ // time-A/B/C/D checked below.
+
+ // Delete anything from the deleted table
+ let dbFile = Services.dirsvc.get("ProfD", Ci.nsIFile).clone();
+ dbFile.append("formhistory.sqlite");
+
+ dbConnection = await Sqlite.openConnection({
+ path: dbFile.path,
+ sharedMemoryCache: false,
+ });
+
+ let stmt = "DELETE FROM moz_deleted_formhistory";
+ try {
+ await dbConnection.executeCached(stmt);
+ } catch (error) {
+ do_throw("Error occurred counting deleted all entries: " + error);
+ }
+
+ // ===== 2 =====
+ // Test looking for nonexistent / bogus data.
+ testnum++;
+ await promiseCountEntries("blah", null, checkNotExists);
+ await promiseCountEntries("", null, checkNotExists);
+ await promiseCountEntries("name-A", "blah", checkNotExists);
+ await promiseCountEntries("name-A", "", checkNotExists);
+ await promiseCountEntries("name-A", null, checkExists);
+ await promiseCountEntries("blah", "value-A", checkNotExists);
+ await promiseCountEntries("", "value-A", checkNotExists);
+ await promiseCountEntries(null, "value-A", checkExists);
+
+ // Cannot use promiseCountEntries when name and value are null
+ // because it treats null values as not set
+ // and here a search should be done explicity for null.
+ let count = await FormHistory.count({ fieldname: null, value: null });
+ checkNotExists(count);
+
+ // ===== 3 =====
+ // Test removeEntriesForName with a single matching value
+ testnum++;
+ await promiseUpdateEntry("remove", "name-A", null);
+
+ await promiseCountEntries("name-A", "value-A", checkNotExists);
+ await promiseCountEntries("name-B", "value-B1", checkExists);
+ await promiseCountEntries("name-B", "value-B2", checkExists);
+ await promiseCountEntries("name-C", "value-C", checkExists);
+ await promiseCountEntries("name-D", "value-D", checkExists);
+ await countDeletedEntries(1);
+
+ // ===== 4 =====
+ // Test removeEntriesForName with multiple matching values
+ testnum++;
+ await promiseUpdateEntry("remove", "name-B", null);
+
+ await promiseCountEntries("name-A", "value-A", checkNotExists);
+ await promiseCountEntries("name-B", "value-B1", checkNotExists);
+ await promiseCountEntries("name-B", "value-B2", checkNotExists);
+ await promiseCountEntries("name-C", "value-C", checkExists);
+ await promiseCountEntries("name-D", "value-D", checkExists);
+ await countDeletedEntries(3);
+
+ // ===== 5 =====
+ // Test removing by time range (single entry, not surrounding entries)
+ testnum++;
+ await promiseCountEntries("time-A", null, checkExists); // firstUsed=1000, lastUsed=1000
+ await promiseCountEntries("time-B", null, checkExists); // firstUsed=1000, lastUsed=1099
+ await promiseCountEntries("time-C", null, checkExists); // firstUsed=1099, lastUsed=1099
+ await promiseCountEntries("time-D", null, checkExists); // firstUsed=2001, lastUsed=2001
+ await promiseUpdate({
+ op: "remove",
+ firstUsedStart: 1050,
+ firstUsedEnd: 2000,
+ });
+
+ await promiseCountEntries("time-A", null, checkExists);
+ await promiseCountEntries("time-B", null, checkExists);
+ await promiseCountEntries("time-C", null, checkNotExists);
+ await promiseCountEntries("time-D", null, checkExists);
+ await countDeletedEntries(4);
+
+ // ===== 6 =====
+ // Test removing by time range (multiple entries)
+ testnum++;
+ await promiseUpdate({
+ op: "remove",
+ firstUsedStart: 1000,
+ firstUsedEnd: 2000,
+ });
+
+ await promiseCountEntries("time-A", null, checkNotExists);
+ await promiseCountEntries("time-B", null, checkNotExists);
+ await promiseCountEntries("time-C", null, checkNotExists);
+ await promiseCountEntries("time-D", null, checkExists);
+ await countDeletedEntries(6);
+
+ // ===== 7 =====
+ // test removeAllEntries
+ testnum++;
+ await promiseUpdateEntry("remove", null, null);
+
+ await promiseCountEntries("name-C", null, checkNotExists);
+ await promiseCountEntries("name-D", null, checkNotExists);
+ await promiseCountEntries("name-C", "value-C", checkNotExists);
+ await promiseCountEntries("name-D", "value-D", checkNotExists);
+
+ await promiseCountEntries(null, null, checkNotExists);
+ await countDeletedEntries(6);
+
+ // ===== 8 =====
+ // Add a single entry back
+ testnum++;
+ await promiseUpdateEntry("add", "newname-A", "newvalue-A");
+ await promiseCountEntries("newname-A", "newvalue-A", checkExists);
+
+ // ===== 9 =====
+ // Remove the single entry
+ testnum++;
+ await promiseUpdateEntry("remove", "newname-A", "newvalue-A");
+ await promiseCountEntries("newname-A", "newvalue-A", checkNotExists);
+
+ // ===== 10 =====
+ // Add a single entry
+ testnum++;
+ await promiseUpdateEntry("add", "field1", "value1");
+ await promiseCountEntries("field1", "value1", checkExists);
+
+ let processFirstResult = function processResults(results) {
+ // Only handle the first result
+ if (results.length) {
+ let result = results[0];
+ return [
+ result.timesUsed,
+ result.firstUsed,
+ result.lastUsed,
+ result.guid,
+ ];
+ }
+ return undefined;
+ };
+
+ let results = await FormHistory.search(
+ ["timesUsed", "firstUsed", "lastUsed"],
+ { fieldname: "field1", value: "value1" }
+ );
+ let [timesUsed, firstUsed, lastUsed] = processFirstResult(results);
+ Assert.equal(1, timesUsed);
+ Assert.ok(firstUsed > 0);
+ Assert.ok(lastUsed > 0);
+ await promiseCountEntries(null, null, num => Assert.equal(num, 1));
+
+ // ===== 11 =====
+ // Add another single entry
+ testnum++;
+ await promiseUpdateEntry("add", "field1", "value1b");
+ await promiseCountEntries("field1", "value1", checkExists);
+ await promiseCountEntries("field1", "value1b", checkExists);
+ await promiseCountEntries(null, null, num => Assert.equal(num, 2));
+
+ // ===== 12 =====
+ // Update a single entry
+ testnum++;
+
+ results = await FormHistory.search(["guid"], {
+ fieldname: "field1",
+ value: "value1",
+ });
+ let guid = processFirstResult(results)[3];
+
+ await promiseUpdate({ op: "update", guid, value: "modifiedValue" });
+ await promiseCountEntries("field1", "modifiedValue", checkExists);
+ await promiseCountEntries("field1", "value1", checkNotExists);
+ await promiseCountEntries("field1", "value1b", checkExists);
+ await promiseCountEntries(null, null, num => Assert.equal(num, 2));
+
+ // ===== 13 =====
+ // Add a single entry with times
+ testnum++;
+ await promiseUpdate({
+ op: "add",
+ fieldname: "field2",
+ value: "value2",
+ timesUsed: 20,
+ firstUsed: 100,
+ lastUsed: 500,
+ });
+
+ results = await FormHistory.search(["timesUsed", "firstUsed", "lastUsed"], {
+ fieldname: "field2",
+ value: "value2",
+ });
+ [timesUsed, firstUsed, lastUsed] = processFirstResult(results);
+
+ Assert.equal(20, timesUsed);
+ Assert.equal(100, firstUsed);
+ Assert.equal(500, lastUsed);
+ await promiseCountEntries(null, null, num => Assert.equal(num, 3));
+
+ // ===== 14 =====
+ // Bump an entry, which updates its lastUsed field
+ testnum++;
+ await promiseUpdate({
+ op: "bump",
+ fieldname: "field2",
+ value: "value2",
+ timesUsed: 20,
+ firstUsed: 100,
+ lastUsed: 500,
+ });
+ results = await FormHistory.search(["timesUsed", "firstUsed", "lastUsed"], {
+ fieldname: "field2",
+ value: "value2",
+ });
+ [timesUsed, firstUsed, lastUsed] = processFirstResult(results);
+ Assert.equal(21, timesUsed);
+ Assert.equal(100, firstUsed);
+ Assert.ok(lastUsed > 500);
+ await promiseCountEntries(null, null, num => Assert.equal(num, 3));
+
+ // ===== 15 =====
+ // Bump an entry that does not exist
+ testnum++;
+ await promiseUpdate({
+ op: "bump",
+ fieldname: "field3",
+ value: "value3",
+ timesUsed: 10,
+ firstUsed: 50,
+ lastUsed: 400,
+ });
+ results = await FormHistory.search(["timesUsed", "firstUsed", "lastUsed"], {
+ fieldname: "field3",
+ value: "value3",
+ });
+ [timesUsed, firstUsed, lastUsed] = processFirstResult(results);
+ Assert.equal(10, timesUsed);
+ Assert.equal(50, firstUsed);
+ Assert.equal(400, lastUsed);
+ await promiseCountEntries(null, null, num => Assert.equal(num, 4));
+
+ // ===== 16 =====
+ // Bump an entry with a guid
+ testnum++;
+ results = await FormHistory.search(["guid"], {
+ fieldname: "field3",
+ value: "value3",
+ });
+ guid = processFirstResult(results)[3];
+ await promiseUpdate({
+ op: "bump",
+ guid,
+ timesUsed: 20,
+ firstUsed: 55,
+ lastUsed: 400,
+ });
+ results = await FormHistory.search(["timesUsed", "firstUsed", "lastUsed"], {
+ fieldname: "field3",
+ value: "value3",
+ });
+ [timesUsed, firstUsed, lastUsed] = processFirstResult(results);
+ Assert.equal(11, timesUsed);
+ Assert.equal(50, firstUsed);
+ Assert.ok(lastUsed > 400);
+ await promiseCountEntries(null, null, num => Assert.equal(num, 4));
+
+ // ===== 17 =====
+ // Remove an entry
+ testnum++;
+ await countDeletedEntries(7);
+
+ results = await FormHistory.search(["guid"], {
+ fieldname: "field1",
+ value: "value1b",
+ });
+ guid = processFirstResult(results)[3];
+
+ await promiseUpdate({ op: "remove", guid });
+ await promiseCountEntries("field1", "modifiedValue", checkExists);
+ await promiseCountEntries("field1", "value1b", checkNotExists);
+ await promiseCountEntries(null, null, num => Assert.equal(num, 3));
+
+ await countDeletedEntries(8);
+ await checkTimeDeleted(guid, timeDeleted => Assert.ok(timeDeleted > 10000));
+
+ // ===== 18 =====
+ // Add yet another single entry
+ testnum++;
+ await promiseUpdate({
+ op: "add",
+ fieldname: "field4",
+ value: "value4",
+ timesUsed: 5,
+ firstUsed: 230,
+ lastUsed: 600,
+ });
+ await promiseCountEntries(null, null, num => Assert.equal(num, 4));
+
+ // ===== 19 =====
+ // Remove an entry by time
+ testnum++;
+ await promiseUpdate({
+ op: "remove",
+ firstUsedStart: 60,
+ firstUsedEnd: 250,
+ });
+ await promiseCountEntries("field1", "modifiedValue", checkExists);
+ await promiseCountEntries("field2", "value2", checkNotExists);
+ await promiseCountEntries("field3", "value3", checkExists);
+ await promiseCountEntries("field4", "value4", checkNotExists);
+ await promiseCountEntries(null, null, num => Assert.equal(num, 2));
+ await countDeletedEntries(10);
+
+ // ===== 20 =====
+ // Bump multiple existing entries at once
+ testnum++;
+
+ await promiseUpdate([
+ {
+ op: "add",
+ fieldname: "field5",
+ value: "value5",
+ timesUsed: 5,
+ firstUsed: 230,
+ lastUsed: 600,
+ },
+ {
+ op: "add",
+ fieldname: "field6",
+ value: "value6",
+ timesUsed: 12,
+ firstUsed: 430,
+ lastUsed: 700,
+ },
+ ]);
+ await promiseCountEntries(null, null, num => Assert.equal(num, 4));
+
+ await promiseUpdate([
+ { op: "bump", fieldname: "field5", value: "value5" },
+ { op: "bump", fieldname: "field6", value: "value6" },
+ ]);
+ results = await FormHistory.search(
+ ["fieldname", "timesUsed", "firstUsed", "lastUsed"],
+ {}
+ );
+
+ Assert.equal(6, results[2].timesUsed);
+ Assert.equal(13, results[3].timesUsed);
+ Assert.equal(230, results[2].firstUsed);
+ Assert.equal(430, results[3].firstUsed);
+ Assert.ok(results[2].lastUsed > 600);
+ Assert.ok(results[3].lastUsed > 700);
+
+ await promiseCountEntries(null, null, num => Assert.equal(num, 4));
+
+ // ===== 21 =====
+ // Check update fails if form history is disabled and the operation is not a
+ // pure removal.
+ testnum++;
+ Services.prefs.setBoolPref("browser.formfill.enable", false);
+
+ // Cannot use arrow functions, see bug 1237961.
+ await Assert.rejects(
+ promiseUpdate({ op: "bump", fieldname: "field5", value: "value5" }),
+ /Form history is disabled, only remove operations are allowed/,
+ "bumping when form history is disabled should fail"
+ );
+ await Assert.rejects(
+ promiseUpdate({ op: "add", fieldname: "field5", value: "value5" }),
+ /Form history is disabled, only remove operations are allowed/,
+ "Adding when form history is disabled should fail"
+ );
+ await Assert.rejects(
+ promiseUpdate([
+ { op: "update", fieldname: "field5", value: "value5" },
+ { op: "remove", fieldname: "field5", value: "value5" },
+ ]),
+ /Form history is disabled, only remove operations are allowed/,
+ "mixed operations when form history is disabled should fail"
+ );
+ await Assert.rejects(
+ promiseUpdate([
+ null,
+ undefined,
+ "",
+ 1,
+ {},
+ { op: "remove", fieldname: "field5", value: "value5" },
+ ]),
+ /Form history is disabled, only remove operations are allowed/,
+ "Invalid entries when form history is disabled should fail"
+ );
+
+ // Remove should work though.
+ await promiseUpdate([
+ { op: "remove", fieldname: "field5", value: null },
+ { op: "remove", fieldname: null, value: null },
+ ]);
+ Services.prefs.clearUserPref("browser.formfill.enable");
+ } catch (e) {
+ throw new Error(`FAILED in test #${testnum} -- ${e}`);
+ } finally {
+ FormHistory._supportsDeletedTable = oldSupportsDeletedTable;
+ await dbConnection.close(do_test_finished);
+ }
+});
+
+function run_test() {
+ return run_next_test();
+}