mirror of
https://github.com/logto-io/logto.git
synced 2024-12-16 20:26:19 -05:00
Merge pull request #4738 from logto-io/gao-refactor-integration-tests
refactor(test): manage test resource lifecycle
This commit is contained in:
commit
7c4f0cd56f
9 changed files with 188 additions and 90 deletions
|
@ -2,7 +2,7 @@ import type { Identities, Role, User } from '@logto/schemas';
|
|||
|
||||
import { authedAdminApi } from './api.js';
|
||||
|
||||
type CreateUserPayload = Partial<{
|
||||
export type CreateUserPayload = Partial<{
|
||||
primaryEmail: string;
|
||||
primaryPhone: string;
|
||||
username: string;
|
||||
|
|
|
@ -7,9 +7,15 @@ import {
|
|||
import { authedAdminApi } from './api.js';
|
||||
import { ApiFactory } from './factory.js';
|
||||
|
||||
class OrganizationRoleApi extends ApiFactory<
|
||||
export type CreateOrganizationRolePostData = {
|
||||
name: string;
|
||||
description?: string;
|
||||
organizationScopeIds?: string[];
|
||||
};
|
||||
|
||||
export class OrganizationRoleApi extends ApiFactory<
|
||||
OrganizationRole,
|
||||
{ name: string; description?: string; organizationScopeIds?: string[] }
|
||||
CreateOrganizationRolePostData
|
||||
> {
|
||||
constructor() {
|
||||
super('organization-roles');
|
||||
|
@ -41,5 +47,3 @@ class OrganizationRoleApi extends ApiFactory<
|
|||
await authedAdminApi.delete(`${this.path}/${id}/scopes/${scopeId}`);
|
||||
}
|
||||
}
|
||||
|
||||
export const roleApi = new OrganizationRoleApi();
|
||||
|
|
|
@ -2,7 +2,7 @@ import { type OrganizationScope } from '@logto/schemas';
|
|||
|
||||
import { ApiFactory } from './factory.js';
|
||||
|
||||
class OrganizationScopeApi extends ApiFactory<
|
||||
export class OrganizationScopeApi extends ApiFactory<
|
||||
OrganizationScope,
|
||||
{ name: string; description?: string }
|
||||
> {
|
||||
|
@ -10,6 +10,3 @@ class OrganizationScopeApi extends ApiFactory<
|
|||
super('organization-scopes');
|
||||
}
|
||||
}
|
||||
|
||||
/** API methods for operating organization template scopes. */
|
||||
export const scopeApi = new OrganizationScopeApi();
|
||||
|
|
|
@ -3,7 +3,10 @@ import { type Role, type Organization, type OrganizationWithRoles } from '@logto
|
|||
import { authedAdminApi } from './api.js';
|
||||
import { ApiFactory } from './factory.js';
|
||||
|
||||
class OrganizationApi extends ApiFactory<Organization, { name: string; description?: string }> {
|
||||
export class OrganizationApi extends ApiFactory<
|
||||
Organization,
|
||||
{ name: string; description?: string }
|
||||
> {
|
||||
constructor() {
|
||||
super('organizations');
|
||||
}
|
||||
|
@ -38,6 +41,3 @@ class OrganizationApi extends ApiFactory<Organization, { name: string; descripti
|
|||
return authedAdminApi.get(`users/${userId}/organizations`).json<OrganizationWithRoles[]>();
|
||||
}
|
||||
}
|
||||
|
||||
/** API methods for operating organizations. */
|
||||
export const organizationApi = new OrganizationApi();
|
||||
|
|
95
packages/integration-tests/src/helpers/organization.ts
Normal file
95
packages/integration-tests/src/helpers/organization.ts
Normal file
|
@ -0,0 +1,95 @@
|
|||
import { type OrganizationScope, type OrganizationRole, type Organization } from '@logto/schemas';
|
||||
import { trySafe } from '@silverhand/essentials';
|
||||
|
||||
import {
|
||||
type CreateOrganizationRolePostData,
|
||||
OrganizationRoleApi,
|
||||
} from '#src/api/organization-role.js';
|
||||
import { OrganizationScopeApi } from '#src/api/organization-scope.js';
|
||||
import { OrganizationApi } from '#src/api/organization.js';
|
||||
|
||||
/* eslint-disable @silverhand/fp/no-mutating-methods */
|
||||
/**
|
||||
* A help class that records the created organization roles, and provides a `cleanUp` method to
|
||||
* delete them.
|
||||
*/
|
||||
export class OrganizationRoleApiTest extends OrganizationRoleApi {
|
||||
protected roles: OrganizationRole[] = [];
|
||||
|
||||
override async create(data: CreateOrganizationRolePostData): Promise<OrganizationRole> {
|
||||
const created = await super.create(data);
|
||||
this.roles.push(created);
|
||||
return created;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete all created roles. This method will ignore errors when deleting roles to avoid error
|
||||
* when they are deleted by other tests.
|
||||
*/
|
||||
async cleanUp(): Promise<void> {
|
||||
// Use `trySafe` to avoid error when role is deleted by other tests.
|
||||
await Promise.all(this.roles.map(async (role) => trySafe(this.delete(role.id))));
|
||||
this.roles = [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A help class that records the created organization scopes, and provides a `cleanUp` method to
|
||||
* delete them.
|
||||
*/
|
||||
export class OrganizationScopeApiTest extends OrganizationScopeApi {
|
||||
protected scopes: OrganizationScope[] = [];
|
||||
|
||||
override async create(data: { name: string; description?: string }): Promise<OrganizationScope> {
|
||||
const created = await super.create(data);
|
||||
this.scopes.push(created);
|
||||
return created;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete all created scopes. This method will ignore errors when deleting scopes to avoid error
|
||||
* when they are deleted by other tests.
|
||||
*/
|
||||
async cleanUp(): Promise<void> {
|
||||
// Use `trySafe` to avoid error when scope is deleted by other tests.
|
||||
await Promise.all(this.scopes.map(async (scope) => trySafe(this.delete(scope.id))));
|
||||
this.scopes = [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A help class that records the created organizations, and provides a `cleanUp` method to
|
||||
* delete them. It also provides `roleApi` and `scopeApi` to manage the organization roles and
|
||||
* scopes.
|
||||
*
|
||||
* @see OrganizationRoleApiTest for more information about `roleApi`.
|
||||
* @see OrganizationScopeApiTest for more information about `scopeApi`.
|
||||
*/
|
||||
export class OrganizationApiTest extends OrganizationApi {
|
||||
roleApi = new OrganizationRoleApiTest();
|
||||
scopeApi = new OrganizationScopeApiTest();
|
||||
|
||||
protected organizations: Organization[] = [];
|
||||
|
||||
override async create(data: { name: string; description?: string }): Promise<Organization> {
|
||||
const created = await super.create(data);
|
||||
this.organizations.push(created);
|
||||
return created;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete all created organizations, roles and scopes. No need to call `cleanUp` of `roleApi` and
|
||||
* `scopeApi` manually.
|
||||
*
|
||||
* This method will ignore errors when deleting organizations, roles and scopes to avoid error
|
||||
* when they are deleted by other tests.
|
||||
*/
|
||||
async cleanUp(): Promise<void> {
|
||||
await Promise.all(
|
||||
// Use `trySafe` to avoid error when organization is deleted by other tests.
|
||||
this.organizations.map(async (organization) => trySafe(this.delete(organization.id)))
|
||||
);
|
||||
this.organizations = [];
|
||||
}
|
||||
}
|
||||
/* eslint-enable @silverhand/fp/no-mutating-methods */
|
|
@ -1,4 +1,7 @@
|
|||
import { createUser } from '#src/api/index.js';
|
||||
import { type User } from '@logto/schemas';
|
||||
import { trySafe } from '@silverhand/essentials';
|
||||
|
||||
import { type CreateUserPayload, createUser, deleteUser } from '#src/api/index.js';
|
||||
import {
|
||||
generateUsername,
|
||||
generateEmail,
|
||||
|
@ -46,3 +49,24 @@ export const generateNewUser = async <T extends NewUserProfileOptions>(options:
|
|||
|
||||
return { user, userProfile };
|
||||
};
|
||||
|
||||
export class UserApiTest {
|
||||
protected users: User[] = [];
|
||||
|
||||
async create(data: CreateUserPayload): Promise<User> {
|
||||
const user = await createUser(data);
|
||||
// eslint-disable-next-line @silverhand/fp/no-mutating-methods
|
||||
this.users.push(user);
|
||||
return user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete all created users. This method will ignore errors when deleting users to avoid error
|
||||
* when they are deleted by other tests.
|
||||
*/
|
||||
async cleanUp(): Promise<void> {
|
||||
// Use `trySafe` to avoid error when user is deleted by other tests.
|
||||
await Promise.all(this.users.map(async (user) => trySafe(deleteUser(user.id))));
|
||||
this.users = [];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,17 +4,22 @@ import { generateStandardId } from '@logto/shared';
|
|||
import { isKeyInObject } from '@silverhand/essentials';
|
||||
import { HTTPError } from 'got';
|
||||
|
||||
import { roleApi } from '#src/api/organization-role.js';
|
||||
import { scopeApi } from '#src/api/organization-scope.js';
|
||||
import { OrganizationRoleApiTest, OrganizationScopeApiTest } from '#src/helpers/organization.js';
|
||||
|
||||
const randomId = () => generateStandardId(4);
|
||||
|
||||
// Add additional layer of describe to run tests in band
|
||||
describe('organization role APIs', () => {
|
||||
describe('organization roles', () => {
|
||||
const roleApi = new OrganizationRoleApiTest();
|
||||
|
||||
afterEach(async () => {
|
||||
await roleApi.cleanUp();
|
||||
});
|
||||
|
||||
it('should fail if the name of the new organization role already exists', async () => {
|
||||
const name = 'test' + randomId();
|
||||
const createdRole = await roleApi.create({ name });
|
||||
await roleApi.create({ name });
|
||||
const response = await roleApi.create({ name }).catch((error: unknown) => error);
|
||||
|
||||
assert(response instanceof HTTPError);
|
||||
|
@ -23,13 +28,11 @@ describe('organization role APIs', () => {
|
|||
const body: unknown = JSON.parse(String(raw));
|
||||
expect(statusCode).toBe(422);
|
||||
expect(isKeyInObject(body, 'code') && body.code).toBe('entity.unique_integrity_violation');
|
||||
|
||||
await roleApi.delete(createdRole.id);
|
||||
});
|
||||
|
||||
it('should get organization roles successfully', async () => {
|
||||
const [name1, name2] = ['test' + randomId(), 'test' + randomId()];
|
||||
const createdRoles = await Promise.all([
|
||||
await Promise.all([
|
||||
roleApi.create({ name: name1, description: 'A test organization role.' }),
|
||||
roleApi.create({ name: name2 }),
|
||||
]);
|
||||
|
@ -39,13 +42,11 @@ describe('organization role APIs', () => {
|
|||
expect.objectContaining({ name: name1, description: 'A test organization role.' })
|
||||
);
|
||||
expect(roles).toContainEqual(expect.objectContaining({ name: name2, description: null }));
|
||||
|
||||
await Promise.all(createdRoles.map(async (role) => roleApi.delete(role.id)));
|
||||
});
|
||||
|
||||
it('should get organization roles with pagination', async () => {
|
||||
// Add 20 roles to exceed the default page size
|
||||
const allRoles = await Promise.all(
|
||||
await Promise.all(
|
||||
Array.from({ length: 30 }).map(async () => roleApi.create({ name: 'test' + randomId() }))
|
||||
);
|
||||
|
||||
|
@ -61,8 +62,6 @@ describe('organization role APIs', () => {
|
|||
expect(roles2.length).toBeGreaterThanOrEqual(10);
|
||||
expect(roles2[0]?.id).not.toBeFalsy();
|
||||
expect(roles2[0]?.id).toBe(roles[10]?.id);
|
||||
|
||||
await Promise.all(allRoles.map(async (role) => roleApi.delete(role.id)));
|
||||
});
|
||||
|
||||
it('should be able to create and get organization roles by id', async () => {
|
||||
|
@ -70,7 +69,6 @@ describe('organization role APIs', () => {
|
|||
const { scopes, ...role } = await roleApi.get(createdRole.id);
|
||||
|
||||
expect(role).toStrictEqual(createdRole);
|
||||
await roleApi.delete(createdRole.id);
|
||||
});
|
||||
|
||||
it('should fail when try to get an organization role that does not exist', async () => {
|
||||
|
@ -91,7 +89,6 @@ describe('organization role APIs', () => {
|
|||
name: newName,
|
||||
description: 'test description.',
|
||||
});
|
||||
await roleApi.delete(createdRole.id);
|
||||
});
|
||||
|
||||
it('should be able to delete organization role', async () => {
|
||||
|
@ -108,6 +105,13 @@ describe('organization role APIs', () => {
|
|||
});
|
||||
|
||||
describe('organization role - scope relations', () => {
|
||||
const roleApi = new OrganizationRoleApiTest();
|
||||
const scopeApi = new OrganizationScopeApiTest();
|
||||
|
||||
afterEach(async () => {
|
||||
await Promise.all([roleApi.cleanUp(), scopeApi.cleanUp()]);
|
||||
});
|
||||
|
||||
it('should be able to add and get scopes of a role', async () => {
|
||||
const [role, scope1, scope2] = await Promise.all([
|
||||
roleApi.create({ name: 'test' + randomId() }),
|
||||
|
@ -127,12 +131,6 @@ describe('organization role APIs', () => {
|
|||
name: scope2.name,
|
||||
})
|
||||
);
|
||||
|
||||
await Promise.all([
|
||||
roleApi.delete(role.id),
|
||||
scopeApi.delete(scope1.id),
|
||||
scopeApi.delete(scope2.id),
|
||||
]);
|
||||
});
|
||||
|
||||
it('should fail when try to add non-existent scopes to a role', async () => {
|
||||
|
@ -152,12 +150,6 @@ describe('organization role APIs', () => {
|
|||
code: 'entity.relation_foreign_key_not_found',
|
||||
})
|
||||
);
|
||||
|
||||
await Promise.all([
|
||||
roleApi.delete(role.id),
|
||||
scopeApi.delete(scope1.id),
|
||||
scopeApi.delete(scope2.id),
|
||||
]);
|
||||
});
|
||||
|
||||
it('should be able to remove scopes from a role', async () => {
|
||||
|
@ -180,12 +172,6 @@ describe('organization role APIs', () => {
|
|||
name: scope2.name,
|
||||
})
|
||||
);
|
||||
|
||||
await Promise.all([
|
||||
roleApi.delete(role.id),
|
||||
scopeApi.delete(scope1.id),
|
||||
scopeApi.delete(scope2.id),
|
||||
]);
|
||||
});
|
||||
|
||||
it('should fail when try to remove non-existent scopes from a role', async () => {
|
||||
|
@ -195,8 +181,6 @@ describe('organization role APIs', () => {
|
|||
|
||||
assert(response instanceof HTTPError);
|
||||
expect(response.response.statusCode).toBe(404);
|
||||
|
||||
await Promise.all([roleApi.delete(role.id)]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -4,11 +4,17 @@ import { generateStandardId } from '@logto/shared';
|
|||
import { isKeyInObject } from '@silverhand/essentials';
|
||||
import { HTTPError } from 'got';
|
||||
|
||||
import { scopeApi } from '#src/api/organization-scope.js';
|
||||
import { OrganizationScopeApiTest } from '#src/helpers/organization.js';
|
||||
|
||||
const randomId = () => generateStandardId(4);
|
||||
|
||||
describe('organization scopes', () => {
|
||||
describe('organization scope APIs', () => {
|
||||
const scopeApi = new OrganizationScopeApiTest();
|
||||
|
||||
afterEach(async () => {
|
||||
await scopeApi.cleanUp();
|
||||
});
|
||||
|
||||
it('should fail if the name of the new organization scope already exists', async () => {
|
||||
const name = 'test' + randomId();
|
||||
await scopeApi.create({ name });
|
||||
|
|
|
@ -3,15 +3,20 @@ import assert from 'node:assert';
|
|||
import { generateStandardId } from '@logto/shared';
|
||||
import { HTTPError } from 'got';
|
||||
|
||||
import { createUser, deleteUser } from '#src/api/admin-user.js';
|
||||
import { roleApi } from '#src/api/organization-role.js';
|
||||
import { organizationApi } from '#src/api/organization.js';
|
||||
import { OrganizationApiTest } from '#src/helpers/organization.js';
|
||||
import { UserApiTest } from '#src/helpers/user.js';
|
||||
|
||||
const randomId = () => generateStandardId(4);
|
||||
|
||||
// Add additional layer of describe to run tests in band
|
||||
describe('organization APIs', () => {
|
||||
describe('organizations', () => {
|
||||
const organizationApi = new OrganizationApiTest();
|
||||
|
||||
afterEach(async () => {
|
||||
await organizationApi.cleanUp();
|
||||
});
|
||||
|
||||
it('should get organizations successfully', async () => {
|
||||
await organizationApi.create({ name: 'test', description: 'A test organization.' });
|
||||
await organizationApi.create({ name: 'test2' });
|
||||
|
@ -23,15 +28,11 @@ describe('organization APIs', () => {
|
|||
expect(organizations).toContainEqual(
|
||||
expect.objectContaining({ name: 'test2', description: null })
|
||||
);
|
||||
|
||||
await Promise.all(
|
||||
organizations.map(async (organization) => organizationApi.delete(organization.id))
|
||||
);
|
||||
});
|
||||
|
||||
it('should get organizations with pagination', async () => {
|
||||
// Add organizations to exceed the default page size
|
||||
const allOrganizations = await Promise.all(
|
||||
await Promise.all(
|
||||
Array.from({ length: 30 }).map(async () => organizationApi.create({ name: 'test' }))
|
||||
);
|
||||
|
||||
|
@ -47,10 +48,6 @@ describe('organization APIs', () => {
|
|||
expect(organizations2.length).toBeGreaterThanOrEqual(10);
|
||||
expect(organizations2[0]?.id).not.toBeFalsy();
|
||||
expect(organizations2[0]?.id).toBe(organizations[10]?.id);
|
||||
|
||||
await Promise.all(
|
||||
allOrganizations.map(async (organization) => organizationApi.delete(organization.id))
|
||||
);
|
||||
});
|
||||
|
||||
it('should be able to create and get organizations by id', async () => {
|
||||
|
@ -58,7 +55,6 @@ describe('organization APIs', () => {
|
|||
const organization = await organizationApi.get(createdOrganization.id);
|
||||
|
||||
expect(organization).toStrictEqual(createdOrganization);
|
||||
await organizationApi.delete(createdOrganization.id);
|
||||
});
|
||||
|
||||
it('should fail when try to get an organization that does not exist', async () => {
|
||||
|
@ -78,7 +74,6 @@ describe('organization APIs', () => {
|
|||
name: 'test2',
|
||||
description: 'test description.',
|
||||
});
|
||||
await organizationApi.delete(createdOrganization.id);
|
||||
});
|
||||
|
||||
it('should be able to delete organization', async () => {
|
||||
|
@ -97,22 +92,24 @@ describe('organization APIs', () => {
|
|||
});
|
||||
|
||||
describe('organization - user relations', () => {
|
||||
const organizationApi = new OrganizationApiTest();
|
||||
const userApi = new UserApiTest();
|
||||
|
||||
afterEach(async () => {
|
||||
await Promise.all([organizationApi.cleanUp(), userApi.cleanUp()]);
|
||||
});
|
||||
|
||||
it('should be able to add and get organization users', async () => {
|
||||
const organization = await organizationApi.create({ name: 'test' });
|
||||
const [user1, user2] = await Promise.all([
|
||||
createUser({ username: 'test' + randomId() }),
|
||||
createUser({ username: 'test' + randomId() }),
|
||||
userApi.create({ username: 'test' + randomId() }),
|
||||
userApi.create({ username: 'test' + randomId() }),
|
||||
]);
|
||||
|
||||
await organizationApi.addUsers(organization.id, [user1.id, user2.id]);
|
||||
const users = await organizationApi.getUsers(organization.id);
|
||||
expect(users).toContainEqual(expect.objectContaining({ id: user1.id }));
|
||||
expect(users).toContainEqual(expect.objectContaining({ id: user2.id }));
|
||||
await Promise.all([
|
||||
organizationApi.delete(organization.id),
|
||||
deleteUser(user1.id),
|
||||
deleteUser(user2.id),
|
||||
]);
|
||||
});
|
||||
|
||||
it('should fail when try to add empty user list', async () => {
|
||||
|
@ -121,7 +118,6 @@ describe('organization APIs', () => {
|
|||
.addUsers(organization.id, [])
|
||||
.catch((error: unknown) => error);
|
||||
expect(response instanceof HTTPError && response.response.statusCode).toBe(400);
|
||||
await organizationApi.delete(organization.id);
|
||||
});
|
||||
|
||||
it('should fail when try to add user to an organization that does not exist', async () => {
|
||||
|
@ -135,13 +131,12 @@ describe('organization APIs', () => {
|
|||
|
||||
it('should be able to delete organization user', async () => {
|
||||
const organization = await organizationApi.create({ name: 'test' });
|
||||
const user = await createUser({ username: 'test' + randomId() });
|
||||
const user = await userApi.create({ username: 'test' + randomId() });
|
||||
|
||||
await organizationApi.addUsers(organization.id, [user.id]);
|
||||
await organizationApi.deleteUser(organization.id, user.id);
|
||||
const users = await organizationApi.getUsers(organization.id);
|
||||
expect(users).not.toContainEqual(user);
|
||||
await Promise.all([organizationApi.delete(organization.id), deleteUser(user.id)]);
|
||||
});
|
||||
|
||||
it('should fail when try to delete user from an organization that does not exist', async () => {
|
||||
|
@ -152,9 +147,17 @@ describe('organization APIs', () => {
|
|||
});
|
||||
|
||||
describe('organization - user - organization role relation routes', () => {
|
||||
const organizationApi = new OrganizationApiTest();
|
||||
const { roleApi } = organizationApi;
|
||||
const userApi = new UserApiTest();
|
||||
|
||||
afterEach(async () => {
|
||||
await Promise.all([organizationApi.cleanUp(), userApi.cleanUp()]);
|
||||
});
|
||||
|
||||
it("should be able to add and get user's organization roles", async () => {
|
||||
const organization = await organizationApi.create({ name: 'test' });
|
||||
const user = await createUser({ username: 'test' + randomId() });
|
||||
const user = await userApi.create({ username: 'test' + randomId() });
|
||||
const [role1, role2] = await Promise.all([
|
||||
roleApi.create({ name: 'test' + randomId() }),
|
||||
roleApi.create({ name: 'test' + randomId() }),
|
||||
|
@ -175,12 +178,6 @@ describe('organization APIs', () => {
|
|||
const roles = await organizationApi.getUserRoles(organization.id, user.id);
|
||||
expect(roles).toContainEqual(expect.objectContaining({ id: role1.id }));
|
||||
expect(roles).toContainEqual(expect.objectContaining({ id: role2.id }));
|
||||
await Promise.all([
|
||||
organizationApi.delete(organization.id),
|
||||
deleteUser(user.id),
|
||||
roleApi.delete(role1.id),
|
||||
roleApi.delete(role2.id),
|
||||
]);
|
||||
});
|
||||
|
||||
it('should be able to get all organizations with roles for a user', async () => {
|
||||
|
@ -188,7 +185,7 @@ describe('organization APIs', () => {
|
|||
organizationApi.create({ name: 'test' }),
|
||||
organizationApi.create({ name: 'test' }),
|
||||
]);
|
||||
const user = await createUser({ username: 'test' + randomId() });
|
||||
const user = await userApi.create({ username: 'test' + randomId() });
|
||||
const [role1, role2] = await Promise.all([
|
||||
roleApi.create({ name: 'test' + randomId() }),
|
||||
roleApi.create({ name: 'test' + randomId() }),
|
||||
|
@ -222,15 +219,6 @@ describe('organization APIs', () => {
|
|||
expect(organization2WithRoles.organizationRoles).toContainEqual(
|
||||
expect.objectContaining({ id: role2.id })
|
||||
);
|
||||
|
||||
// Clean up
|
||||
await Promise.all([
|
||||
organizationApi.delete(organization1.id),
|
||||
organizationApi.delete(organization2.id),
|
||||
deleteUser(user.id),
|
||||
roleApi.delete(role1.id),
|
||||
roleApi.delete(role2.id),
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue