Source code
Revision control
Copy as Markdown
Other Tools
Test Info: Warnings
- This test gets skipped with pattern: os == 'android' OR appname == 'thunderbird' && !nightly_build
- Manifest: services/fxaccounts/tests/xpcshell/xpcshell.toml
/* Any copyright is dedicated to the Public Domain.
"use strict";
const { FxAccounts } = ChromeUtils.importESModule(
"resource://gre/modules/FxAccounts.sys.mjs"
);
const { FxAccountsClient } = ChromeUtils.importESModule(
"resource://gre/modules/FxAccountsClient.sys.mjs"
);
// We grab some additional stuff via backstage passes.
var { AccountState } = ChromeUtils.importESModule(
"resource://gre/modules/FxAccounts.sys.mjs"
);
function promiseNotification(topic) {
return new Promise(resolve => {
let observe = () => {
Services.obs.removeObserver(observe, topic);
resolve();
};
Services.obs.addObserver(observe, topic);
});
}
// A storage manager that doesn't actually write anywhere.
function MockStorageManager() {}
MockStorageManager.prototype = {
promiseInitialized: Promise.resolve(),
initialize(accountData) {
this.accountData = accountData;
},
finalize() {
return Promise.resolve();
},
getAccountData() {
return Promise.resolve(this.accountData);
},
updateAccountData(updatedFields) {
for (let [name, value] of Object.entries(updatedFields)) {
if (value == null) {
delete this.accountData[name];
} else {
this.accountData[name] = value;
}
}
return Promise.resolve();
},
deleteAccountData() {
this.accountData = null;
return Promise.resolve();
},
};
// Just enough mocks so we can avoid hawk etc.
function MockFxAccountsClient() {
this._email = "nobody@example.com";
this._verified = false;
this.accountStatus = function (uid) {
return Promise.resolve(!!uid && !this._deletedOnServer);
};
this.signOut = function () {
return Promise.resolve();
};
this.registerDevice = function () {
return Promise.resolve();
};
this.updateDevice = function () {
return Promise.resolve();
};
this.signOutAndDestroyDevice = function () {
return Promise.resolve();
};
this.getDeviceList = function () {
return Promise.resolve();
};
FxAccountsClient.apply(this);
}
MockFxAccountsClient.prototype = {};
Object.setPrototypeOf(
MockFxAccountsClient.prototype,
FxAccountsClient.prototype
);
function MockFxAccounts() {
return new FxAccounts({
fxAccountsClient: new MockFxAccountsClient(),
newAccountState(credentials) {
// we use a real accountState but mocked storage.
let storage = new MockStorageManager();
storage.initialize(credentials);
return new AccountState(storage);
},
_getDeviceName() {
return "mock device name";
},
fxaPushService: {
registerPushEndpoint() {
return new Promise(resolve => {
resolve({
});
});
},
},
});
}
async function createMockFxA() {
let fxa = new MockFxAccounts();
let credentials = {
email: "foo@example.com",
uid: "1234@lcip.org",
sessionToken: "dead",
scopedKeys: {
[SCOPE_OLD_SYNC]: {
kid: "key id for sync key",
k: "key material for sync key",
kty: "oct",
},
},
verified: true,
};
await fxa._internal.setSignedInUser(credentials);
return fxa;
}
// The tests.
add_task(async function testCacheStorage() {
let fxa = await createMockFxA();
// Hook what the impl calls to save to disk.
let cas = fxa._internal.currentAccountState;
let origPersistCached = cas._persistCachedTokens.bind(cas);
cas._persistCachedTokens = function () {
return origPersistCached().then(() => {
Services.obs.notifyObservers(null, "testhelper-fxa-cache-persist-done");
});
};
let promiseWritten = promiseNotification("testhelper-fxa-cache-persist-done");
let tokenData = { token: "token1", somethingelse: "something else" };
let scopeArray = ["foo", "bar"];
cas.setCachedToken(scopeArray, tokenData);
deepEqual(cas.getCachedToken(scopeArray), tokenData);
deepEqual(cas.oauthTokens, { "bar|foo": tokenData });
// wait for background write to complete.
await promiseWritten;
// Check the token cache made it to our mocked storage.
deepEqual(cas.storageManager.accountData.oauthTokens, {
"bar|foo": tokenData,
});
// Drop the token from the cache and ensure it is removed from the json.
promiseWritten = promiseNotification("testhelper-fxa-cache-persist-done");
await cas.removeCachedToken("token1");
deepEqual(cas.oauthTokens, {});
await promiseWritten;
deepEqual(cas.storageManager.accountData.oauthTokens, {});
// sign out and the token storage should end up with null.
let storageManager = cas.storageManager; // .signOut() removes the attribute.
await fxa.signOut(/* localOnly = */ true);
deepEqual(storageManager.accountData, null);
});