diff options
Diffstat (limited to 'services/fxaccounts/tests/xpcshell/test_push_service.js')
-rw-r--r-- | services/fxaccounts/tests/xpcshell/test_push_service.js | 520 |
1 files changed, 520 insertions, 0 deletions
diff --git a/services/fxaccounts/tests/xpcshell/test_push_service.js b/services/fxaccounts/tests/xpcshell/test_push_service.js new file mode 100644 index 0000000000..cc01d256fe --- /dev/null +++ b/services/fxaccounts/tests/xpcshell/test_push_service.js @@ -0,0 +1,520 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Tests for the FxA push service. + +/* eslint-disable mozilla/use-chromeutils-generateqi */ + +const { + FXA_PUSH_SCOPE_ACCOUNT_UPDATE, + ONLOGOUT_NOTIFICATION, + ON_ACCOUNT_DESTROYED_NOTIFICATION, + ON_DEVICE_CONNECTED_NOTIFICATION, + ON_DEVICE_DISCONNECTED_NOTIFICATION, + ON_PASSWORD_CHANGED_NOTIFICATION, + ON_PASSWORD_RESET_NOTIFICATION, + ON_PROFILE_CHANGE_NOTIFICATION, + ON_PROFILE_UPDATED_NOTIFICATION, + ON_VERIFY_LOGIN_NOTIFICATION, + log, +} = ChromeUtils.import("resource://gre/modules/FxAccountsCommon.js"); + +const { FxAccountsPushService } = ChromeUtils.importESModule( + "resource://gre/modules/FxAccountsPush.sys.mjs" +); + +XPCOMUtils.defineLazyServiceGetter( + this, + "PushService", + "@mozilla.org/push/Service;1", + "nsIPushService" +); + +initTestLogging("Trace"); +log.level = Log.Level.Trace; + +const MOCK_ENDPOINT = "http://mochi.test:8888"; + +// tests do not allow external connections, mock the PushService +let mockPushService = { + pushTopic: PushService.pushTopic, + subscriptionChangeTopic: PushService.subscriptionChangeTopic, + subscribe(scope, principal, cb) { + cb(Cr.NS_OK, { + endpoint: MOCK_ENDPOINT, + }); + }, + unsubscribe(scope, principal, cb) { + cb(Cr.NS_OK, true); + }, +}; + +let mockFxAccounts = { + checkVerificationStatus() {}, + updateDeviceRegistration() {}, +}; + +let mockLog = { + trace() {}, + debug() {}, + warn() {}, + error() {}, +}; + +add_task(async function initialize() { + let pushService = new FxAccountsPushService(); + equal(pushService.initialize(), false); +}); + +add_task(async function registerPushEndpointSuccess() { + let pushService = new FxAccountsPushService({ + pushService: mockPushService, + fxai: mockFxAccounts, + }); + + let subscription = await pushService.registerPushEndpoint(); + equal(subscription.endpoint, MOCK_ENDPOINT); +}); + +add_task(async function registerPushEndpointFailure() { + let failPushService = Object.assign(mockPushService, { + subscribe(scope, principal, cb) { + cb(Cr.NS_ERROR_ABORT); + }, + }); + + let pushService = new FxAccountsPushService({ + pushService: failPushService, + fxai: mockFxAccounts, + }); + + let subscription = await pushService.registerPushEndpoint(); + equal(subscription, null); +}); + +add_task(async function unsubscribeSuccess() { + let pushService = new FxAccountsPushService({ + pushService: mockPushService, + fxai: mockFxAccounts, + }); + + let result = await pushService.unsubscribe(); + equal(result, true); +}); + +add_task(async function unsubscribeFailure() { + let failPushService = Object.assign(mockPushService, { + unsubscribe(scope, principal, cb) { + cb(Cr.NS_ERROR_ABORT); + }, + }); + + let pushService = new FxAccountsPushService({ + pushService: failPushService, + fxai: mockFxAccounts, + }); + + let result = await pushService.unsubscribe(); + equal(result, null); +}); + +add_test(function observeLogout() { + let customLog = Object.assign(mockLog, { + trace(msg) { + if (msg === "FxAccountsPushService unsubscribe") { + // logout means we unsubscribe + run_next_test(); + } + }, + }); + + let pushService = new FxAccountsPushService({ + pushService: mockPushService, + log: customLog, + }); + + pushService.observe(null, ONLOGOUT_NOTIFICATION); +}); + +add_test(function observePushTopicVerify() { + let emptyMsg = { + QueryInterface() { + return this; + }, + }; + let customAccounts = Object.assign(mockFxAccounts, { + checkVerificationStatus() { + // checking verification status on push messages without data + run_next_test(); + }, + }); + + let pushService = new FxAccountsPushService({ + pushService: mockPushService, + fxai: customAccounts, + }); + + pushService.observe( + emptyMsg, + mockPushService.pushTopic, + FXA_PUSH_SCOPE_ACCOUNT_UPDATE + ); +}); + +add_test(function observePushTopicDeviceConnected() { + let msg = { + data: { + json: () => ({ + command: ON_DEVICE_CONNECTED_NOTIFICATION, + data: { + deviceName: "My phone", + }, + }), + }, + QueryInterface() { + return this; + }, + }; + let obs = (subject, topic, data) => { + Services.obs.removeObserver(obs, topic); + run_next_test(); + }; + Services.obs.addObserver(obs, ON_DEVICE_CONNECTED_NOTIFICATION); + + let pushService = new FxAccountsPushService({ + pushService: mockPushService, + fxai: mockFxAccounts, + }); + + pushService.observe( + msg, + mockPushService.pushTopic, + FXA_PUSH_SCOPE_ACCOUNT_UPDATE + ); +}); + +add_task(async function observePushTopicDeviceDisconnected_current_device() { + const deviceId = "bogusid"; + let msg = { + data: { + json: () => ({ + command: ON_DEVICE_DISCONNECTED_NOTIFICATION, + data: { + id: deviceId, + }, + }), + }, + QueryInterface() { + return this; + }, + }; + + let signoutCalled = false; + let { FxAccounts } = ChromeUtils.importESModule( + "resource://gre/modules/FxAccounts.sys.mjs" + ); + const fxAccountsMock = new FxAccounts({ + newAccountState() { + return { + async getUserAccountData() { + return { device: { id: deviceId } }; + }, + }; + }, + signOut() { + signoutCalled = true; + }, + })._internal; + + const deviceDisconnectedNotificationObserved = new Promise(resolve => { + Services.obs.addObserver(function obs(subject, topic, data) { + Services.obs.removeObserver(obs, topic); + equal(data, JSON.stringify({ isLocalDevice: true })); + resolve(); + }, ON_DEVICE_DISCONNECTED_NOTIFICATION); + }); + + let pushService = new FxAccountsPushService({ + pushService: mockPushService, + fxai: fxAccountsMock, + }); + + pushService.observe( + msg, + mockPushService.pushTopic, + FXA_PUSH_SCOPE_ACCOUNT_UPDATE + ); + + await deviceDisconnectedNotificationObserved; + ok(signoutCalled); +}); + +add_task(async function observePushTopicDeviceDisconnected_another_device() { + const deviceId = "bogusid"; + let msg = { + data: { + json: () => ({ + command: ON_DEVICE_DISCONNECTED_NOTIFICATION, + data: { + id: deviceId, + }, + }), + }, + QueryInterface() { + return this; + }, + }; + + let signoutCalled = false; + let { FxAccounts } = ChromeUtils.importESModule( + "resource://gre/modules/FxAccounts.sys.mjs" + ); + const fxAccountsMock = new FxAccounts({ + newAccountState() { + return { + async getUserAccountData() { + return { device: { id: "thelocaldevice" } }; + }, + }; + }, + signOut() { + signoutCalled = true; + }, + })._internal; + + const deviceDisconnectedNotificationObserved = new Promise(resolve => { + Services.obs.addObserver(function obs(subject, topic, data) { + Services.obs.removeObserver(obs, topic); + equal(data, JSON.stringify({ isLocalDevice: false })); + resolve(); + }, ON_DEVICE_DISCONNECTED_NOTIFICATION); + }); + + let pushService = new FxAccountsPushService({ + pushService: mockPushService, + fxai: fxAccountsMock, + }); + + pushService.observe( + msg, + mockPushService.pushTopic, + FXA_PUSH_SCOPE_ACCOUNT_UPDATE + ); + + await deviceDisconnectedNotificationObserved; + ok(!signoutCalled); +}); + +add_test(function observePushTopicAccountDestroyed() { + const uid = "bogusuid"; + let msg = { + data: { + json: () => ({ + command: ON_ACCOUNT_DESTROYED_NOTIFICATION, + data: { + uid, + }, + }), + }, + QueryInterface() { + return this; + }, + }; + let customAccounts = Object.assign(mockFxAccounts, { + _handleAccountDestroyed() { + // checking verification status on push messages without data + run_next_test(); + }, + }); + + let pushService = new FxAccountsPushService({ + pushService: mockPushService, + fxai: customAccounts, + }); + + pushService.observe( + msg, + mockPushService.pushTopic, + FXA_PUSH_SCOPE_ACCOUNT_UPDATE + ); +}); + +add_test(function observePushTopicVerifyLogin() { + let url = "http://localhost/newLogin"; + let title = "bogustitle"; + let body = "bogusbody"; + let msg = { + data: { + json: () => ({ + command: ON_VERIFY_LOGIN_NOTIFICATION, + data: { + body, + title, + url, + }, + }), + }, + QueryInterface() { + return this; + }, + }; + let obs = (subject, topic, data) => { + Services.obs.removeObserver(obs, topic); + Assert.equal(data, JSON.stringify(msg.data.json().data)); + run_next_test(); + }; + Services.obs.addObserver(obs, ON_VERIFY_LOGIN_NOTIFICATION); + + let pushService = new FxAccountsPushService({ + pushService: mockPushService, + fxai: mockFxAccounts, + }); + + pushService.observe( + msg, + mockPushService.pushTopic, + FXA_PUSH_SCOPE_ACCOUNT_UPDATE + ); +}); + +add_test(function observePushTopicProfileUpdated() { + let msg = { + data: { + json: () => ({ + command: ON_PROFILE_UPDATED_NOTIFICATION, + }), + }, + QueryInterface() { + return this; + }, + }; + let obs = (subject, topic, data) => { + Services.obs.removeObserver(obs, topic); + run_next_test(); + }; + Services.obs.addObserver(obs, ON_PROFILE_CHANGE_NOTIFICATION); + + let pushService = new FxAccountsPushService({ + pushService: mockPushService, + fxai: mockFxAccounts, + }); + + pushService.observe( + msg, + mockPushService.pushTopic, + FXA_PUSH_SCOPE_ACCOUNT_UPDATE + ); +}); + +add_test(function observePushTopicPasswordChanged() { + let msg = { + data: { + json: () => ({ + command: ON_PASSWORD_CHANGED_NOTIFICATION, + }), + }, + QueryInterface() { + return this; + }, + }; + + let pushService = new FxAccountsPushService({ + pushService: mockPushService, + }); + + pushService._onPasswordChanged = function () { + run_next_test(); + }; + + pushService.observe( + msg, + mockPushService.pushTopic, + FXA_PUSH_SCOPE_ACCOUNT_UPDATE + ); +}); + +add_test(function observePushTopicPasswordReset() { + let msg = { + data: { + json: () => ({ + command: ON_PASSWORD_RESET_NOTIFICATION, + }), + }, + QueryInterface() { + return this; + }, + }; + + let pushService = new FxAccountsPushService({ + pushService: mockPushService, + }); + + pushService._onPasswordChanged = function () { + run_next_test(); + }; + + pushService.observe( + msg, + mockPushService.pushTopic, + FXA_PUSH_SCOPE_ACCOUNT_UPDATE + ); +}); + +add_task(async function commandReceived() { + let msg = { + data: { + json: () => ({ + command: "fxaccounts:command_received", + data: { + url: "https://api.accounts.firefox.com/auth/v1/account/device/commands?index=42&limit=1", + }, + }), + }, + QueryInterface() { + return this; + }, + }; + + let fxAccountsMock = {}; + const promiseConsumeRemoteMessagesCalled = new Promise(res => { + fxAccountsMock.commands = { + pollDeviceCommands() { + res(); + }, + }; + }); + + let pushService = new FxAccountsPushService({ + pushService: mockPushService, + fxai: fxAccountsMock, + }); + + pushService.observe( + msg, + mockPushService.pushTopic, + FXA_PUSH_SCOPE_ACCOUNT_UPDATE + ); + await promiseConsumeRemoteMessagesCalled; +}); + +add_test(function observeSubscriptionChangeTopic() { + let customAccounts = Object.assign(mockFxAccounts, { + updateDeviceRegistration() { + // subscription change means updating the device registration + run_next_test(); + }, + }); + + let pushService = new FxAccountsPushService({ + pushService: mockPushService, + fxai: customAccounts, + }); + + pushService.observe( + null, + mockPushService.subscriptionChangeTopic, + FXA_PUSH_SCOPE_ACCOUNT_UPDATE + ); +}); |