// We grab some additional stuff via the system global. var { AccountState } = ChromeUtils.importESModule( "resource://gre/modules/FxAccounts.sys.mjs"
);
var log = Log.repository.getLogger("Services.FxAccounts.test");
log.level = Log.Level.Debug;
// See verbose logging from FxAccounts.sys.mjs and jwcrypto.sys.mjs.
Services.prefs.setStringPref("identity.fxaccounts.loglevel", "Trace");
Log.repository.getLogger("FirefoxAccounts").level = Log.Level.Trace;
Services.prefs.setStringPref("services.crypto.jwcrypto.log.level", "Debug");
getAccountData(fields = null) {
let result; if (!this.accountData) {
result = null;
} elseif (fields == null) { // can't use cloneInto as the keys get upset...
result = {}; for (let field of Object.keys(this.accountData)) {
result[field] = this.accountData[field];
}
} else { if (!Array.isArray(fields)) {
fields = [fields];
}
result = {}; for (let field of fields) {
result[field] = this.accountData[field];
}
} return Promise.resolve(result);
},
updateAccountData(updatedFields) { if (!this.accountData) { return Promise.resolve();
} for (let [name, value] of Object.entries(updatedFields)) { if (value == null) { deletethis.accountData[name];
} else { this.accountData[name] = value;
}
} return Promise.resolve();
},
function MockFxAccountsClient() { this._email = "nobody@example.com"; this._verified = false; this._deletedOnServer = false; // for our accountStatus mock
// mock calls up to the auth server to determine whether the // user account has been verified this.recoveryEmailStatus = async function () { // simulate a call to /recovery_email/status return {
email: this._email,
verified: this._verified,
};
};
this.sessionStatus = async function () { // If the sessionStatus check says an account is OK, we typically will not // end up calling accountStatus - so this must return false if accountStatus // would. return !this._deletedOnServer;
};
this.resendVerificationEmail = function (sessionToken) { // Return the session token to show that we received it in the first place return Promise.resolve(sessionToken);
};
this.signOut = () => Promise.resolve();
FxAccountsClient.apply(this);
}
MockFxAccountsClient.prototype = {};
Object.setPrototypeOf(
MockFxAccountsClient.prototype,
FxAccountsClient.prototype
); /* *WeneedtomocktheFxAccountsmodule'sinterfacestoexternal *services,suchasstorageandtheFxAccountsclient.Wealso *mockthenow()method,sothatwecansimulatethepassingof *timeandverifythatsignaturesexpirecorrectly.
*/ function MockFxAccounts() {
let result = new FxAccounts({
VERIFICATION_POLL_TIMEOUT_INITIAL: 100, // 100ms
_getCertificateSigned_calls: [],
_d_signCertificate: Promise.withResolvers(),
_now_is: new Date(),
now() { returnthis._now_is;
},
newAccountState(newCredentials) { // we use a real accountState but mocked storage.
let storage = new MockStorageManager();
storage.initialize(newCredentials); returnnew AccountState(storage);
},
fxAccountsClient: new MockFxAccountsClient(),
observerPreloads: [],
device: {
_registerOrUpdateDevice() {},
_checkRemoteCommandsUpdateNeeded: async () => false,
},
profile: {
getProfile() { returnnull;
},
},
}); // and for convenience so we don't have to touch as many lines in this test // when we refactored FxAccounts.sys.mjs :)
result.setSignedInUser = function (creds) { return result._internal.setSignedInUser(creds);
}; return result;
}
/* *Sometestswanta"real"fxainstance-however,westillmockthestorage *tokeepthetestsfastonb2g.
*/
async function MakeFxAccounts({ internal = {}, credentials } = {}) { if (!internal.newAccountState) { // we use a real accountState but mocked storage.
internal.newAccountState = function (newCredentials) {
let storage = new MockStorageManager();
storage.initialize(newCredentials); returnnew AccountState(storage);
};
} if (!internal._signOutServer) {
internal._signOutServer = () => Promise.resolve();
} if (internal.device) { if (!internal.device._registerOrUpdateDevice) {
internal.device._registerOrUpdateDevice = () => Promise.resolve();
internal.device._checkRemoteCommandsUpdateNeeded = async () => false;
}
} else {
internal.device = {
_registerOrUpdateDevice() {},
_checkRemoteCommandsUpdateNeeded: async () => false,
};
} if (!internal.observerPreloads) {
internal.observerPreloads = [];
}
let result = new FxAccounts(internal);
if (credentials) {
await result._internal.setSignedInUser(credentials);
} return result;
}
add_task(async function test_get_signed_in_user_initially_unset() {
_("Check getSignedInUser initially and after signout reports no user");
let account = await MakeFxAccounts();
let credentials = {
email: "foo@example.com",
uid: "1234567890abcdef1234567890abcdef",
sessionToken: "dead",
verified: true,
...MOCK_ACCOUNT_KEYS,
};
let result = await account.getSignedInUser(); Assert.equal(result, null);
// getSignedInUser only returns a subset.
result = await account.getSignedInUser(); Assert.deepEqual(result.email, credentials.email); Assert.deepEqual(result.scopedKeys, undefined);
// for the sake of testing, use the low-level function to check it's all there
result = await account._internal.currentAccountState.getUserAccountData(); Assert.deepEqual(result.email, credentials.email); Assert.deepEqual(result.scopedKeys, credentials.scopedKeys);
// sign out
let localOnly = true;
await account.signOut(localOnly);
// user should be undefined after sign out
result = await account.getSignedInUser(); Assert.equal(result, null);
});
add_task(async function test_set_signed_in_user_signs_out_previous_account() {
_("Check setSignedInUser signs out the previous account.");
let signOutServerCalled = false;
let credentials = {
email: "foo@example.com",
uid: "1234567890abcdef1234567890abcdef",
sessionToken: "dead",
verified: true,
...MOCK_ACCOUNT_KEYS,
};
let account = await MakeFxAccounts({ credentials });
add_task(async function test_update_account_data() {
_("Check updateUserAccountData does the right thing.");
let credentials = {
email: "foo@example.com",
uid: "1234567890abcdef1234567890abcdef",
sessionToken: "dead",
verified: true,
...MOCK_ACCOUNT_KEYS,
};
let account = await MakeFxAccounts({ credentials });
let newCreds = {
email: credentials.email,
uid: credentials.uid,
sessionToken: "alive",
};
await account._internal.updateUserAccountData(newCreds); Assert.equal(
(await account._internal.getUserAccountData()).sessionToken, "alive", "new field value was saved"
);
// but we should fail attempting to change the uid.
newCreds = {
email: credentials.email,
uid: "11111111111111111111222222222222",
sessionToken: "alive",
};
await Assert.rejects(
account._internal.updateUserAccountData(newCreds),
/The specified credentials aren't for the current user/
);
// should fail without the uid.
newCreds = {
sessionToken: "alive",
};
await Assert.rejects(
account._internal.updateUserAccountData(newCreds),
/The specified credentials have no uid/
);
// and should fail with a field name that's not known by storage.
newCreds = {
email: credentials.email,
uid: "11111111111111111111222222222222",
foo: "bar",
};
await Assert.rejects(
account._internal.updateUserAccountData(newCreds),
/The specified credentials aren't for the current user/
);
});
// Sanity-check that our mocked client is working correctly
add_test(function test_client_mock() {
let fxa = new MockFxAccounts();
let client = fxa._internal.fxAccountsClient; Assert.equal(client._verified, false); Assert.equal(typeof client.signIn, "function");
// The recoveryEmailStatus function eventually fulfills its promise
client.recoveryEmailStatus().then(response => { Assert.equal(response.verified, false);
run_next_test();
});
});
add_test(function test_getKeyForScope() {
let fxa = new MockFxAccounts();
let user = getTestUser("eusebius");
// Once email has been verified, we will be able to get keys
user.verified = true;
fxa.setSignedInUser(user).then(() => {
fxa._internal.getUserAccountData().then(user2 => { // Before getKeyForScope, we have no keys Assert.equal(!!user2.scopedKeys, false); // And we still have a key-fetch token and unwrapBKey to use Assert.equal(!!user2.keyFetchToken, true); Assert.equal(!!user2.unwrapBKey, true);
fxa.keys.getKeyForScope(SCOPE_APP_SYNC).then(() => {
fxa._internal.getUserAccountData().then(user3 => { // Now we should have keys Assert.equal(!!user3.verified, true); Assert.notEqual(null, user3.scopedKeys); Assert.equal(user3.keyFetchToken, undefined); Assert.equal(user3.unwrapBKey, undefined);
run_next_test();
});
});
});
});
});
add_task(async function test_oauth_verification() {
let fxa = new MockFxAccounts();
let user = getTestUser("eusebius");
user.verified = false;
await fxa.setSignedInUser(user);
let fetched = await fxa.getSignedInUser(); Assert.ok(!fetched.verified);
// Simulate the follow-up login message that marks the account verified.
await fxa._internal.updateUserAccountData({
uid: user.uid,
verified: true,
});
// Tests for hasKeysForScope - checking if sync keys exist locally
add_task(async function test_hasKeysForScope_not_signed_in() { const fxa = await MakeFxAccounts(); // Should return false when no user is signed in Assert.ok(!(await fxa.keys.hasKeysForScope(SCOPE_APP_SYNC)));
});
add_task(async function test_hasKeysForScope_not_verified() { const credentials = {
email: "foo@example.com",
uid: "1234567890abcdef1234567890abcdef",
sessionToken: "dead",
verified: false, // Not verified
...MOCK_ACCOUNT_KEYS,
}; const fxa = await MakeFxAccounts({ credentials }); // Should return true even keys exist locally // (Keys are provided during OAuth before the account is marked verified) Assert.ok(await fxa.keys.hasKeysForScope(SCOPE_APP_SYNC));
});
add_task(async function test_hasKeysForScope_no_keys() { const credentials = {
email: "foo@example.com",
uid: "1234567890abcdef1234567890abcdef",
sessionToken: "dead",
verified: true, // NO scopedKeys - third party auth scenario
}; const fxa = await MakeFxAccounts({ credentials }); // Should return false when user has no sync keys (third-party auth) Assert.ok(!(await fxa.keys.hasKeysForScope(SCOPE_APP_SYNC)));
});
add_task(async function test_hasKeysForScope_with_keys() { const credentials = {
email: "foo@example.com",
uid: "1234567890abcdef1234567890abcdef",
sessionToken: "dead",
verified: true,
...MOCK_ACCOUNT_KEYS, // Has sync keys
}; const fxa = await MakeFxAccounts({ credentials }); // Should return true when user has sync keys Assert.ok(await fxa.keys.hasKeysForScope(SCOPE_APP_SYNC));
});
add_task(async function test_hasKeysForScope_wrong_scope() { const credentials = {
email: "foo@example.com",
uid: "1234567890abcdef1234567890abcdef",
sessionToken: "dead",
verified: true,
...MOCK_ACCOUNT_KEYS,
}; const fxa = await MakeFxAccounts({ credentials }); // Should return false for a scope we don't have keys for Assert.ok(
!(await fxa.keys.hasKeysForScope( "https://identity.mozilla.com/apps/unknown"
))
);
});
add_task(
async function test_getKeyForScope_scopedKeys_migration_removes_deprecated_high_level_keys() {
let fxa = new MockFxAccounts();
let user = getTestUser("eusebius");
user.verified = true;
// An account state with the deprecated kinto extension sync keys...
user.kExtSync = "f5ccd9cfdefd9b1ac4d02c56964f59239d8dfa1ca326e63696982765c1352cdc" + "5d78a5a9c633a6d25edfea0a6c221a3480332a49fd866f311c2e3508ddd07395";
user.kExtKbHash = "6192f1cc7dce95334455ba135fa1d8fca8f70e8f594ae318528de06f24ed0273";
user.scopedKeys = {
...MOCK_ACCOUNT_KEYS.scopedKeys,
};
await fxa.setSignedInUser(user); // getKeyForScope will run the migration
await fxa.keys.getKeyForScope(SCOPE_APP_SYNC);
let newUser = await fxa._internal.getUserAccountData(); // Then, the deprecated keys will be removed Assert.strictEqual(newUser.kExtSync, undefined); Assert.strictEqual(newUser.kExtKbHash, undefined);
}
);
add_task(
async function test_getKeyForScope_scopedKeys_migration_removes_deprecated_scoped_keys() {
let fxa = new MockFxAccounts();
let user = getTestUser("eusebius"); const DEPRECATED_SCOPE_WEBEXT_SYNC = "sync:addon_storage"; const EXTRA_SCOPE = "an unknown, but non-deprecated scope";
user.verified = true;
user.ecosystemUserId = "ecoUserId";
user.ecosystemAnonId = "ecoAnonId";
user.scopedKeys = {
...MOCK_ACCOUNT_KEYS.scopedKeys,
[DEPRECATED_SCOPE_ECOSYSTEM_TELEMETRY]:
MOCK_ACCOUNT_KEYS.scopedKeys[SCOPE_APP_SYNC],
[DEPRECATED_SCOPE_WEBEXT_SYNC]:
MOCK_ACCOUNT_KEYS.scopedKeys[SCOPE_APP_SYNC],
[EXTRA_SCOPE]: MOCK_ACCOUNT_KEYS.scopedKeys[SCOPE_APP_SYNC],
};
await fxa.setSignedInUser(user);
await fxa.keys.getKeyForScope(SCOPE_APP_SYNC);
let newUser = await fxa._internal.getUserAccountData(); // It should have removed the deprecated ecosystem_telemetry key, // and the old kinto extension sync key // but left the other keys intact. const expectedScopedKeys = {
...MOCK_ACCOUNT_KEYS.scopedKeys,
[EXTRA_SCOPE]: MOCK_ACCOUNT_KEYS.scopedKeys[SCOPE_APP_SYNC],
}; Assert.deepEqual(newUser.scopedKeys, expectedScopedKeys); Assert.equal(newUser.ecosystemUserId, null); Assert.equal(newUser.ecosystemAnonId, null);
}
);
// Test vectors from // https://wiki.mozilla.org/Identity/AttachedServices/KeyServerProtocol#Test_Vectors
add_task(async function test_getKeyForScope_oldsync() {
let fxa = new MockFxAccounts();
let client = fxa._internal.fxAccountsClient;
client.getScopedKeyData = () =>
Promise.resolve({
[SCOPE_APP_SYNC]: {
identifier: SCOPE_APP_SYNC,
keyRotationSecret: "0000000000000000000000000000000000000000000000000000000000000000",
keyRotationTimestamp: 1510726317123,
},
});
// We mock the server returning the wrapKB from our test vectors
client.accountKeys = async () => { return {
wrapKB: CommonUtils.hexToBytes( "404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f"
),
};
};
// We set the user to have the keyFetchToken and unwrapBKey from our test vectors
let user = {
...getTestUser("eusebius"),
uid: "aeaa1725c7a24ff983c6295725d5fc9b",
keyFetchToken: "808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f",
unwrapBKey: "6ea660be9c89ec355397f89afb282ea0bf21095760c8c5009bbcc894155bbe2a",
sessionToken: "mock session token, used in metadata request",
verified: true,
};
await fxa.setSignedInUser(user);
// We derive, persist and return the sync key const key = await fxa.keys.getKeyForScope(SCOPE_APP_SYNC);
// We verify the key returned matches what we would expect from the test vectors // kb = 2ee722fdd8ccaa721bdeb2d1b76560efef705b04349d9357c3e592cf4906e075 (from test vectors) // // kid can be verified by "${keyRotationTimestamp}-${sha256(kb)[0:16]}" // // k can be verified by HKDF(kb, undefined, "identity.mozilla.com/picl/v1/oldsync", 64) Assert.deepEqual(key, {
scope: SCOPE_APP_SYNC,
kid: "1510726317123-BAik7hEOdpGnPZnPBSdaTg",
k: "fwM5VZu0Spf5XcFRZYX2zk6MrqZP7zvovCBcvuKwgYMif3hz98FHmIVa3qjKjrW0J244Zj-P5oWaOcQbvypmpw",
kty: "oct",
});
});
add_task(async function test_getScopedKeys_cached_key() {
let fxa = new MockFxAccounts();
let user = {
...getTestUser("eusebius"),
uid: "aeaa1725c7a24ff983c6295725d5fc9b",
verified: true,
...MOCK_ACCOUNT_KEYS,
};
add_task(async function test_getScopedKeys_unavailable_scope() {
let fxa = new MockFxAccounts();
let user = {
...getTestUser("eusebius"),
uid: "aeaa1725c7a24ff983c6295725d5fc9b",
verified: true,
...MOCK_ACCOUNT_KEYS,
};
await fxa.setSignedInUser(user);
await Assert.rejects(
fxa.keys.getKeyForScope("otherkeybearingscope"),
/Key not available for scope/
);
});
add_task(async function test_getScopedKeys_misconfigured_fxa_server() {
let fxa = new MockFxAccounts();
let client = fxa._internal.fxAccountsClient;
client.getScopedKeyData = () =>
Promise.resolve({
wrongscope: {
identifier: "wrongscope",
keyRotationSecret: "0000000000000000000000000000000000000000000000000000000000000000",
keyRotationTimestamp: 1510726331712,
},
});
let user = {
...getTestUser("eusebius"),
uid: "aeaa1725c7a24ff983c6295725d5fc9b",
verified: true,
keyFetchToken: "808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f",
unwrapBKey: "6ea660be9c89ec355397f89afb282ea0bf21095760c8c5009bbcc894155bbe2a",
sessionToken: "mock session token, used in metadata request",
};
await fxa.setSignedInUser(user);
await Assert.rejects(
fxa.keys.getKeyForScope(SCOPE_APP_SYNC),
/The FxA server did not grant Firefox the sync scope/
);
});
add_task(async function test_setScopedKeys_user_not_signed_in() { const fxa = new MockFxAccounts();
await Assert.rejects(
fxa.keys.setScopedKeys(MOCK_ACCOUNT_KEYS.scopedKeys),
/Cannot persist keys, no user signed in/
);
});
// _fetchAndUnwrapAndDeriveKeys with no keyFetchToken should trigger signOut // XXX - actually, it probably shouldn't - bug 1572313.
add_test(function test_fetchAndUnwrapAndDeriveKeys_no_token() {
let fxa = new MockFxAccounts();
let user = getTestUser("lettuce.protheroe"); delete user.keyFetchToken;
add_task(async function test_resend_email_not_signed_in() {
let fxa = new MockFxAccounts();
try {
await fxa.resendVerificationEmail();
} catch (err) { Assert.equal(err.message, ERROR_NO_ACCOUNT); return;
}
do_throw("Should not be able to resend email when nobody is signed in");
});
add_task(async function test_accountStatus() {
let fxa = new MockFxAccounts();
let alice = getTestUser("alice");
// If we have no user, we have no account server-side
let result = await fxa.checkAccountStatus(); Assert.ok(!result); // Set a user - the fxAccountsClient mock will say "ok".
await fxa.setSignedInUser(alice);
result = await fxa.checkAccountStatus(); Assert.ok(result); // flag the item as deleted on the server.
fxa._internal.fxAccountsClient._deletedOnServer = true;
result = await fxa.checkAccountStatus(); Assert.ok(!result);
fxa._internal.fxAccountsClient._deletedOnServer = false;
await fxa.signOut();
});
add_task(async function test_resend_email_invalid_token() {
let fxa = new MockFxAccounts();
let sophia = getTestUser("sophia"); Assert.notEqual(sophia.sessionToken, null);
let client = fxa._internal.fxAccountsClient;
client.resendVerificationEmail = () => { return Promise.reject({
code: 401,
errno: ERRNO_INVALID_AUTH_TOKEN,
});
}; // This test wants the account to exist but the local session invalid.
client.accountStatus = uid => { Assert.ok(uid, "got a uid to check"); return Promise.resolve(true);
};
client.sessionStatus = token => { Assert.ok(token, "got a token to check"); return Promise.resolve(false);
};
await fxa.setSignedInUser(sophia);
let user = await fxa._internal.getUserAccountData(); Assert.equal(user.email, sophia.email); Assert.equal(user.verified, false);
log.debug("Sophia wants verification email resent");
fxa.resendVerificationEmail().then(result => { // Mock server response; ensures that the session token actually was // passed to the client to make the hawk call Assert.equal(result, "alice's session token");
fxa._internal.abortExistingFlow();
run_next_test();
});
});
});
});
add_test(async function test_getOAuthTokenWithSessionToken() {
Services.prefs.setBoolPref( "identity.fxaccounts.useSessionTokensForOAuth", true
);
let fxa = new MockFxAccounts();
let alice = getTestUser("alice");
alice.verified = true;
let oauthTokenCalled = false;
add_task(async function test_getOAuthTokenCachedWithSessionToken() {
Services.prefs.setBoolPref( "identity.fxaccounts.useSessionTokensForOAuth", true
);
let fxa = new MockFxAccounts();
let alice = getTestUser("alice");
alice.verified = true;
let numOauthTokenCalls = 0;
await fxa.setSignedInUser(alice);
let result = await fxa.getOAuthToken({
scope: "profile",
service: "test-service",
}); Assert.equal(numOauthTokenCalls, 1); Assert.equal(
result, "43793fdfffec22eb39fc3c44ed09193a6fde4c24e5d6a73f73178597b268af69"
);
// requesting it again should not re-fetch the token.
result = await fxa.getOAuthToken({
scope: "profile",
service: "test-service",
}); Assert.equal(numOauthTokenCalls, 1); Assert.equal(
result, "43793fdfffec22eb39fc3c44ed09193a6fde4c24e5d6a73f73178597b268af69"
); // But requesting the same service and a different scope *will* get a new one.
result = await fxa.getOAuthToken({
scope: "something-else",
service: "test-service",
}); Assert.equal(numOauthTokenCalls, 2); Assert.equal(
result, "43793fdfffec22eb39fc3c44ed09193a6fde4c24e5d6a73f73178597b268af69"
);
Services.prefs.setBoolPref( "identity.fxaccounts.useSessionTokensForOAuth", false
);
});
add_test(function test_getOAuthTokenScopedWithSessionToken() {
let fxa = new MockFxAccounts();
let alice = getTestUser("alice");
alice.verified = true;
let numOauthTokenCalls = 0;
let client = fxa._internal.fxAccountsClient;
client.accessTokenWithSessionToken = async (
_sessionTokenHex,
_clientId,
scopeString
) => {
equal(scopeString, "bar foo"); // scopes are sorted locally before request.
numOauthTokenCalls++; return MOCK_TOKEN_RESPONSE;
};
add_task(async function test_getOAuthTokenCachedScopeNormalization() {
let fxa = new MockFxAccounts();
let alice = getTestUser("alice");
alice.verified = true;
let numOAuthTokenCalls = 0;
await fxa.setSignedInUser(alice);
let result = await fxa.getOAuthToken({
scope: ["foo", "bar"],
service: "test-service",
}); Assert.equal(numOAuthTokenCalls, 1); Assert.equal(
result, "43793fdfffec22eb39fc3c44ed09193a6fde4c24e5d6a73f73178597b268af69"
);
// requesting it again with the scope array in a different order should not re-fetch the token.
result = await fxa.getOAuthToken({
scope: ["bar", "foo"],
service: "test-service",
}); Assert.equal(numOAuthTokenCalls, 1); Assert.equal(
result, "43793fdfffec22eb39fc3c44ed09193a6fde4c24e5d6a73f73178597b268af69"
); // requesting it again with the scope array in different case should not re-fetch the token.
result = await fxa.getOAuthToken({
scope: ["Bar", "Foo"],
service: "test-service",
}); Assert.equal(numOAuthTokenCalls, 1); Assert.equal(
result, "43793fdfffec22eb39fc3c44ed09193a6fde4c24e5d6a73f73178597b268af69"
); // But requesting with a new entry in the array does fetch one.
result = await fxa.getOAuthToken({
scope: ["foo", "bar", "etc"],
service: "test-service",
}); Assert.equal(numOAuthTokenCalls, 2); Assert.equal(
result, "43793fdfffec22eb39fc3c44ed09193a6fde4c24e5d6a73f73178597b268af69"
);
});
add_test(function test_getOAuthToken_invalid_param() {
let fxa = new MockFxAccounts();
await fxa._internal.setSignedInUser(alice);
fxa._internal._profile = mockProfile;
let result = await fxa.getSignedInUser(); Assert.ok(!!result); Assert.equal(result.avatar, "image");
});
add_task(async function test_getSignedInUserProfile_error_uses_account_data() {
let fxa = new MockFxAccounts();
let alice = getTestUser("alice");
alice.verified = true;
let teardownCalled = false;
await fxa.setSignedInUser(alice);
let result = await fxa.getSignedInUser(); Assert.deepEqual(result.avatar, null);
await fxa.signOut(); Assert.ok(teardownCalled);
});
add_task(async function test_flushLogFile() {
_("Tests flushLogFile");
let account = await MakeFxAccounts();
let promiseObserved = new Promise(res => {
log.info("Adding flush-log-file observer.");
Services.obs.addObserver(function onFlushLogFile() {
Services.obs.removeObserver(
onFlushLogFile, "service:log-manager:flush-log-file"
);
res();
}, "service:log-manager:flush-log-file");
});
account.flushLogFile();
await promiseObserved;
});
/* *Endoftests. *Utilityfunctionsfollow.
*/
function expandHex(two_hex) { // Return a 64-character hex string, encoding 32 identical bytes.
let eight_hex = two_hex + two_hex + two_hex + two_hex;
let thirtytwo_hex = eight_hex + eight_hex + eight_hex + eight_hex; return thirtytwo_hex + thirtytwo_hex;
}
function expandBytes(two_hex) { return CommonUtils.hexToBytes(expandHex(two_hex));
}
function getTestUser(name) { return {
email: name + "@example.com",
uid: "1ad7f5024cc74ec1a209071fd2fae348",
sessionToken: name + "'s session token",
keyFetchToken: name + "'s keyfetch token",
unwrapBKey: expandHex("44"),
verified: false,
encryptedSendTabKeys: name + "'s encrypted Send tab keys",
};
}
function makeObserver(aObserveTopic, aObserveFunc) {
let observer = { // nsISupports provides type management in C++ // nsIObserver is to be an observer
QueryInterface: ChromeUtils.generateQI(["nsIObserver"]),
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.