summaryrefslogtreecommitdiffstats
path: root/dom/push/test/xpcshell/test_broadcast_success.js
diff options
context:
space:
mode:
Diffstat (limited to 'dom/push/test/xpcshell/test_broadcast_success.js')
-rw-r--r--dom/push/test/xpcshell/test_broadcast_success.js428
1 files changed, 428 insertions, 0 deletions
diff --git a/dom/push/test/xpcshell/test_broadcast_success.js b/dom/push/test/xpcshell/test_broadcast_success.js
new file mode 100644
index 0000000000..16f586081b
--- /dev/null
+++ b/dom/push/test/xpcshell/test_broadcast_success.js
@@ -0,0 +1,428 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Create the profile directory early to ensure pushBroadcastService
+// is initialized with the correct path
+do_get_profile();
+const { BroadcastService } = ChromeUtils.importESModule(
+ "resource://gre/modules/PushBroadcastService.sys.mjs"
+);
+const { JSONFile } = ChromeUtils.importESModule(
+ "resource://gre/modules/JSONFile.sys.mjs"
+);
+
+const { FileTestUtils } = ChromeUtils.importESModule(
+ "resource://testing-common/FileTestUtils.sys.mjs"
+);
+const { broadcastHandler } = ChromeUtils.importESModule(
+ "resource://test/broadcast_handler.sys.mjs"
+);
+
+const broadcastService = pushBroadcastService;
+const assert = Assert;
+const userAgentID = "bd744428-f125-436a-b6d0-dd0c9845837f";
+const channelID = "0ef2ad4a-6c49-41ad-af6e-95d2425276bf";
+
+function run_test() {
+ setPrefs({
+ userAgentID,
+ alwaysConnect: true,
+ requestTimeout: 1000,
+ retryBaseInterval: 150,
+ });
+ run_next_test();
+}
+
+function getPushServiceMock() {
+ return {
+ subscribed: [],
+ subscribeBroadcast(broadcastId, version) {
+ this.subscribed.push([broadcastId, version]);
+ },
+ };
+}
+
+add_task(async function test_register_success() {
+ await broadcastService._resetListeners();
+ const db = PushServiceWebSocket.newPushDB();
+ broadcastHandler.reset();
+ const notifications = broadcastHandler.notifications;
+ let socket;
+ registerCleanupFunction(() => {
+ return db.drop().then(_ => db.close());
+ });
+
+ await broadcastService.addListener("broadcast-test", "2018-02-01", {
+ moduleURI: "resource://test/broadcast_handler.sys.mjs",
+ symbolName: "broadcastHandler",
+ });
+
+ PushServiceWebSocket._generateID = () => channelID;
+
+ var broadcastSubscriptions = [];
+
+ let handshakeDone;
+ let handshakePromise = new Promise(resolve => (handshakeDone = resolve));
+ await PushService.init({
+ serverURI: "wss://push.example.org/",
+ db,
+ makeWebSocket(uri) {
+ return new MockWebSocket(uri, {
+ onHello(data) {
+ socket = this;
+ deepEqual(
+ data.broadcasts,
+ { "broadcast-test": "2018-02-01" },
+ "Handshake: doesn't consult listeners"
+ );
+ equal(data.messageType, "hello", "Handshake: wrong message type");
+ ok(
+ !data.uaid,
+ "Should not send UAID in handshake without local subscriptions"
+ );
+ this.serverSendMsg(
+ JSON.stringify({
+ messageType: "hello",
+ status: 200,
+ uaid: userAgentID,
+ })
+ );
+ handshakeDone();
+ },
+
+ onBroadcastSubscribe(data) {
+ broadcastSubscriptions.push(data);
+ },
+ });
+ },
+ });
+ await handshakePromise;
+
+ socket.serverSendMsg(
+ JSON.stringify({
+ messageType: "broadcast",
+ broadcasts: {
+ "broadcast-test": "2018-03-02",
+ },
+ })
+ );
+
+ await broadcastHandler.wasNotified;
+
+ deepEqual(
+ notifications,
+ [
+ [
+ "2018-03-02",
+ "broadcast-test",
+ { phase: broadcastService.PHASES.BROADCAST },
+ ],
+ ],
+ "Broadcast notification didn't get delivered"
+ );
+
+ deepEqual(
+ await broadcastService.getListeners(),
+ {
+ "broadcast-test": "2018-03-02",
+ },
+ "Broadcast version wasn't updated"
+ );
+
+ await broadcastService.addListener("example-listener", "2018-03-01", {
+ moduleURI: "resource://gre/modules/not-real-example.jsm",
+ symbolName: "doesntExist",
+ });
+
+ deepEqual(broadcastSubscriptions, [
+ {
+ messageType: "broadcast_subscribe",
+ broadcasts: { "example-listener": "2018-03-01" },
+ },
+ ]);
+});
+
+add_task(async function test_handle_hello_broadcasts() {
+ PushService.uninit();
+ await broadcastService._resetListeners();
+ let db = PushServiceWebSocket.newPushDB();
+ broadcastHandler.reset();
+ let notifications = broadcastHandler.notifications;
+ registerCleanupFunction(() => {
+ return db.drop().then(_ => db.close());
+ });
+
+ await broadcastService.addListener("broadcast-test", "2018-02-01", {
+ moduleURI: "resource://test/broadcast_handler.sys.mjs",
+ symbolName: "broadcastHandler",
+ });
+
+ PushServiceWebSocket._generateID = () => channelID;
+
+ await PushService.init({
+ serverURI: "wss://push.example.org/",
+ db,
+ makeWebSocket(uri) {
+ return new MockWebSocket(uri, {
+ onHello(data) {
+ deepEqual(
+ data.broadcasts,
+ { "broadcast-test": "2018-02-01" },
+ "Handshake: doesn't consult listeners"
+ );
+ equal(data.messageType, "hello", "Handshake: wrong message type");
+ ok(
+ !data.uaid,
+ "Should not send UAID in handshake without local subscriptions"
+ );
+ this.serverSendMsg(
+ JSON.stringify({
+ messageType: "hello",
+ status: 200,
+ uaid: userAgentID,
+ broadcasts: {
+ "broadcast-test": "2018-02-02",
+ },
+ })
+ );
+ },
+
+ onBroadcastSubscribe(data) {},
+ });
+ },
+ });
+
+ await broadcastHandler.wasNotified;
+
+ deepEqual(
+ notifications,
+ [
+ [
+ "2018-02-02",
+ "broadcast-test",
+ { phase: broadcastService.PHASES.HELLO },
+ ],
+ ],
+ "Broadcast notification on hello was delivered"
+ );
+
+ deepEqual(
+ await broadcastService.getListeners(),
+ {
+ "broadcast-test": "2018-02-02",
+ },
+ "Broadcast version wasn't updated"
+ );
+});
+
+add_task(async function test_broadcast_context() {
+ await broadcastService._resetListeners();
+ const db = PushServiceWebSocket.newPushDB();
+ broadcastHandler.reset();
+ registerCleanupFunction(() => {
+ return db.drop().then(() => db.close());
+ });
+
+ const serviceId = "broadcast-test";
+ const version = "2018-02-01";
+ await broadcastService.addListener(serviceId, version, {
+ moduleURI: "resource://test/broadcast_handler.sys.mjs",
+ symbolName: "broadcastHandler",
+ });
+
+ // PushServiceWebSocket._generateID = () => channelID;
+
+ await PushService.init({
+ serverURI: "wss://push.example.org/",
+ db,
+ makeWebSocket(uri) {
+ return new MockWebSocket(uri, {
+ onHello(data) {},
+ });
+ },
+ });
+
+ // Simulate registration.
+ PushServiceWebSocket.sendSubscribeBroadcast(serviceId, version);
+
+ // Simulate broadcast reply received by PushWebSocketListener.
+ const message = JSON.stringify({
+ messageType: "broadcast",
+ broadcasts: {
+ [serviceId]: version,
+ },
+ });
+ PushServiceWebSocket._wsOnMessageAvailable({}, message);
+ await broadcastHandler.wasNotified;
+
+ deepEqual(
+ broadcastHandler.notifications,
+ [[version, serviceId, { phase: broadcastService.PHASES.REGISTER }]],
+ "Broadcast passes REGISTER context"
+ );
+
+ // Simulate broadcast reply, without previous registration.
+ broadcastHandler.reset();
+ PushServiceWebSocket._wsOnMessageAvailable({}, message);
+ await broadcastHandler.wasNotified;
+
+ deepEqual(
+ broadcastHandler.notifications,
+ [[version, serviceId, { phase: broadcastService.PHASES.BROADCAST }]],
+ "Broadcast passes BROADCAST context"
+ );
+});
+
+add_task(async function test_broadcast_unit() {
+ const fakeListenersData = {
+ abc: {
+ version: "2018-03-04",
+ sourceInfo: {
+ moduleURI: "resource://gre/modules/abc.jsm",
+ symbolName: "getAbc",
+ },
+ },
+ def: {
+ version: "2018-04-05",
+ sourceInfo: {
+ moduleURI: "resource://gre/modules/def.jsm",
+ symbolName: "getDef",
+ },
+ },
+ };
+ const path = FileTestUtils.getTempFile("broadcast-listeners.json").path;
+
+ const jsonFile = new JSONFile({ path });
+ jsonFile.data = {
+ listeners: fakeListenersData,
+ };
+ await jsonFile._save();
+
+ const pushServiceMock = getPushServiceMock();
+
+ const mockBroadcastService = new BroadcastService(pushServiceMock, path);
+ const listeners = await mockBroadcastService.getListeners();
+ deepEqual(listeners, {
+ abc: "2018-03-04",
+ def: "2018-04-05",
+ });
+
+ await mockBroadcastService.addListener("ghi", "2018-05-06", {
+ moduleURI: "resource://gre/modules/ghi.jsm",
+ symbolName: "getGhi",
+ });
+
+ deepEqual(pushServiceMock.subscribed, [["ghi", "2018-05-06"]]);
+
+ await mockBroadcastService._saveImmediately();
+
+ const newJSONFile = new JSONFile({ path });
+ await newJSONFile.load();
+
+ deepEqual(newJSONFile.data, {
+ listeners: {
+ ...fakeListenersData,
+ ghi: {
+ version: "2018-05-06",
+ sourceInfo: {
+ moduleURI: "resource://gre/modules/ghi.jsm",
+ symbolName: "getGhi",
+ },
+ },
+ },
+ version: 1,
+ });
+
+ deepEqual(await mockBroadcastService.getListeners(), {
+ abc: "2018-03-04",
+ def: "2018-04-05",
+ ghi: "2018-05-06",
+ });
+});
+
+add_task(async function test_broadcast_initialize_sane() {
+ const path = FileTestUtils.getTempFile("broadcast-listeners.json").path;
+ const mockBroadcastService = new BroadcastService(getPushServiceMock(), path);
+ deepEqual(
+ await mockBroadcastService.getListeners(),
+ {},
+ "listeners should start out sane"
+ );
+ await mockBroadcastService._saveImmediately();
+ let onDiskJSONFile = new JSONFile({ path });
+ await onDiskJSONFile.load();
+ deepEqual(
+ onDiskJSONFile.data,
+ { listeners: {}, version: 1 },
+ "written JSON file has listeners and version fields"
+ );
+
+ await mockBroadcastService.addListener("ghi", "2018-05-06", {
+ moduleURI: "resource://gre/modules/ghi.jsm",
+ symbolName: "getGhi",
+ });
+
+ await mockBroadcastService._saveImmediately();
+
+ onDiskJSONFile = new JSONFile({ path });
+ await onDiskJSONFile.load();
+
+ deepEqual(
+ onDiskJSONFile.data,
+ {
+ listeners: {
+ ghi: {
+ version: "2018-05-06",
+ sourceInfo: {
+ moduleURI: "resource://gre/modules/ghi.jsm",
+ symbolName: "getGhi",
+ },
+ },
+ },
+ version: 1,
+ },
+ "adding listeners to initial state is written OK"
+ );
+});
+
+add_task(async function test_broadcast_reject_invalid_sourceinfo() {
+ const path = FileTestUtils.getTempFile("broadcast-listeners.json").path;
+ const mockBroadcastService = new BroadcastService(getPushServiceMock(), path);
+
+ await assert.rejects(
+ mockBroadcastService.addListener("ghi", "2018-05-06", {
+ moduleName: "resource://gre/modules/ghi.jsm",
+ symbolName: "getGhi",
+ }),
+ /moduleURI must be a string/,
+ "rejects sourceInfo that doesn't have moduleURI"
+ );
+});
+
+add_task(async function test_broadcast_reject_version_not_string() {
+ await assert.rejects(
+ broadcastService.addListener(
+ "ghi",
+ {},
+ {
+ moduleURI: "resource://gre/modules/ghi.jsm",
+ symbolName: "getGhi",
+ }
+ ),
+ /version should be a string/,
+ "rejects version that isn't a string"
+ );
+});
+
+add_task(async function test_broadcast_reject_version_empty_string() {
+ await assert.rejects(
+ broadcastService.addListener("ghi", "", {
+ moduleURI: "resource://gre/modules/ghi.jsm",
+ symbolName: "getGhi",
+ }),
+ /version should not be an empty string/,
+ "rejects version that is an empty string"
+ );
+});