Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(auth): Fixing flaky deleteUsers() integration tests #917

Merged
merged 1 commit into from
Jun 24, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
16 changes: 9 additions & 7 deletions src/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -590,6 +590,8 @@ declare namespace admin.auth {
export import UpdatePhoneMultiFactorInfoRequest = _auth.admin.auth.UpdatePhoneMultiFactorInfoRequest;
export import MultiFactorCreateSettings = _auth.admin.auth.MultiFactorCreateSettings;
export import MultiFactorUpdateSettings = _auth.admin.auth.MultiFactorUpdateSettings;
export import DeleteUsersResult = _auth.admin.auth.DeleteUsersResult;
export import GetUsersResult = _auth.admin.auth.GetUsersResult;
}

declare namespace admin.credential {
Expand Down Expand Up @@ -882,7 +884,7 @@ declare namespace admin.remoteConfig {

/**
* Interface representing a Remote Config parameter.
* At minimum, a `defaultValue` or a `conditionalValues` entry must be present for the
* At minimum, a `defaultValue` or a `conditionalValues` entry must be present for the
* parameter to have any effect.
*/
interface RemoteConfigParameter {
Expand All @@ -894,7 +896,7 @@ declare namespace admin.remoteConfig {

/**
* A `(condition name, value)` map. The condition name of the highest priority
* (the one listed first in the Remote Config template's conditions list) determines the value of
* (the one listed first in the Remote Config template's conditions list) determines the value of
* this parameter.
*/
conditionalValues?: { [key: string]: RemoteConfigParameterValue };
Expand Down Expand Up @@ -977,7 +979,7 @@ declare namespace admin.remoteConfig {

/**
* Type representing a Remote Config parameter value.
* A `RemoteConfigParameterValue` could be either an `ExplicitParameterValue` or
* A `RemoteConfigParameterValue` could be either an `ExplicitParameterValue` or
* an `InAppDefaultValue`.
*/
type RemoteConfigParameterValue = ExplicitParameterValue | InAppDefaultValue;
Expand Down Expand Up @@ -1012,11 +1014,11 @@ declare namespace admin.remoteConfig {
*
* @param template The Remote Config template to be published.
* @param options Optional options object when publishing a Remote Config template:
* - {boolean} `force` Setting this to `true` forces the Remote Config template to
* be updated and circumvent the ETag. This approach is not recommended
* because it risks causing the loss of updates to your Remote Config
* - {boolean} `force` Setting this to `true` forces the Remote Config template to
* be updated and circumvent the ETag. This approach is not recommended
* because it risks causing the loss of updates to your Remote Config
* template if multiple clients are updating the Remote Config template.
* See {@link https://firebase.google.com/docs/remote-config/use-config-rest#etag_usage_and_forced_updates
* See {@link https://firebase.google.com/docs/remote-config/use-config-rest#etag_usage_and_forced_updates
* ETag usage and forced updates}.
*
* @return A Promise that fulfills with the published `RemoteConfigTemplate`.
Expand Down
28 changes: 22 additions & 6 deletions test/integration/auth.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ describe('admin.auth', () => {
// left over from a prior run).
const uidsToDelete = usersToCreate.map((user) => user.uid);
uidsToDelete.push(importUser1.uid);
await admin.auth().deleteUsers(uidsToDelete);
await deleteUsersWithDelay(uidsToDelete);

// Create/import users required by these tests
await Promise.all(usersToCreate.map((user) => admin.auth().createUser(user)));
Expand All @@ -267,7 +267,7 @@ describe('admin.auth', () => {
after(async () => {
const uidsToDelete = usersToCreate.map((user) => user.uid);
uidsToDelete.push(importUser1.uid);
await admin.auth().deleteUsers(uidsToDelete);
await deleteUsersWithDelay(uidsToDelete);
});

it('returns users by various identifier types in a single call', async () => {
Expand Down Expand Up @@ -1461,7 +1461,7 @@ describe('admin.auth', () => {
const uid3 = await admin.auth().createUser({}).then((ur) => ur.uid);
const ids = [{uid: uid1}, {uid: uid2}, {uid: uid3}];

return admin.auth().deleteUsers([uid1, uid2, uid3])
return deleteUsersWithDelay([uid1, uid2, uid3])
.then((deleteUsersResult) => {
expect(deleteUsersResult.successCount).to.equal(3);
expect(deleteUsersResult.failureCount).to.equal(0);
Expand All @@ -1480,7 +1480,7 @@ describe('admin.auth', () => {
const uid2 = 'uid-that-doesnt-exist';
const ids = [{uid: uid1}, {uid: uid2}];

return admin.auth().deleteUsers([uid1, uid2])
return deleteUsersWithDelay([uid1, uid2])
.then((deleteUsersResult) => {
expect(deleteUsersResult.successCount).to.equal(2);
expect(deleteUsersResult.failureCount).to.equal(0);
Expand All @@ -1497,13 +1497,13 @@ describe('admin.auth', () => {
it('is idempotent', async () => {
const uid = await admin.auth().createUser({}).then((ur) => ur.uid);

return admin.auth().deleteUsers([uid])
return deleteUsersWithDelay([uid])
.then((deleteUsersResult) => {
expect(deleteUsersResult.successCount).to.equal(1);
expect(deleteUsersResult.failureCount).to.equal(0);
})
// Delete the user again, ensuring that everything still counts as a success.
.then(() => admin.auth().deleteUsers([uid]))
.then(() => deleteUsersWithDelay([uid]))
.then((deleteUsersResult) => {
expect(deleteUsersResult.successCount).to.equal(1);
expect(deleteUsersResult.failureCount).to.equal(0);
Expand Down Expand Up @@ -2083,6 +2083,22 @@ function safeDelete(uid: string): Promise<void> {
return deletePromise;
}

/**
* Deletes the specified list of users by calling the `deleteUsers()` API. This
* API is rate limited at 1 QPS, and therefore this helper function staggers
* subsequent invocations by adding 1 second delay to each call.
*
* @param {string[]} uids The list of user identifiers to delete.
* @return {Promise} A promise that resolves when delete operation resolves.
*/
function deleteUsersWithDelay(uids: string[]): Promise<admin.auth.DeleteUsersResult> {
return new Promise((resolve) => {
setTimeout(resolve, 1000);
}).then(() => {
return admin.auth().deleteUsers(uids);
});
}

/**
* Asserts actual object is equal to expected object while ignoring key order.
* This is useful since to.deep.equal fails when order differs.
Expand Down