0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2024-12-16 20:26:19 -05:00

test: add integration tests for roles (#2900)

This commit is contained in:
wangsijie 2023-01-11 16:36:31 +08:00 committed by GitHub
parent 375a8f4842
commit 175516a711
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 259 additions and 9 deletions

View file

@ -132,7 +132,7 @@ describe('role routes', () => {
const response = await roleRequester
.patch(`/roles/${mockRole.id}`)
.send({ name: mockRole.name });
expect(response.status).toEqual(400);
expect(response.status).toEqual(422);
});
});
@ -158,11 +158,12 @@ describe('role routes', () => {
it('POST /roles/:id/scopes', async () => {
findRoleById.mockResolvedValueOnce(mockRole);
findRolesScopesByRoleId.mockResolvedValueOnce([]);
findRolesScopesByRoleId.mockResolvedValue([]);
findScopesByIds.mockResolvedValueOnce([]);
const response = await roleRequester.post(`/roles/${mockRole.id}/scopes`).send({
scopeIds: [mockScope.id],
});
expect(response.status).toEqual(201);
expect(response.status).toEqual(200);
expect(insertRolesScopes).toHaveBeenCalledWith([
{ roleId: mockRole.id, scopeId: mockScope.id },
]);
@ -181,7 +182,7 @@ describe('role routes', () => {
findUsersByIds.mockResolvedValueOnce([mockUser]);
const response = await roleRequester.get(`/roles/${mockRole.id}/users`);
expect(response.status).toEqual(200);
expect(response.body).toEqual([mockUser]);
expect(response.body[0]).toHaveProperty('id', mockUser.id);
});
it('POST /roles/:id/users', async () => {

View file

@ -1,7 +1,8 @@
import { buildIdGenerator } from '@logto/core-kit';
import type { RoleResponse, ScopeResponse } from '@logto/schemas';
import { Roles } from '@logto/schemas';
import { userInfoSelectFields, Roles } from '@logto/schemas';
import { tryThat } from '@logto/shared';
import { pick } from '@silverhand/essentials';
import { object, string, z } from 'zod';
import RequestError from '#src/errors/RequestError/index.js';
@ -107,6 +108,7 @@ export default function roleRoutes<T extends AuthedRouter>(...[router]: RouterIn
new RequestError({
code: 'role.name_in_use',
name: roleBody.name,
status: 422,
})
);
@ -156,7 +158,14 @@ export default function roleRoutes<T extends AuthedRouter>(...[router]: RouterIn
} = ctx.guard;
await findRoleById(id);
assertThat(!name || !(await findRoleByRoleName(name, id)), 'role.name_in_use');
assertThat(
!name || !(await findRoleByRoleName(name, id)),
new RequestError({
code: 'role.name_in_use',
name,
status: 422,
})
);
ctx.body = await updateRoleById(id, body);
return next();
@ -238,7 +247,10 @@ export default function roleRoutes<T extends AuthedRouter>(...[router]: RouterIn
await Promise.all(scopeIds.map(async (scopeId) => findScopeById(scopeId)));
await insertRolesScopes(scopeIds.map((scopeId) => ({ roleId: id, scopeId })));
ctx.status = 201;
const newRolesScopes = await findRolesScopesByRoleId(id);
const scopes = await findScopesByIds(newRolesScopes.map(({ scopeId }) => scopeId));
ctx.body = scopes;
return next();
}
@ -272,7 +284,8 @@ export default function roleRoutes<T extends AuthedRouter>(...[router]: RouterIn
await findRoleById(id);
const usersRoles = await findUsersRolesByRoleId(id);
ctx.body = await findUsersByIds(usersRoles.map(({ userId }) => userId));
const users = await findUsersByIds(usersRoles.map(({ userId }) => userId));
ctx.body = users.map((user) => pick(user, ...userInfoSelectFields));
return next();
}

View file

@ -0,0 +1,55 @@
import type { CreateRole, Role, Scope, User } from '@logto/schemas';
import { generateRoleName } from '#src/utils.js';
import { authedAdminApi } from './api.js';
export const createRole = (name?: string, description?: string, scopeIds?: string[]) =>
authedAdminApi
.post('roles', {
json: {
name: name ?? generateRoleName(),
description: description ?? generateRoleName(),
scopeIds,
},
})
.json<Role>();
export const getRoles = () => authedAdminApi.get('roles').json<Role[]>();
export const getRole = (roleId: string) => authedAdminApi.get(`roles/${roleId}`).json<Role>();
export const updateRole = (roleId: string, payload: Partial<Omit<CreateRole, 'id'>>) =>
authedAdminApi
.patch(`roles/${roleId}`, {
json: {
...payload,
},
})
.json<Role>();
export const deleteRole = (roleId: string) => authedAdminApi.delete(`roles/${roleId}`);
export const getRoleScopes = (roleId: string) =>
authedAdminApi.get(`roles/${roleId}/scopes`).json<Scope[]>();
export const assignScopesToRole = (scopeIds: string[], roleId: string) =>
authedAdminApi
.post(`roles/${roleId}/scopes`, {
json: { scopeIds },
})
.json<Scope[]>();
export const deleteScopeFromRole = (scopeId: string, roleId: string) =>
authedAdminApi.delete(`roles/${roleId}/scopes/${scopeId}`);
export const getRoleUsers = (roleId: string) =>
authedAdminApi.get(`roles/${roleId}/users`).json<User[]>();
export const assignUsersToRole = (userIds: string[], roleId: string) =>
authedAdminApi.post(`roles/${roleId}/users`, {
json: { userIds },
});
export const deleteUserFromRole = (userId: string, roleId: string) =>
authedAdminApi.delete(`roles/${roleId}/users/${userId}`);

View file

@ -5,7 +5,7 @@ import { createResource } from '#src/api/index.js';
import { createScope, deleteScope, getScopes, updateScope } from '#src/api/scope.js';
import { generateScopeName } from '#src/utils.js';
describe('admin console api resources', () => {
describe('scopes', () => {
it('should get management api resource scopes successfully', async () => {
const scopes = await getScopes(managementResource.id);

View file

@ -0,0 +1,46 @@
import { createResource } from '#src/api/index.js';
import {
assignScopesToRole,
createRole,
deleteScopeFromRole,
getRoleScopes,
} from '#src/api/role.js';
import { createScope } from '#src/api/scope.js';
describe('roles scopes', () => {
it('should get role scopes successfully', async () => {
const role = await createRole();
const resource = await createResource();
const scope = await createScope(resource.id);
await assignScopesToRole([scope.id], role.id);
const scopes = await getRoleScopes(role.id);
expect(scopes.length).toBe(1);
expect(scopes[0]).toHaveProperty('id', scope.id);
expect(scopes[0]).toHaveProperty('resource.id', resource.id);
});
it('should assign scopes to role successfully', async () => {
const role = await createRole();
const resource = await createResource();
const scope1 = await createScope(resource.id);
const scope2 = await createScope(resource.id);
const scopes = await assignScopesToRole([scope1.id, scope2.id], role.id);
expect(scopes.length).toBe(2);
});
it('should remove scope from role successfully', async () => {
const role = await createRole();
const resource = await createResource();
const scope = await createScope(resource.id);
await assignScopesToRole([scope.id], role.id);
const scopes = await getRoleScopes(role.id);
expect(scopes.length).toBe(1);
await deleteScopeFromRole(scope.id, role.id);
const newScopes = await getRoleScopes(role.id);
expect(newScopes.length).toBe(0);
});
});

View file

@ -0,0 +1,96 @@
import { HTTPError } from 'got';
import { createResource } from '#src/api/resource.js';
import {
createRole,
deleteRole,
getRole,
getRoles,
getRoleScopes,
updateRole,
} from '#src/api/role.js';
import { createScope } from '#src/api/scope.js';
import { generateRoleName } from '#src/utils.js';
describe('roles', () => {
it('should get roles list successfully', async () => {
await createRole();
const roles = await getRoles();
expect(roles.length > 0).toBeTruthy();
});
it('should create role successfully', async () => {
const roleName = generateRoleName();
const description = roleName;
const role = await createRole(roleName, description);
expect(role.name).toBe(roleName);
expect(role.description).toBe(description);
});
it('should create role with scopeIds successfully', async () => {
const roleName = generateRoleName();
const description = roleName;
const resource = await createResource();
const scope = await createScope(resource.id);
const role = await createRole(roleName, description, [scope.id]);
const scopes = await getRoleScopes(role.id);
expect(role.name).toBe(roleName);
expect(role.description).toBe(description);
expect(scopes[0]).toHaveProperty('id', scope.id);
});
it('should fail when create role with conflict name', async () => {
const createdRole = await createRole();
const response = await createRole(createdRole.name).catch((error: unknown) => error);
expect(response instanceof HTTPError && response.response.statusCode === 422).toBe(true);
});
it('should get role detail successfully', async () => {
const createdRole = await createRole();
const role = await getRole(createdRole.id);
expect(role.name).toBe(createdRole.name);
expect(role.description).toBe(createdRole.description);
});
it('should update role details successfully', async () => {
const role = await createRole();
const newName = `new_${role.name}`;
const newDescription = `new_${role.description}`;
await updateRole(role.id, {
name: newName,
description: newDescription,
});
const updatedRole = await getRole(role.id);
expect(updatedRole.name).toBe(newName);
expect(updatedRole.description).toBe(newDescription);
});
it('should fail when update role with conflict name', async () => {
const role1 = await createRole();
const role2 = await createRole();
const response = await updateRole(role2.id, {
name: role1.name,
}).catch((error: unknown) => error);
expect(response instanceof HTTPError && response.response.statusCode === 422).toBe(true);
});
it('should delete role successfully', async () => {
const role = await createRole();
await deleteRole(role.id);
const response = await getRole(role.id).catch((error: unknown) => error);
expect(response instanceof HTTPError && response.response.statusCode === 404).toBe(true);
});
});

View file

@ -0,0 +1,38 @@
import { createUser } from '#src/api/index.js';
import { assignUsersToRole, createRole, deleteUserFromRole, getRoleUsers } from '#src/api/role.js';
import { generateNewUserProfile } from '#src/helpers/user.js';
describe('roles users', () => {
it('should get role users successfully', async () => {
const role = await createRole();
const user = await createUser(generateNewUserProfile({}));
await assignUsersToRole([user.id], role.id);
const users = await getRoleUsers(role.id);
expect(users.length).toBe(1);
expect(users[0]).toHaveProperty('id', user.id);
});
it('should assign users to role successfully', async () => {
const role = await createRole();
const user1 = await createUser(generateNewUserProfile({}));
const user2 = await createUser(generateNewUserProfile({}));
await assignUsersToRole([user1.id, user2.id], role.id);
const users = await getRoleUsers(role.id);
expect(users.length).toBe(2);
});
it('should remove user from role successfully', async () => {
const role = await createRole();
const user = await createUser(generateNewUserProfile({}));
await assignUsersToRole([user.id], role.id);
const users = await getRoleUsers(role.id);
expect(users.length).toBe(1);
await deleteUserFromRole(user.id, role.id);
const newUsers = await getRoleUsers(role.id);
expect(newUsers.length).toBe(0);
});
});

View file

@ -7,6 +7,7 @@ export const generateResourceName = () => `res_${crypto.randomUUID()}`;
export const generateResourceIndicator = () => `https://${crypto.randomUUID()}.logto.io`;
export const generateEmail = () => `${crypto.randomUUID().toLowerCase()}@logto.io`;
export const generateScopeName = () => `sc:${crypto.randomUUID()}`;
export const generateRoleName = () => `role_${crypto.randomUUID()}`;
export const generatePhone = () => {
const array = new Uint32Array(1);