tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

test_merinoClient_sessions.js (11291B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 // Test for MerinoClient sessions.
      6 
      7 "use strict";
      8 
      9 const { MerinoClient } = ChromeUtils.importESModule(
     10  "moz-src:///browser/components/urlbar/MerinoClient.sys.mjs"
     11 );
     12 
     13 const { SEARCH_PARAMS } = MerinoClient;
     14 
     15 let gClient;
     16 
     17 add_setup(async () => {
     18  gClient = new MerinoClient();
     19  await MerinoTestUtils.server.start();
     20 });
     21 
     22 // In a single session, all requests should use the same session ID and the
     23 // sequence number should be incremented.
     24 add_task(async function singleSession() {
     25  for (let i = 0; i < 3; i++) {
     26    let query = "search" + i;
     27    await gClient.fetch({ query });
     28 
     29    MerinoTestUtils.server.checkAndClearRequests([
     30      {
     31        params: {
     32          [SEARCH_PARAMS.QUERY]: query,
     33          [SEARCH_PARAMS.SEQUENCE_NUMBER]: i,
     34        },
     35      },
     36    ]);
     37  }
     38 
     39  gClient.resetSession();
     40 });
     41 
     42 // Different sessions should use different session IDs and the sequence number
     43 // should be reset.
     44 add_task(async function manySessions() {
     45  for (let i = 0; i < 3; i++) {
     46    let query = "search" + i;
     47    await gClient.fetch({ query });
     48 
     49    MerinoTestUtils.server.checkAndClearRequests([
     50      {
     51        params: {
     52          [SEARCH_PARAMS.QUERY]: query,
     53          [SEARCH_PARAMS.SEQUENCE_NUMBER]: 0,
     54        },
     55      },
     56    ]);
     57 
     58    gClient.resetSession();
     59  }
     60 });
     61 
     62 // Tests two consecutive fetches:
     63 //
     64 // 1. Start a fetch
     65 // 2. Wait for the mock Merino server to receive the request
     66 // 3. Start a second fetch before the client receives the response
     67 //
     68 // The first fetch will be canceled by the second but the sequence number in the
     69 // second fetch should still be incremented.
     70 add_task(async function twoFetches_wait() {
     71  for (let i = 0; i < 3; i++) {
     72    // Send the first response after a delay to make sure the client will not
     73    // receive it before we start the second fetch.
     74    MerinoTestUtils.server.response.delay = UrlbarPrefs.get("merino.timeoutMs");
     75 
     76    // Start the first fetch but don't wait for it to finish.
     77    let requestPromise = MerinoTestUtils.server.waitForNextRequest();
     78    let query1 = "search" + i;
     79    gClient.fetch({ query: query1 });
     80 
     81    // Wait until the first request is received before starting the second
     82    // fetch, which will cancel the first. The response doesn't need to be
     83    // delayed, so remove it to make the test run faster.
     84    await requestPromise;
     85    delete MerinoTestUtils.server.response.delay;
     86    let query2 = query1 + "again";
     87    await gClient.fetch({ query: query2 });
     88 
     89    // The sequence number should have been incremented for each fetch.
     90    MerinoTestUtils.server.checkAndClearRequests([
     91      {
     92        params: {
     93          [SEARCH_PARAMS.QUERY]: query1,
     94          [SEARCH_PARAMS.SEQUENCE_NUMBER]: 2 * i,
     95        },
     96      },
     97      {
     98        params: {
     99          [SEARCH_PARAMS.QUERY]: query2,
    100          [SEARCH_PARAMS.SEQUENCE_NUMBER]: 2 * i + 1,
    101        },
    102      },
    103    ]);
    104  }
    105 
    106  gClient.resetSession();
    107 });
    108 
    109 // Tests two consecutive fetches:
    110 //
    111 // 1. Start a fetch
    112 // 2. Immediately start a second fetch
    113 //
    114 // The first fetch will be canceled by the second but the sequence number in the
    115 // second fetch should still be incremented.
    116 add_task(async function twoFetches_immediate() {
    117  for (let i = 0; i < 3; i++) {
    118    // Send the first response after a delay to make sure the client will not
    119    // receive it before we start the second fetch.
    120    MerinoTestUtils.server.response.delay =
    121      100 * UrlbarPrefs.get("merino.timeoutMs");
    122 
    123    // Start the first fetch but don't wait for it to finish.
    124    let query1 = "search" + i;
    125    gClient.fetch({ query: query1 });
    126 
    127    // Immediately do a second fetch that cancels the first. The response
    128    // doesn't need to be delayed, so remove it to make the test run faster.
    129    delete MerinoTestUtils.server.response.delay;
    130    let query2 = query1 + "again";
    131    await gClient.fetch({ query: query2 });
    132 
    133    // The sequence number should have been incremented for each fetch, but the
    134    // first won't have reached the server since it was immediately canceled.
    135    MerinoTestUtils.server.checkAndClearRequests([
    136      {
    137        params: {
    138          [SEARCH_PARAMS.QUERY]: query2,
    139          [SEARCH_PARAMS.SEQUENCE_NUMBER]: 2 * i + 1,
    140        },
    141      },
    142    ]);
    143  }
    144 
    145  gClient.resetSession();
    146 });
    147 
    148 // When a network error occurs, the sequence number should still be incremented.
    149 add_task(async function networkError() {
    150  for (let i = 0; i < 3; i++) {
    151    // Do a fetch that fails with a network error.
    152    let query1 = "search" + i;
    153    await MerinoTestUtils.server.withNetworkError(async () => {
    154      await gClient.fetch({ query: query1 });
    155    });
    156 
    157    Assert.equal(
    158      gClient.lastFetchStatus,
    159      "network_error",
    160      "The request failed with a network error"
    161    );
    162 
    163    // Do another fetch that successfully finishes.
    164    let query2 = query1 + "again";
    165    await gClient.fetch({ query: query2 });
    166 
    167    Assert.equal(
    168      gClient.lastFetchStatus,
    169      "success",
    170      "The request completed successfully"
    171    );
    172 
    173    // Only the second request should have been received but the sequence number
    174    // should have been incremented for each.
    175    MerinoTestUtils.server.checkAndClearRequests([
    176      {
    177        params: {
    178          [SEARCH_PARAMS.QUERY]: query2,
    179          [SEARCH_PARAMS.SEQUENCE_NUMBER]: 2 * i + 1,
    180        },
    181      },
    182    ]);
    183  }
    184 
    185  gClient.resetSession();
    186 });
    187 
    188 // When the server returns a response with an HTTP error, the sequence number
    189 // should be incremented.
    190 add_task(async function httpError() {
    191  for (let i = 0; i < 3; i++) {
    192    // Do a fetch that fails with an HTTP error.
    193    MerinoTestUtils.server.response.status = 500;
    194    let query1 = "search" + i;
    195    await gClient.fetch({ query: query1 });
    196 
    197    Assert.equal(
    198      gClient.lastFetchStatus,
    199      "http_error",
    200      "The last request failed with a network error"
    201    );
    202 
    203    // Do another fetch that successfully finishes.
    204    MerinoTestUtils.server.response.status = 200;
    205    let query2 = query1 + "again";
    206    await gClient.fetch({ query: query2 });
    207 
    208    Assert.equal(
    209      gClient.lastFetchStatus,
    210      "success",
    211      "The last request completed successfully"
    212    );
    213 
    214    // Both requests should have been received and the sequence number should
    215    // have been incremented for each.
    216    MerinoTestUtils.server.checkAndClearRequests([
    217      {
    218        params: {
    219          [SEARCH_PARAMS.QUERY]: query1,
    220          [SEARCH_PARAMS.SEQUENCE_NUMBER]: 2 * i,
    221        },
    222      },
    223      {
    224        params: {
    225          [SEARCH_PARAMS.QUERY]: query2,
    226          [SEARCH_PARAMS.SEQUENCE_NUMBER]: 2 * i + 1,
    227        },
    228      },
    229    ]);
    230 
    231    MerinoTestUtils.server.reset();
    232  }
    233 
    234  gClient.resetSession();
    235 });
    236 
    237 // When the client times out waiting for a response but later receives it and no
    238 // other fetch happens in the meantime, the sequence number should be
    239 // incremented.
    240 add_task(async function clientTimeout_wait() {
    241  for (let i = 0; i < 3; i++) {
    242    // Do a fetch that causes the client to time out.
    243    MerinoTestUtils.server.response.delay =
    244      2 * UrlbarPrefs.get("merino.timeoutMs");
    245    let responsePromise = gClient.waitForNextResponse();
    246    let query1 = "search" + i;
    247    await gClient.fetch({ query: query1 });
    248 
    249    Assert.equal(
    250      gClient.lastFetchStatus,
    251      "timeout",
    252      "The last request failed with a client timeout"
    253    );
    254 
    255    // Wait for the client to receive the response.
    256    await responsePromise;
    257 
    258    // Do another fetch that successfully finishes.
    259    delete MerinoTestUtils.server.response.delay;
    260    let query2 = query1 + "again";
    261    await gClient.fetch({ query: query2 });
    262 
    263    Assert.equal(
    264      gClient.lastFetchStatus,
    265      "success",
    266      "The last request completed successfully"
    267    );
    268 
    269    MerinoTestUtils.server.checkAndClearRequests([
    270      {
    271        params: {
    272          [SEARCH_PARAMS.QUERY]: query1,
    273          [SEARCH_PARAMS.SEQUENCE_NUMBER]: 2 * i,
    274        },
    275      },
    276      {
    277        params: {
    278          [SEARCH_PARAMS.QUERY]: query2,
    279          [SEARCH_PARAMS.SEQUENCE_NUMBER]: 2 * i + 1,
    280        },
    281      },
    282    ]);
    283  }
    284 
    285  gClient.resetSession();
    286 });
    287 
    288 // When the client times out waiting for a response and a second fetch starts
    289 // before the response is received, the first fetch should be canceled but the
    290 // sequence number should still be incremented.
    291 add_task(async function clientTimeout_canceled() {
    292  for (let i = 0; i < 3; i++) {
    293    // Do a fetch that causes the client to time out.
    294    MerinoTestUtils.server.response.delay =
    295      2 * UrlbarPrefs.get("merino.timeoutMs");
    296    let query1 = "search" + i;
    297    await gClient.fetch({ query: query1 });
    298 
    299    Assert.equal(
    300      gClient.lastFetchStatus,
    301      "timeout",
    302      "The last request failed with a client timeout"
    303    );
    304 
    305    // Do another fetch that successfully finishes.
    306    delete MerinoTestUtils.server.response.delay;
    307    let query2 = query1 + "again";
    308    await gClient.fetch({ query: query2 });
    309 
    310    Assert.equal(
    311      gClient.lastFetchStatus,
    312      "success",
    313      "The last request completed successfully"
    314    );
    315 
    316    MerinoTestUtils.server.checkAndClearRequests([
    317      {
    318        params: {
    319          [SEARCH_PARAMS.QUERY]: query1,
    320          [SEARCH_PARAMS.SEQUENCE_NUMBER]: 2 * i,
    321        },
    322      },
    323      {
    324        params: {
    325          [SEARCH_PARAMS.QUERY]: query2,
    326          [SEARCH_PARAMS.SEQUENCE_NUMBER]: 2 * i + 1,
    327        },
    328      },
    329    ]);
    330  }
    331 
    332  gClient.resetSession();
    333 });
    334 
    335 // When the session times out, the next fetch should use a new session ID and
    336 // the sequence number should be reset.
    337 add_task(async function sessionTimeout() {
    338  // Set the session timeout to something reasonable to test.
    339  let originalTimeoutMs = gClient.sessionTimeoutMs;
    340  gClient.sessionTimeoutMs = 500;
    341 
    342  // Do a fetch.
    343  let query1 = "search";
    344  await gClient.fetch({ query: query1 });
    345 
    346  // Wait for the session to time out.
    347  await gClient.waitForNextSessionReset();
    348 
    349  Assert.strictEqual(
    350    gClient.sessionID,
    351    null,
    352    "sessionID is null after session timeout"
    353  );
    354  Assert.strictEqual(
    355    gClient.sequenceNumber,
    356    0,
    357    "sequenceNumber is zero after session timeout"
    358  );
    359  Assert.strictEqual(
    360    gClient._test_sessionTimer,
    361    null,
    362    "sessionTimer is null after session timeout"
    363  );
    364 
    365  // Do another fetch.
    366  let query2 = query1 + "again";
    367  await gClient.fetch({ query: query2 });
    368 
    369  // The second request's sequence number should be zero due to the session
    370  // timeout.
    371  MerinoTestUtils.server.checkAndClearRequests([
    372    {
    373      params: {
    374        [SEARCH_PARAMS.QUERY]: query1,
    375        [SEARCH_PARAMS.SEQUENCE_NUMBER]: 0,
    376      },
    377    },
    378    {
    379      params: {
    380        [SEARCH_PARAMS.QUERY]: query2,
    381        [SEARCH_PARAMS.SEQUENCE_NUMBER]: 0,
    382      },
    383    },
    384  ]);
    385 
    386  Assert.ok(
    387    gClient.sessionID,
    388    "sessionID is non-null after first request in a new session"
    389  );
    390  Assert.equal(
    391    gClient.sequenceNumber,
    392    1,
    393    "sequenceNumber is one after first request in a new session"
    394  );
    395  Assert.ok(
    396    gClient._test_sessionTimer,
    397    "sessionTimer is non-null after first request in a new session"
    398  );
    399 
    400  gClient.sessionTimeoutMs = originalTimeoutMs;
    401  gClient.resetSession();
    402 });