tor-browser

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

test_broadcast_success.js (11685B)


      1 /* Any copyright is dedicated to the Public Domain.
      2   http://creativecommons.org/publicdomain/zero/1.0/ */
      3 
      4 "use strict";
      5 
      6 // Create the profile directory early to ensure pushBroadcastService
      7 // is initialized with the correct path
      8 do_get_profile();
      9 const { BroadcastService } = ChromeUtils.importESModule(
     10  "resource://gre/modules/PushBroadcastService.sys.mjs"
     11 );
     12 const { JSONFile } = ChromeUtils.importESModule(
     13  "resource://gre/modules/JSONFile.sys.mjs"
     14 );
     15 
     16 const { FileTestUtils } = ChromeUtils.importESModule(
     17  "resource://testing-common/FileTestUtils.sys.mjs"
     18 );
     19 const { broadcastHandler } = ChromeUtils.importESModule(
     20  "resource://test/broadcast_handler.sys.mjs"
     21 );
     22 
     23 const broadcastService = pushBroadcastService;
     24 const assert = Assert;
     25 const userAgentID = "bd744428-f125-436a-b6d0-dd0c9845837f";
     26 const channelID = "0ef2ad4a-6c49-41ad-af6e-95d2425276bf";
     27 
     28 function run_test() {
     29  setPrefs({
     30    userAgentID,
     31    alwaysConnect: true,
     32    requestTimeout: 1000,
     33    retryBaseInterval: 150,
     34  });
     35  run_next_test();
     36 }
     37 
     38 function getPushServiceMock() {
     39  return {
     40    subscribed: [],
     41    subscribeBroadcast(broadcastId, version) {
     42      this.subscribed.push([broadcastId, version]);
     43    },
     44  };
     45 }
     46 
     47 add_task(async function test_register_success() {
     48  await broadcastService._resetListeners();
     49  const db = PushServiceWebSocket.newPushDB();
     50  broadcastHandler.reset();
     51  const notifications = broadcastHandler.notifications;
     52  let socket;
     53  registerCleanupFunction(() => {
     54    return db.drop().then(_ => db.close());
     55  });
     56 
     57  await broadcastService.addListener("broadcast-test", "2018-02-01", {
     58    moduleURI: "resource://test/broadcast_handler.sys.mjs",
     59    symbolName: "broadcastHandler",
     60  });
     61 
     62  PushServiceWebSocket._generateID = () => channelID;
     63 
     64  var broadcastSubscriptions = [];
     65 
     66  let handshakeDone;
     67  let handshakePromise = new Promise(resolve => (handshakeDone = resolve));
     68  await PushService.init({
     69    serverURI: "wss://push.example.org/",
     70    db,
     71    makeWebSocket(uri) {
     72      return new MockWebSocket(uri, {
     73        onHello(data) {
     74          socket = this;
     75          deepEqual(
     76            data.broadcasts,
     77            { "broadcast-test": "2018-02-01" },
     78            "Handshake: doesn't consult listeners"
     79          );
     80          equal(data.messageType, "hello", "Handshake: wrong message type");
     81          ok(
     82            !data.uaid,
     83            "Should not send UAID in handshake without local subscriptions"
     84          );
     85          this.serverSendMsg(
     86            JSON.stringify({
     87              messageType: "hello",
     88              status: 200,
     89              uaid: userAgentID,
     90            })
     91          );
     92          handshakeDone();
     93        },
     94 
     95        onBroadcastSubscribe(data) {
     96          broadcastSubscriptions.push(data);
     97        },
     98      });
     99    },
    100  });
    101  await handshakePromise;
    102 
    103  socket.serverSendMsg(
    104    JSON.stringify({
    105      messageType: "broadcast",
    106      broadcasts: {
    107        "broadcast-test": "2018-03-02",
    108      },
    109    })
    110  );
    111 
    112  await broadcastHandler.wasNotified;
    113 
    114  deepEqual(
    115    notifications,
    116    [
    117      [
    118        "2018-03-02",
    119        "broadcast-test",
    120        { phase: broadcastService.PHASES.BROADCAST },
    121      ],
    122    ],
    123    "Broadcast notification didn't get delivered"
    124  );
    125 
    126  deepEqual(
    127    await broadcastService.getListeners(),
    128    {
    129      "broadcast-test": "2018-03-02",
    130    },
    131    "Broadcast version wasn't updated"
    132  );
    133 
    134  await broadcastService.addListener("example-listener", "2018-03-01", {
    135    // NOTE: This file is non-existing.
    136    moduleURI: "resource://gre/modules/not-real-example.sys.mjs",
    137    symbolName: "doesntExist",
    138  });
    139 
    140  deepEqual(broadcastSubscriptions, [
    141    {
    142      messageType: "broadcast_subscribe",
    143      broadcasts: { "example-listener": "2018-03-01" },
    144    },
    145  ]);
    146 });
    147 
    148 add_task(async function test_handle_hello_broadcasts() {
    149  PushService.uninit();
    150  await broadcastService._resetListeners();
    151  let db = PushServiceWebSocket.newPushDB();
    152  broadcastHandler.reset();
    153  let notifications = broadcastHandler.notifications;
    154  registerCleanupFunction(() => {
    155    return db.drop().then(_ => db.close());
    156  });
    157 
    158  await broadcastService.addListener("broadcast-test", "2018-02-01", {
    159    moduleURI: "resource://test/broadcast_handler.sys.mjs",
    160    symbolName: "broadcastHandler",
    161  });
    162 
    163  PushServiceWebSocket._generateID = () => channelID;
    164 
    165  await PushService.init({
    166    serverURI: "wss://push.example.org/",
    167    db,
    168    makeWebSocket(uri) {
    169      return new MockWebSocket(uri, {
    170        onHello(data) {
    171          deepEqual(
    172            data.broadcasts,
    173            { "broadcast-test": "2018-02-01" },
    174            "Handshake: doesn't consult listeners"
    175          );
    176          equal(data.messageType, "hello", "Handshake: wrong message type");
    177          ok(
    178            !data.uaid,
    179            "Should not send UAID in handshake without local subscriptions"
    180          );
    181          this.serverSendMsg(
    182            JSON.stringify({
    183              messageType: "hello",
    184              status: 200,
    185              uaid: userAgentID,
    186              broadcasts: {
    187                "broadcast-test": "2018-02-02",
    188              },
    189            })
    190          );
    191        },
    192 
    193        onBroadcastSubscribe() {},
    194      });
    195    },
    196  });
    197 
    198  await broadcastHandler.wasNotified;
    199 
    200  deepEqual(
    201    notifications,
    202    [
    203      [
    204        "2018-02-02",
    205        "broadcast-test",
    206        { phase: broadcastService.PHASES.HELLO },
    207      ],
    208    ],
    209    "Broadcast notification on hello was delivered"
    210  );
    211 
    212  deepEqual(
    213    await broadcastService.getListeners(),
    214    {
    215      "broadcast-test": "2018-02-02",
    216    },
    217    "Broadcast version wasn't updated"
    218  );
    219 });
    220 
    221 add_task(async function test_broadcast_context() {
    222  await broadcastService._resetListeners();
    223  const db = PushServiceWebSocket.newPushDB();
    224  broadcastHandler.reset();
    225  registerCleanupFunction(() => {
    226    return db.drop().then(() => db.close());
    227  });
    228 
    229  const serviceId = "broadcast-test";
    230  const version = "2018-02-01";
    231  await broadcastService.addListener(serviceId, version, {
    232    moduleURI: "resource://test/broadcast_handler.sys.mjs",
    233    symbolName: "broadcastHandler",
    234  });
    235 
    236  // PushServiceWebSocket._generateID = () => channelID;
    237 
    238  await PushService.init({
    239    serverURI: "wss://push.example.org/",
    240    db,
    241    makeWebSocket(uri) {
    242      return new MockWebSocket(uri, {
    243        onHello() {},
    244      });
    245    },
    246  });
    247 
    248  // Simulate registration.
    249  PushServiceWebSocket.sendSubscribeBroadcast(serviceId, version);
    250 
    251  // Simulate broadcast reply received by PushWebSocketListener.
    252  const message = JSON.stringify({
    253    messageType: "broadcast",
    254    broadcasts: {
    255      [serviceId]: version,
    256    },
    257  });
    258  PushServiceWebSocket._wsOnMessageAvailable({}, message);
    259  await broadcastHandler.wasNotified;
    260 
    261  deepEqual(
    262    broadcastHandler.notifications,
    263    [[version, serviceId, { phase: broadcastService.PHASES.REGISTER }]],
    264    "Broadcast passes REGISTER context"
    265  );
    266 
    267  // Simulate broadcast reply, without previous registration.
    268  broadcastHandler.reset();
    269  PushServiceWebSocket._wsOnMessageAvailable({}, message);
    270  await broadcastHandler.wasNotified;
    271 
    272  deepEqual(
    273    broadcastHandler.notifications,
    274    [[version, serviceId, { phase: broadcastService.PHASES.BROADCAST }]],
    275    "Broadcast passes BROADCAST context"
    276  );
    277 });
    278 
    279 add_task(async function test_broadcast_unit() {
    280  const fakeListenersData = {
    281    abc: {
    282      version: "2018-03-04",
    283      sourceInfo: {
    284        // NOTE: This file is non-existing.
    285        moduleURI: "resource://gre/modules/abc.sys.mjs",
    286        symbolName: "getAbc",
    287      },
    288    },
    289    def: {
    290      version: "2018-04-05",
    291      sourceInfo: {
    292        // NOTE: This file is non-existing.
    293        moduleURI: "resource://gre/modules/def.sys.mjs",
    294        symbolName: "getDef",
    295      },
    296    },
    297  };
    298  const path = FileTestUtils.getTempFile("broadcast-listeners.json").path;
    299 
    300  const jsonFile = new JSONFile({ path });
    301  jsonFile.data = {
    302    listeners: fakeListenersData,
    303  };
    304  await jsonFile._save();
    305 
    306  const pushServiceMock = getPushServiceMock();
    307 
    308  const mockBroadcastService = new BroadcastService(pushServiceMock, path);
    309  const listeners = await mockBroadcastService.getListeners();
    310  deepEqual(listeners, {
    311    abc: "2018-03-04",
    312    def: "2018-04-05",
    313  });
    314 
    315  await mockBroadcastService.addListener("ghi", "2018-05-06", {
    316    // NOTE: This file is non-existing.
    317    moduleURI: "resource://gre/modules/ghi.sys.mjs",
    318    symbolName: "getGhi",
    319  });
    320 
    321  deepEqual(pushServiceMock.subscribed, [["ghi", "2018-05-06"]]);
    322 
    323  await mockBroadcastService._saveImmediately();
    324 
    325  const newJSONFile = new JSONFile({ path });
    326  await newJSONFile.load();
    327 
    328  deepEqual(newJSONFile.data, {
    329    listeners: {
    330      ...fakeListenersData,
    331      ghi: {
    332        version: "2018-05-06",
    333        sourceInfo: {
    334          // NOTE: This file is non-existing.
    335          moduleURI: "resource://gre/modules/ghi.sys.mjs",
    336          symbolName: "getGhi",
    337        },
    338      },
    339    },
    340    version: 1,
    341  });
    342 
    343  deepEqual(await mockBroadcastService.getListeners(), {
    344    abc: "2018-03-04",
    345    def: "2018-04-05",
    346    ghi: "2018-05-06",
    347  });
    348 });
    349 
    350 add_task(async function test_broadcast_initialize_sane() {
    351  const path = FileTestUtils.getTempFile("broadcast-listeners.json").path;
    352  const mockBroadcastService = new BroadcastService(getPushServiceMock(), path);
    353  deepEqual(
    354    await mockBroadcastService.getListeners(),
    355    {},
    356    "listeners should start out sane"
    357  );
    358  await mockBroadcastService._saveImmediately();
    359  let onDiskJSONFile = new JSONFile({ path });
    360  await onDiskJSONFile.load();
    361  deepEqual(
    362    onDiskJSONFile.data,
    363    { listeners: {}, version: 1 },
    364    "written JSON file has listeners and version fields"
    365  );
    366 
    367  await mockBroadcastService.addListener("ghi", "2018-05-06", {
    368    // NOTE: This file is non-existing.
    369    moduleURI: "resource://gre/modules/ghi.sys.mjs",
    370    symbolName: "getGhi",
    371  });
    372 
    373  await mockBroadcastService._saveImmediately();
    374 
    375  onDiskJSONFile = new JSONFile({ path });
    376  await onDiskJSONFile.load();
    377 
    378  deepEqual(
    379    onDiskJSONFile.data,
    380    {
    381      listeners: {
    382        ghi: {
    383          version: "2018-05-06",
    384          sourceInfo: {
    385            // NOTE: This file is non-existing.
    386            moduleURI: "resource://gre/modules/ghi.sys.mjs",
    387            symbolName: "getGhi",
    388          },
    389        },
    390      },
    391      version: 1,
    392    },
    393    "adding listeners to initial state is written OK"
    394  );
    395 });
    396 
    397 add_task(async function test_broadcast_reject_invalid_sourceinfo() {
    398  const path = FileTestUtils.getTempFile("broadcast-listeners.json").path;
    399  const mockBroadcastService = new BroadcastService(getPushServiceMock(), path);
    400 
    401  await assert.rejects(
    402    mockBroadcastService.addListener("ghi", "2018-05-06", {
    403      // NOTE: This file is non-existing.
    404      moduleName: "resource://gre/modules/ghi.sys.mjs",
    405      symbolName: "getGhi",
    406    }),
    407    /moduleURI must be a string/,
    408    "rejects sourceInfo that doesn't have moduleURI"
    409  );
    410 });
    411 
    412 add_task(async function test_broadcast_reject_version_not_string() {
    413  await assert.rejects(
    414    broadcastService.addListener(
    415      "ghi",
    416      {},
    417      {
    418        // NOTE: This file is non-existing.
    419        moduleURI: "resource://gre/modules/ghi.sys.mjs",
    420        symbolName: "getGhi",
    421      }
    422    ),
    423    /version should be a string/,
    424    "rejects version that isn't a string"
    425  );
    426 });
    427 
    428 add_task(async function test_broadcast_reject_version_empty_string() {
    429  await assert.rejects(
    430    broadcastService.addListener("ghi", "", {
    431      // NOTE: This file is non-existing.
    432      moduleURI: "resource://gre/modules/ghi.sys.mjs",
    433      symbolName: "getGhi",
    434    }),
    435    /version should not be an empty string/,
    436    "rejects version that is an empty string"
    437  );
    438 });