0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2025-02-17 22:04:19 -05:00

Merge pull request #4739 from logto-io/gao-add-organization-user-api-tests

chore(test): add integration tests for new APIs
This commit is contained in:
Gao Sun 2023-10-24 23:46:04 -05:00 committed by GitHub
commit fb48db4185
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 434 additions and 152 deletions

View file

@ -50,7 +50,7 @@ function AddMembersToOrganization({ organization, isOpen, onClose }: Props) {
await api.post(`api/organizations/${organization.id}/users/roles`, {
json: {
userIds: data.users.map(({ id }) => id),
roleIds: data.scopes.map(({ value }) => value),
organizationRoleIds: data.scopes.map(({ value }) => value),
},
});
}

View file

@ -58,7 +58,10 @@ export const verifyBearerTokenFromRequest = async (
const userId = request.headers['development-user-id']?.toString() ?? developmentUserId;
if ((!isProduction || isIntegrationTest) && userId) {
consoleLog.warn(`Found dev user ID ${userId}, skip token validation.`);
// This log is distracting in integration tests.
if (!isIntegrationTest) {
consoleLog.warn(`Found dev user ID ${userId}, skip token validation.`);
}
return {
sub: userId,

View file

@ -67,16 +67,16 @@ export default function organizationRoutes<T extends AuthedRouter>(...args: Rout
params: z.object({ id: z.string().min(1) }),
body: z.object({
userIds: z.string().min(1).array().nonempty(),
roleIds: z.string().min(1).array().nonempty(),
organizationRoleIds: z.string().min(1).array().nonempty(),
}),
status: [201, 422],
}),
async (ctx, next) => {
const { id } = ctx.guard.params;
const { userIds, roleIds } = ctx.guard.body;
const { userIds, organizationRoleIds } = ctx.guard.body;
await organizations.relations.rolesUsers.insert(
...roleIds.flatMap<[string, string, string]>((roleId) =>
...organizationRoleIds.flatMap<[string, string, string]>((roleId) =>
userIds.map<[string, string, string]>((userId) => [id, roleId, userId])
)
);

View file

@ -1,8 +1,19 @@
import { type Role, type Organization, type OrganizationWithRoles } from '@logto/schemas';
import {
type Role,
type Organization,
type OrganizationWithRoles,
type UserWithOrganizationRoles,
} from '@logto/schemas';
import { authedAdminApi } from './api.js';
import { ApiFactory } from './factory.js';
type Query = {
q?: string;
page?: number;
page_size?: number;
};
export class OrganizationApi extends ApiFactory<
Organization,
{ name: string; description?: string }
@ -15,8 +26,12 @@ export class OrganizationApi extends ApiFactory<
await authedAdminApi.post(`${this.path}/${id}/users`, { json: { userIds } });
}
async getUsers(id: string): Promise<Organization[]> {
return authedAdminApi.get(`${this.path}/${id}/users`).json<Organization[]>();
async getUsers(
id: string,
query?: Query
): Promise<[rows: UserWithOrganizationRoles[], totalCount: number]> {
const got = await authedAdminApi.get(`${this.path}/${id}/users`, { searchParams: query });
return [JSON.parse(got.body), Number(got.headers['total-number'] ?? 0)];
}
async deleteUser(id: string, userId: string): Promise<void> {
@ -29,6 +44,12 @@ export class OrganizationApi extends ApiFactory<
});
}
async addUsersRoles(id: string, userIds: string[], organizationRoleIds: string[]): Promise<void> {
await authedAdminApi.post(`${this.path}/${id}/users/roles`, {
json: { userIds, organizationRoleIds },
});
}
async getUserRoles(id: string, userId: string): Promise<Role[]> {
return authedAdminApi.get(`${this.path}/${id}/users/${userId}/roles`).json<Role[]>();
}

View file

@ -14,7 +14,11 @@ import { OrganizationApi } from '#src/api/organization.js';
* delete them.
*/
export class OrganizationRoleApiTest extends OrganizationRoleApi {
protected roles: OrganizationRole[] = [];
#roles: OrganizationRole[] = [];
get roles(): OrganizationRole[] {
return this.#roles;
}
override async create(data: CreateOrganizationRolePostData): Promise<OrganizationRole> {
const created = await super.create(data);
@ -29,7 +33,7 @@ export class OrganizationRoleApiTest extends OrganizationRoleApi {
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 = [];
this.#roles = [];
}
}
@ -38,7 +42,11 @@ export class OrganizationRoleApiTest extends OrganizationRoleApi {
* delete them.
*/
export class OrganizationScopeApiTest extends OrganizationScopeApi {
protected scopes: OrganizationScope[] = [];
#scopes: OrganizationScope[] = [];
get scopes(): OrganizationScope[] {
return this.#scopes;
}
override async create(data: { name: string; description?: string }): Promise<OrganizationScope> {
const created = await super.create(data);
@ -53,7 +61,7 @@ export class OrganizationScopeApiTest extends OrganizationScopeApi {
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 = [];
this.#scopes = [];
}
}
@ -69,7 +77,11 @@ export class OrganizationApiTest extends OrganizationApi {
roleApi = new OrganizationRoleApiTest();
scopeApi = new OrganizationScopeApiTest();
protected organizations: Organization[] = [];
#organizations: Organization[] = [];
get organizations(): Organization[] {
return this.#organizations;
}
override async create(data: { name: string; description?: string }): Promise<Organization> {
const created = await super.create(data);
@ -89,7 +101,7 @@ export class OrganizationApiTest extends OrganizationApi {
// Use `trySafe` to avoid error when organization is deleted by other tests.
this.organizations.map(async (organization) => trySafe(this.delete(organization.id)))
);
this.organizations = [];
this.#organizations = [];
}
}
/* eslint-enable @silverhand/fp/no-mutating-methods */

View file

@ -51,7 +51,11 @@ export const generateNewUser = async <T extends NewUserProfileOptions>(options:
};
export class UserApiTest {
protected users: User[] = [];
#users: User[] = [];
get users(): User[] {
return this.#users;
}
async create(data: CreateUserPayload): Promise<User> {
const user = await createUser(data);
@ -67,6 +71,6 @@ export class UserApiTest {
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 = [];
this.#users = [];
}
}

View file

@ -1,9 +1,12 @@
import type { IncomingHttpHeaders } from 'node:http';
import type { User } from '@logto/schemas';
import type { Role, User } from '@logto/schemas';
import { authedAdminApi, deleteUser } from '#src/api/index.js';
import { assignRolesToUser, authedAdminApi, createUser, deleteUser } from '#src/api/index.js';
import { createRole, deleteRole } from '#src/api/role.js';
import { createUserByAdmin, expectRejects } from '#src/helpers/index.js';
import { OrganizationApiTest } from '#src/helpers/organization.js';
import { UserApiTest } from '#src/helpers/user.js';
const getUsers = async <T>(
init: string[][] | Record<string, string> | URLSearchParams
@ -235,3 +238,134 @@ describe('admin console user search params', () => {
]);
});
});
describe('admin console user search params - excludeRoleId', () => {
const users: User[] = [];
const roles: Role[] = [];
const userPrefix = `search_exclude_role_`;
const rolePrefix = `role_`;
beforeAll(async () => {
// Create users with different roles
// eslint-disable-next-line @silverhand/fp/no-mutating-methods
users.push(
...(await Promise.all([
createUser({ username: userPrefix + '1' }),
createUser({ username: userPrefix + '2' }),
createUser({ username: userPrefix + '3' }),
]))
);
// eslint-disable-next-line @silverhand/fp/no-mutating-methods
roles.push(
...(await Promise.all([
createRole({ name: rolePrefix + '1' }),
createRole({ name: rolePrefix + '2' }),
createRole({ name: rolePrefix + '3' }),
]))
);
// Assign roles to users
await Promise.all([
assignRolesToUser(users[0]!.id, [roles[0]!.id, roles[1]!.id]),
assignRolesToUser(users[1]!.id, [roles[1]!.id, roles[2]!.id]),
assignRolesToUser(users[2]!.id, [roles[2]!.id]),
]);
});
afterAll(async () => {
await Promise.all(users.map(async ({ id }) => deleteUser(id)));
await Promise.all(roles.map(async ({ id }) => deleteRole(id)));
});
it('should be able to exclude users with a specific role (1)', async () => {
const { headers, json } = await getUsers<User[]>([
['search.username', userPrefix + '%'],
['excludeRoleId', roles[0]!.id],
]);
expect(headers['total-number']).toEqual('2');
expect(json).toHaveLength(2);
expect(json).toContainEqual(expect.objectContaining({ id: users[1]!.id }));
expect(json).toContainEqual(expect.objectContaining({ id: users[2]!.id }));
});
it('should be able to exclude users with a specific role (2)', async () => {
const { headers, json } = await getUsers<User[]>([
['search.username', userPrefix + '%'],
['excludeRoleId', roles[1]!.id],
]);
expect(headers['total-number']).toEqual('1');
expect(json).toHaveLength(1);
expect(json).toContainEqual(expect.objectContaining({ id: users[2]!.id }));
});
});
describe('admin console user search params - excludeOrganizationId', () => {
const organizationApi = new OrganizationApiTest();
const userApi = new UserApiTest();
const organizationPrefix = `search_exclude_organization_`;
const userPrefix = `search_exclude_organization_`;
beforeAll(async () => {
await Promise.all([
organizationApi.create({ name: organizationPrefix + '1' }),
organizationApi.create({ name: organizationPrefix + '2' }),
organizationApi.create({ name: organizationPrefix + '3' }),
]);
await Promise.all([
userApi.create({ username: userPrefix + '1' }),
userApi.create({ username: userPrefix + '2' }),
userApi.create({ username: userPrefix + '3' }),
]);
const { organizations } = organizationApi;
const { users } = userApi;
await Promise.all([
organizationApi.addUsers(organizations[0]!.id, [users[0]!.id, users[1]!.id]),
organizationApi.addUsers(organizations[1]!.id, [users[1]!.id, users[2]!.id]),
organizationApi.addUsers(organizations[2]!.id, [users[2]!.id]),
]);
});
afterAll(async () => {
await organizationApi.cleanUp();
await userApi.cleanUp();
});
it('should be able to exclude users with a specific organization (1)', async () => {
const { headers, json } = await getUsers<User[]>([
['search.username', userPrefix + '%'],
['excludeOrganizationId', organizationApi.organizations[0]!.id],
]);
expect(headers['total-number']).toEqual('1');
expect(json).toHaveLength(1);
expect(json).toContainEqual(expect.objectContaining({ id: userApi.users[2]!.id }));
});
it('should be able to exclude users with a specific organization (2)', async () => {
const { headers, json } = await getUsers<User[]>([
['search.username', userPrefix + '%'],
['excludeOrganizationId', organizationApi.organizations[1]!.id],
]);
expect(headers['total-number']).toEqual('1');
expect(json).toHaveLength(1);
expect(json).toContainEqual(expect.objectContaining({ id: userApi.users[0]!.id }));
});
it('should be able to exclude users with a specific organization (3)', async () => {
const { headers, json } = await getUsers<User[]>([
['search.username', userPrefix + '%'],
['excludeOrganizationId', organizationApi.organizations[2]!.id],
]);
expect(headers['total-number']).toEqual('2');
expect(json).toHaveLength(2);
expect(json).toContainEqual(expect.objectContaining({ id: userApi.users[0]!.id }));
expect(json).toContainEqual(expect.objectContaining({ id: userApi.users[1]!.id }));
});
});

View file

@ -0,0 +1,231 @@
import assert from 'node:assert';
import { HTTPError } from 'got';
import { OrganizationApiTest } from '#src/helpers/organization.js';
import { UserApiTest } from '#src/helpers/user.js';
import { generateTestName } from '#src/utils.js';
describe('organization user APIs', () => {
describe('organization get users', () => {
const organizationApi = new OrganizationApiTest();
const userApi = new UserApiTest();
beforeAll(async () => {
const organization = await organizationApi.create({ name: 'test' });
const createdUsers = await Promise.all(
Array.from({ length: 30 }).map(async () => userApi.create({ username: generateTestName() }))
);
await organizationApi.addUsers(
organization.id,
createdUsers.map((user) => user.id)
);
});
afterAll(async () => {
await Promise.all([organizationApi.cleanUp(), userApi.cleanUp()]);
});
it('should be able to get organization users with pagination', async () => {
const organizationId = organizationApi.organizations[0]!.id;
const [users1, total1] = await organizationApi.getUsers(organizationId, {
page: 1,
page_size: 20,
});
const [users2, total2] = await organizationApi.getUsers(organizationId, {
page: 2,
page_size: 10,
});
expect(users2.length).toBeGreaterThanOrEqual(10);
expect(users2[0]?.id).not.toBeFalsy();
expect(users2[0]?.id).toBe(users1[10]?.id);
expect(total1).toBe(30);
expect(total2).toBe(30);
});
it('should be able to get organization users with search keyword', async () => {
const organizationId = organizationApi.organizations[0]!.id;
const username = generateTestName();
const createdUser = await userApi.create({ username });
await organizationApi.addUsers(organizationId, [createdUser.id]);
const [users] = await organizationApi.getUsers(organizationId, {
q: username,
});
expect(users).toHaveLength(1);
expect(users[0]).toMatchObject(createdUser);
});
it('should be able to get organization users with their roles', async () => {
const organizationId = organizationApi.organizations[0]!.id;
const user = userApi.users[0]!;
const roles = await Promise.all([
organizationApi.roleApi.create({ name: generateTestName() }),
organizationApi.roleApi.create({ name: generateTestName() }),
]);
const roleIds = roles.map(({ id }) => id);
await organizationApi.addUserRoles(organizationId, user.id, roleIds);
const [usersWithRoles] = await organizationApi.getUsers(organizationId, {
q: user.username!,
});
expect(usersWithRoles).toHaveLength(1);
expect(usersWithRoles[0]).toMatchObject(user);
expect(usersWithRoles[0]!.organizationRoles).toHaveLength(2);
expect(usersWithRoles[0]!.organizationRoles).toContainEqual(
expect.objectContaining({ id: roles[0].id })
);
expect(usersWithRoles[0]!.organizationRoles).toContainEqual(
expect.objectContaining({ id: roles[1].id })
);
});
});
describe('organization - user relations', () => {
const organizationApi = new OrganizationApiTest();
const userApi = new UserApiTest();
afterEach(async () => {
await Promise.all([organizationApi.cleanUp(), userApi.cleanUp()]);
});
it('should fail when try to add empty user list', async () => {
const organization = await organizationApi.create({ name: 'test' });
const response = await organizationApi
.addUsers(organization.id, [])
.catch((error: unknown) => error);
expect(response instanceof HTTPError && response.response.statusCode).toBe(400);
});
it('should fail when try to add user to an organization that does not exist', async () => {
const response = await organizationApi.addUsers('0', ['0']).catch((error: unknown) => error);
assert(response instanceof HTTPError);
expect(response.response.statusCode).toBe(422);
expect(JSON.parse(String(response.response.body))).toMatchObject(
expect.objectContaining({ code: 'entity.relation_foreign_key_not_found' })
);
});
it('should be able to delete organization user', async () => {
const organization = await organizationApi.create({ name: 'test' });
const user = await userApi.create({ username: generateTestName() });
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);
});
it('should fail when try to delete user from an organization that does not exist', async () => {
const response = await organizationApi.deleteUser('0', '0').catch((error: unknown) => error);
assert(response instanceof HTTPError);
expect(response.response.statusCode).toBe(404);
});
});
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 userApi.create({ username: generateTestName() });
const [role1, role2] = await Promise.all([
roleApi.create({ name: generateTestName() }),
roleApi.create({ name: generateTestName() }),
]);
const response = await organizationApi
.addUserRoles(organization.id, user.id, [role1.id, role2.id])
.catch((error: unknown) => error);
assert(response instanceof HTTPError);
expect(response.response.statusCode).toBe(422);
expect(JSON.parse(String(response.response.body))).toMatchObject(
expect.objectContaining({ code: 'organization.require_membership' })
);
await organizationApi.addUsers(organization.id, [user.id]);
await organizationApi.addUserRoles(organization.id, user.id, [role1.id, role2.id]);
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 }));
});
it('should be able to get all organizations with roles for a user', async () => {
const [organization1, organization2] = await Promise.all([
organizationApi.create({ name: 'test' }),
organizationApi.create({ name: 'test' }),
]);
const user = await userApi.create({ username: generateTestName() });
const [role1, role2] = await Promise.all([
roleApi.create({ name: generateTestName() }),
roleApi.create({ name: generateTestName() }),
]);
await organizationApi.addUsers(organization1.id, [user.id]);
await organizationApi.addUserRoles(organization1.id, user.id, [role1.id]);
await organizationApi.addUsers(organization2.id, [user.id]);
await organizationApi.addUserRoles(organization2.id, user.id, [role1.id, role2.id]);
const organizations = await organizationApi.getUserOrganizations(user.id);
// Check organization 1 and ensure it only has role 1
const organization1WithRoles = organizations.find((org) => org.id === organization1.id);
assert(organization1WithRoles);
expect(organization1WithRoles.id).toBe(organization1.id);
expect(organization1WithRoles.organizationRoles).toContainEqual(
expect.objectContaining({ id: role1.id })
);
expect(organization1WithRoles.organizationRoles).not.toContainEqual(
expect.objectContaining({ id: role2.id })
);
// Check organization 2 and ensure it has both role 1 and role 2
const organization2WithRoles = organizations.find((org) => org.id === organization2.id);
assert(organization2WithRoles);
expect(organization2WithRoles.id).toBe(organization2.id);
expect(organization2WithRoles.organizationRoles).toContainEqual(
expect.objectContaining({ id: role1.id })
);
expect(organization2WithRoles.organizationRoles).toContainEqual(
expect.objectContaining({ id: role2.id })
);
});
it('should be able to assign multiple roles to multiple users', async () => {
const organization = await organizationApi.create({ name: 'test' });
const [user1, user2] = await Promise.all([
userApi.create({ username: generateTestName() }),
userApi.create({ username: generateTestName() }),
]);
const [role1, role2] = await Promise.all([
roleApi.create({ name: generateTestName() }),
roleApi.create({ name: generateTestName() }),
]);
await organizationApi.addUsers(organization.id, [user1.id, user2.id]);
await organizationApi.addUsersRoles(
organization.id,
[user1.id, user2.id],
[role1.id, role2.id]
);
const [user1Roles, user2Roles] = await Promise.all([
organizationApi.getUserRoles(organization.id, user1.id),
organizationApi.getUserRoles(organization.id, user2.id),
]);
expect(user1Roles).toContainEqual(expect.objectContaining({ id: role1.id }));
expect(user1Roles).toContainEqual(expect.objectContaining({ id: role2.id }));
expect(user2Roles).toContainEqual(expect.objectContaining({ id: role1.id }));
expect(user2Roles).toContainEqual(expect.objectContaining({ id: role2.id }));
});
});
});

