summaryrefslogtreecommitdiffstats
path: root/browser/components/urlbar/tests/quicksuggest/unit/test_merinoClient_sessions.js
diff options
context:
space:
mode:
Diffstat (limited to 'browser/components/urlbar/tests/quicksuggest/unit/test_merinoClient_sessions.js')
-rw-r--r--browser/components/urlbar/tests/quicksuggest/unit/test_merinoClient_sessions.js402
1 files changed, 402 insertions, 0 deletions
diff --git a/browser/components/urlbar/tests/quicksuggest/unit/test_merinoClient_sessions.js b/browser/components/urlbar/tests/quicksuggest/unit/test_merinoClient_sessions.js
new file mode 100644
index 0000000000..70e970af8a
--- /dev/null
+++ b/browser/components/urlbar/tests/quicksuggest/unit/test_merinoClient_sessions.js
@@ -0,0 +1,402 @@
+/* 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/. */
+
+// Test for MerinoClient sessions.
+
+"use strict";
+
+const { MerinoClient } = ChromeUtils.importESModule(
+ "resource:///modules/MerinoClient.sys.mjs"
+);
+
+const { SEARCH_PARAMS } = MerinoClient;
+
+let gClient;
+
+add_task(async function init() {
+ gClient = new MerinoClient();
+ await MerinoTestUtils.server.start();
+});
+
+// In a single session, all requests should use the same session ID and the
+// sequence number should be incremented.
+add_task(async function singleSession() {
+ for (let i = 0; i < 3; i++) {
+ let query = "search" + i;
+ await gClient.fetch({ query });
+
+ MerinoTestUtils.server.checkAndClearRequests([
+ {
+ params: {
+ [SEARCH_PARAMS.QUERY]: query,
+ [SEARCH_PARAMS.SEQUENCE_NUMBER]: i,
+ },
+ },
+ ]);
+ }
+
+ gClient.resetSession();
+});
+
+// Different sessions should use different session IDs and the sequence number
+// should be reset.
+add_task(async function manySessions() {
+ for (let i = 0; i < 3; i++) {
+ let query = "search" + i;
+ await gClient.fetch({ query });
+
+ MerinoTestUtils.server.checkAndClearRequests([
+ {
+ params: {
+ [SEARCH_PARAMS.QUERY]: query,
+ [SEARCH_PARAMS.SEQUENCE_NUMBER]: 0,
+ },
+ },
+ ]);
+
+ gClient.resetSession();
+ }
+});
+
+// Tests two consecutive fetches:
+//
+// 1. Start a fetch
+// 2. Wait for the mock Merino server to receive the request
+// 3. Start a second fetch before the client receives the response
+//
+// The first fetch will be canceled by the second but the sequence number in the
+// second fetch should still be incremented.
+add_task(async function twoFetches_wait() {
+ for (let i = 0; i < 3; i++) {
+ // Send the first response after a delay to make sure the client will not
+ // receive it before we start the second fetch.
+ MerinoTestUtils.server.response.delay = UrlbarPrefs.get("merino.timeoutMs");
+
+ // Start the first fetch but don't wait for it to finish.
+ let requestPromise = MerinoTestUtils.server.waitForNextRequest();
+ let query1 = "search" + i;
+ gClient.fetch({ query: query1 });
+
+ // Wait until the first request is received before starting the second
+ // fetch, which will cancel the first. The response doesn't need to be
+ // delayed, so remove it to make the test run faster.
+ await requestPromise;
+ delete MerinoTestUtils.server.response.delay;
+ let query2 = query1 + "again";
+ await gClient.fetch({ query: query2 });
+
+ // The sequence number should have been incremented for each fetch.
+ MerinoTestUtils.server.checkAndClearRequests([
+ {
+ params: {
+ [SEARCH_PARAMS.QUERY]: query1,
+ [SEARCH_PARAMS.SEQUENCE_NUMBER]: 2 * i,
+ },
+ },
+ {
+ params: {
+ [SEARCH_PARAMS.QUERY]: query2,
+ [SEARCH_PARAMS.SEQUENCE_NUMBER]: 2 * i + 1,
+ },
+ },
+ ]);
+ }
+
+ gClient.resetSession();
+});
+
+// Tests two consecutive fetches:
+//
+// 1. Start a fetch
+// 2. Immediately start a second fetch
+//
+// The first fetch will be canceled by the second but the sequence number in the
+// second fetch should still be incremented.
+add_task(async function twoFetches_immediate() {
+ for (let i = 0; i < 3; i++) {
+ // Send the first response after a delay to make sure the client will not
+ // receive it before we start the second fetch.
+ MerinoTestUtils.server.response.delay =
+ 100 * UrlbarPrefs.get("merino.timeoutMs");
+
+ // Start the first fetch but don't wait for it to finish.
+ let query1 = "search" + i;
+ gClient.fetch({ query: query1 });
+
+ // Immediately do a second fetch that cancels the first. The response
+ // doesn't need to be delayed, so remove it to make the test run faster.
+ delete MerinoTestUtils.server.response.delay;
+ let query2 = query1 + "again";
+ await gClient.fetch({ query: query2 });
+
+ // The sequence number should have been incremented for each fetch, but the
+ // first won't have reached the server since it was immediately canceled.
+ MerinoTestUtils.server.checkAndClearRequests([
+ {
+ params: {
+ [SEARCH_PARAMS.QUERY]: query2,
+ [SEARCH_PARAMS.SEQUENCE_NUMBER]: 2 * i + 1,
+ },
+ },
+ ]);
+ }
+
+ gClient.resetSession();
+});
+
+// When a network error occurs, the sequence number should still be incremented.
+add_task(async function networkError() {
+ for (let i = 0; i < 3; i++) {
+ // Do a fetch that fails with a network error.
+ let query1 = "search" + i;
+ await MerinoTestUtils.server.withNetworkError(async () => {
+ await gClient.fetch({ query: query1 });
+ });
+
+ Assert.equal(
+ gClient.lastFetchStatus,
+ "network_error",
+ "The request failed with a network error"
+ );
+
+ // Do another fetch that successfully finishes.
+ let query2 = query1 + "again";
+ await gClient.fetch({ query: query2 });
+
+ Assert.equal(
+ gClient.lastFetchStatus,
+ "success",
+ "The request completed successfully"
+ );
+
+ // Only the second request should have been received but the sequence number
+ // should have been incremented for each.
+ MerinoTestUtils.server.checkAndClearRequests([
+ {
+ params: {
+ [SEARCH_PARAMS.QUERY]: query2,
+ [SEARCH_PARAMS.SEQUENCE_NUMBER]: 2 * i + 1,
+ },
+ },
+ ]);
+ }
+
+ gClient.resetSession();
+});
+
+// When the server returns a response with an HTTP error, the sequence number
+// should be incremented.
+add_task(async function httpError() {
+ for (let i = 0; i < 3; i++) {
+ // Do a fetch that fails with an HTTP error.
+ MerinoTestUtils.server.response.status = 500;
+ let query1 = "search" + i;
+ await gClient.fetch({ query: query1 });
+
+ Assert.equal(
+ gClient.lastFetchStatus,
+ "http_error",
+ "The last request failed with a network error"
+ );
+
+ // Do another fetch that successfully finishes.
+ MerinoTestUtils.server.response.status = 200;
+ let query2 = query1 + "again";
+ await gClient.fetch({ query: query2 });
+
+ Assert.equal(
+ gClient.lastFetchStatus,
+ "success",
+ "The last request completed successfully"
+ );
+
+ // Both requests should have been received and the sequence number should
+ // have been incremented for each.
+ MerinoTestUtils.server.checkAndClearRequests([
+ {
+ params: {
+ [SEARCH_PARAMS.QUERY]: query1,
+ [SEARCH_PARAMS.SEQUENCE_NUMBER]: 2 * i,
+ },
+ },
+ {
+ params: {
+ [SEARCH_PARAMS.QUERY]: query2,
+ [SEARCH_PARAMS.SEQUENCE_NUMBER]: 2 * i + 1,
+ },
+ },
+ ]);
+
+ MerinoTestUtils.server.reset();
+ }
+
+ gClient.resetSession();
+});
+
+// When the client times out waiting for a response but later receives it and no
+// other fetch happens in the meantime, the sequence number should be
+// incremented.
+add_task(async function clientTimeout_wait() {
+ for (let i = 0; i < 3; i++) {
+ // Do a fetch that causes the client to time out.
+ MerinoTestUtils.server.response.delay =
+ 2 * UrlbarPrefs.get("merino.timeoutMs");
+ let responsePromise = gClient.waitForNextResponse();
+ let query1 = "search" + i;
+ await gClient.fetch({ query: query1 });
+
+ Assert.equal(
+ gClient.lastFetchStatus,
+ "timeout",
+ "The last request failed with a client timeout"
+ );
+
+ // Wait for the client to receive the response.
+ await responsePromise;
+
+ // Do another fetch that successfully finishes.
+ delete MerinoTestUtils.server.response.delay;
+ let query2 = query1 + "again";
+ await gClient.fetch({ query: query2 });
+
+ Assert.equal(
+ gClient.lastFetchStatus,
+ "success",
+ "The last request completed successfully"
+ );
+
+ MerinoTestUtils.server.checkAndClearRequests([
+ {
+ params: {
+ [SEARCH_PARAMS.QUERY]: query1,
+ [SEARCH_PARAMS.SEQUENCE_NUMBER]: 2 * i,
+ },
+ },
+ {
+ params: {
+ [SEARCH_PARAMS.QUERY]: query2,
+ [SEARCH_PARAMS.SEQUENCE_NUMBER]: 2 * i + 1,
+ },
+ },
+ ]);
+ }
+
+ gClient.resetSession();
+});
+
+// When the client times out waiting for a response and a second fetch starts
+// before the response is received, the first fetch should be canceled but the
+// sequence number should still be incremented.
+add_task(async function clientTimeout_canceled() {
+ for (let i = 0; i < 3; i++) {
+ // Do a fetch that causes the client to time out.
+ MerinoTestUtils.server.response.delay =
+ 2 * UrlbarPrefs.get("merino.timeoutMs");
+ let query1 = "search" + i;
+ await gClient.fetch({ query: query1 });
+
+ Assert.equal(
+ gClient.lastFetchStatus,
+ "timeout",
+ "The last request failed with a client timeout"
+ );
+
+ // Do another fetch that successfully finishes.
+ delete MerinoTestUtils.server.response.delay;
+ let query2 = query1 + "again";
+ await gClient.fetch({ query: query2 });
+
+ Assert.equal(
+ gClient.lastFetchStatus,
+ "success",
+ "The last request completed successfully"
+ );
+
+ MerinoTestUtils.server.checkAndClearRequests([
+ {
+ params: {
+ [SEARCH_PARAMS.QUERY]: query1,
+ [SEARCH_PARAMS.SEQUENCE_NUMBER]: 2 * i,
+ },
+ },
+ {
+ params: {
+ [SEARCH_PARAMS.QUERY]: query2,
+ [SEARCH_PARAMS.SEQUENCE_NUMBER]: 2 * i + 1,
+ },
+ },
+ ]);
+ }
+
+ gClient.resetSession();
+});
+
+// When the session times out, the next fetch should use a new session ID and
+// the sequence number should be reset.
+add_task(async function sessionTimeout() {
+ // Set the session timeout to something reasonable to test.
+ let originalTimeoutMs = gClient.sessionTimeoutMs;
+ gClient.sessionTimeoutMs = 500;
+
+ // Do a fetch.
+ let query1 = "search";
+ await gClient.fetch({ query: query1 });
+
+ // Wait for the session to time out.
+ await gClient.waitForNextSessionReset();
+
+ Assert.strictEqual(
+ gClient.sessionID,
+ null,
+ "sessionID is null after session timeout"
+ );
+ Assert.strictEqual(
+ gClient.sequenceNumber,
+ 0,
+ "sequenceNumber is zero after session timeout"
+ );
+ Assert.strictEqual(
+ gClient._test_sessionTimer,
+ null,
+ "sessionTimer is null after session timeout"
+ );
+
+ // Do another fetch.
+ let query2 = query1 + "again";
+ await gClient.fetch({ query: query2 });
+
+ // The second request's sequence number should be zero due to the session
+ // timeout.
+ MerinoTestUtils.server.checkAndClearRequests([
+ {
+ params: {
+ [SEARCH_PARAMS.QUERY]: query1,
+ [SEARCH_PARAMS.SEQUENCE_NUMBER]: 0,
+ },
+ },
+ {
+ params: {
+ [SEARCH_PARAMS.QUERY]: query2,
+ [SEARCH_PARAMS.SEQUENCE_NUMBER]: 0,
+ },
+ },
+ ]);
+
+ Assert.ok(
+ gClient.sessionID,
+ "sessionID is non-null after first request in a new session"
+ );
+ Assert.equal(
+ gClient.sequenceNumber,
+ 1,
+ "sequenceNumber is one after first request in a new session"
+ );
+ Assert.ok(
+ gClient._test_sessionTimer,
+ "sessionTimer is non-null after first request in a new session"
+ );
+
+ gClient.sessionTimeoutMs = originalTimeoutMs;
+ gClient.resetSession();
+});