View file

@ -1,10 +1,7 @@
import assert from 'node:assert';
import { generateStandardId } from '@logto/shared';
import { HTTPError } from 'got';
import { OrganizationApiTest } from '#src/helpers/organization.js';
import { UserApiTest } from '#src/helpers/user.js';
const randomId = () => generateStandardId(4);
@ -90,135 +87,4 @@ describe('organization APIs', () => {
expect(response instanceof HTTPError && response.response.statusCode).toBe(404);
});
});
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([
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 }));
});
it('should fail when try to add empty user list', async () => {
const organization = await organizationApi.create({ name: 'test' });
const response = await organizationApi
.addUsers(organization.id, [])
.catch((error: unknown) => error);
expect(response instanceof HTTPError && response.response.statusCode).toBe(400);
});
it('should fail when try to add user to an organization that does not exist', async () => {
const response = await organizationApi.addUsers('0', ['0']).catch((error: unknown) => error);
assert(response instanceof HTTPError);
expect(response.response.statusCode).toBe(422);
expect(JSON.parse(String(response.response.body))).toMatchObject(
expect.objectContaining({ code: 'entity.relation_foreign_key_not_found' })
);
});
it('should be able to delete organization user', async () => {
const organization = await organizationApi.create({ name: 'test' });
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);
});
it('should fail when try to delete user from an organization that does not exist', async () => {
const response = await organizationApi.deleteUser('0', '0').catch((error: unknown) => error);
assert(response instanceof HTTPError);
expect(response.response.statusCode).toBe(404);
});
});
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 userApi.create({ username: 'test' + randomId() });
const [role1, role2] = await Promise.all([
roleApi.create({ name: 'test' + randomId() }),
roleApi.create({ name: 'test' + randomId() }),
]);
const response = await organizationApi
.addUserRoles(organization.id, user.id, [role1.id, role2.id])
.catch((error: unknown) => error);
assert(response instanceof HTTPError);
expect(response.response.statusCode).toBe(422);
expect(JSON.parse(String(response.response.body))).toMatchObject(
expect.objectContaining({ code: 'organization.require_membership' })
);
await organizationApi.addUsers(organization.id, [user.id]);
await organizationApi.addUserRoles(organization.id, user.id, [role1.id, role2.id]);
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 }));
});
it('should be able to get all organizations with roles for a user', async () => {
const [organization1, organization2] = await Promise.all([
organizationApi.create({ name: 'test' }),
organizationApi.create({ name: 'test' }),
]);
const user = await userApi.create({ username: 'test' + randomId() });
const [role1, role2] = await Promise.all([
roleApi.create({ name: 'test' + randomId() }),
roleApi.create({ name: 'test' + randomId() }),
]);
await organizationApi.addUsers(organization1.id, [user.id]);
await organizationApi.addUserRoles(organization1.id, user.id, [role1.id]);
await organizationApi.addUsers(organization2.id, [user.id]);
await organizationApi.addUserRoles(organization2.id, user.id, [role1.id, role2.id]);
const organizations = await organizationApi.getUserOrganizations(user.id);
// Check organization 1 and ensure it only has role 1
const organization1WithRoles = organizations.find((org) => org.id === organization1.id);
assert(organization1WithRoles);
expect(organization1WithRoles.id).toBe(organization1.id);
expect(organization1WithRoles.organizationRoles).toContainEqual(
expect.objectContaining({ id: role1.id })
);
expect(organization1WithRoles.organizationRoles).not.toContainEqual(
expect.objectContaining({ id: role2.id })
);
// Check organization 2 and ensure it has both role 1 and role 2
const organization2WithRoles = organizations.find((org) => org.id === organization2.id);
assert(organization2WithRoles);
expect(organization2WithRoles.id).toBe(organization2.id);
expect(organization2WithRoles.organizationRoles).toContainEqual(
expect.objectContaining({ id: role1.id })
);
expect(organization2WithRoles.organizationRoles).toContainEqual(
expect.objectContaining({ id: role2.id })
);
});
});
});

View file

@ -1,6 +1,7 @@
import crypto from 'node:crypto';
import path from 'node:path';
import { generateStandardId } from '@logto/shared';
import { assert } from '@silverhand/essentials';
import { type Page } from 'puppeteer';
@ -105,3 +106,13 @@ export const cls = <C extends string>(className: C) => `[class*=_${className}]`
* @see {@link cls}
*/
export const dcls = <C extends string>(className: C) => `div${cls(className)}` as const;
/**
* Generate a random test name that starts with `test_` and followed by 4 random characters.
*
* @example
* ```ts
* generateTestName() // => 'test_abc1'
* ```
*/
export const generateTestName = () => `test_${generateStandardId(4)}`;