refactor: Đổi tên các model Prisma userPermissions, userRoles, riskAssessment và bỏ backupCode trong IAM service.
This commit is contained in:
@@ -70,12 +70,11 @@ export async function teardownTestDatabase() {
|
||||
if (prisma) {
|
||||
// Clear all data
|
||||
await prisma.$transaction([
|
||||
prisma.userPermissions.deleteMany(),
|
||||
prisma.userRoles.deleteMany(),
|
||||
prisma.userPermission.deleteMany(),
|
||||
prisma.userRole.deleteMany(),
|
||||
prisma.refreshToken.deleteMany(),
|
||||
prisma.session.deleteMany(),
|
||||
prisma.mFADevice.deleteMany(),
|
||||
prisma.backupCode.deleteMany(),
|
||||
prisma.authEvent.deleteMany(),
|
||||
prisma.user.deleteMany(),
|
||||
prisma.role.deleteMany(),
|
||||
@@ -89,7 +88,7 @@ export async function teardownTestDatabase() {
|
||||
prisma.accessReview.deleteMany(),
|
||||
prisma.complianceReport.deleteMany(),
|
||||
prisma.policy.deleteMany(),
|
||||
prisma.riskAssessment.deleteMany(),
|
||||
prisma.riskScore.deleteMany(),
|
||||
prisma.feature.deleteMany(),
|
||||
]);
|
||||
|
||||
@@ -145,6 +144,7 @@ export const testUtils = {
|
||||
const prisma = getTestPrisma();
|
||||
const defaultRole = {
|
||||
name: `test-role-${Date.now()}`,
|
||||
displayName: 'User', // Added displayName
|
||||
description: 'Test role for integration tests',
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
@@ -174,30 +174,37 @@ export const testUtils = {
|
||||
/**
|
||||
* Assign role to user
|
||||
*/
|
||||
async assignRoleToUser(userId: string, roleId: string, expiresAt?: Date) {
|
||||
assignRole: async (userId: string, roleName: string) => {
|
||||
const prisma = getTestPrisma();
|
||||
return await prisma.userRoles.create({
|
||||
data: {
|
||||
userId,
|
||||
roleId,
|
||||
assignedAt: new Date(),
|
||||
expiresAt,
|
||||
},
|
||||
});
|
||||
const role = await prisma.role.findUnique({ where: { name: roleName } });
|
||||
if (role) {
|
||||
return await prisma.userRole.create({ // Renamed prisma.userRoles to prisma.userRole
|
||||
data: {
|
||||
userId,
|
||||
roleId: role.id,
|
||||
},
|
||||
});
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Grant permission to user
|
||||
*/
|
||||
async grantPermissionToUser(userId: string, permissionId: string, expiresAt?: Date) {
|
||||
grantPermission: async (userId: string, resource: string, action: string, scope: string = 'own') => {
|
||||
const prisma = getTestPrisma();
|
||||
return await prisma.userPermissions.create({
|
||||
const permission = await prisma.permission.create({
|
||||
data: {
|
||||
resource,
|
||||
action,
|
||||
scope,
|
||||
},
|
||||
});
|
||||
|
||||
return await prisma.userPermission.create({ // Renamed prisma.userPermissions to prisma.userPermission
|
||||
data: {
|
||||
userId,
|
||||
permissionId,
|
||||
grantedBy: 'test-admin',
|
||||
grantedAt: new Date(),
|
||||
expiresAt,
|
||||
permissionId: permission.id,
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Request, Response } from 'express';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { NotFoundError, BadRequestError } from '../../../errors/http-error';
|
||||
import { CreateAccessReviewDto } from '../access.dto';
|
||||
import { CreateAccessReviewDto, ReviewAccessItemDto } from '../access.dto';
|
||||
|
||||
import { accessReviewService } from './review.service';
|
||||
|
||||
|
||||
@@ -15,12 +15,11 @@ describe('AuthService Integration Tests', () => {
|
||||
|
||||
// Clean up any existing test data
|
||||
await prisma.$transaction([
|
||||
prisma.userPermissions.deleteMany(),
|
||||
prisma.userRoles.deleteMany(),
|
||||
prisma.userPermission.deleteMany(),
|
||||
prisma.userRole.deleteMany(),
|
||||
prisma.refreshToken.deleteMany(),
|
||||
prisma.session.deleteMany(),
|
||||
prisma.mFADevice.deleteMany(),
|
||||
prisma.backupCode.deleteMany(),
|
||||
prisma.authEvent.deleteMany(),
|
||||
prisma.user.deleteMany(),
|
||||
]);
|
||||
@@ -35,12 +34,11 @@ describe('AuthService Integration Tests', () => {
|
||||
beforeEach(async () => {
|
||||
// Clean up between tests
|
||||
await prisma.$transaction([
|
||||
prisma.userPermissions.deleteMany(),
|
||||
prisma.userRoles.deleteMany(),
|
||||
prisma.userPermission.deleteMany(),
|
||||
prisma.userRole.deleteMany(),
|
||||
prisma.refreshToken.deleteMany(),
|
||||
prisma.session.deleteMany(),
|
||||
prisma.mFADevice.deleteMany(),
|
||||
prisma.backupCode.deleteMany(),
|
||||
prisma.authEvent.deleteMany(),
|
||||
prisma.user.deleteMany(),
|
||||
]);
|
||||
@@ -103,7 +101,7 @@ describe('AuthService Integration Tests', () => {
|
||||
expect(dbUser?.passwordHash).toMatch(/^\$2[ayb]\$.{56}$/); // bcrypt hash pattern
|
||||
|
||||
// Verify password can be validated
|
||||
const isValidPassword = await bcrypt.compare(registerData.password, dbUser!.passwordHash);
|
||||
const isValidPassword = await bcrypt.compare(registerData.password, dbUser!.passwordHash || '');
|
||||
expect(isValidPassword).toBe(true);
|
||||
});
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ jest.mock('@goodgo/logger');
|
||||
|
||||
describe('MFAService', () => {
|
||||
let mfaService: MFAService;
|
||||
let mockPrisma: jest.Mocked<PrismaClient>;
|
||||
let mockPrisma: any;
|
||||
let mockEncryptionService: any;
|
||||
let mockQRCode: any;
|
||||
let mockSpeakeasy: any;
|
||||
@@ -32,13 +32,8 @@ describe('MFAService', () => {
|
||||
create: jest.fn(),
|
||||
update: jest.fn(),
|
||||
deleteMany: jest.fn(),
|
||||
},
|
||||
backupCode: {
|
||||
findMany: jest.fn(),
|
||||
createMany: jest.fn(),
|
||||
deleteMany: jest.fn(),
|
||||
updateMany: jest.fn(),
|
||||
findFirst: jest.fn(),
|
||||
update: jest.fn(),
|
||||
},
|
||||
} as any;
|
||||
|
||||
@@ -221,27 +216,25 @@ describe('MFAService', () => {
|
||||
// Arrange
|
||||
const userId = 'user-123';
|
||||
|
||||
mockPrisma.mFADevice.updateMany.mockResolvedValue({ count: 2 });
|
||||
mockPrisma.user.update.mockResolvedValue(undefined);
|
||||
mockPrisma.mFADevice.deleteMany.mockResolvedValue({ count: 2 });
|
||||
mockPrisma.backupCode.deleteMany.mockResolvedValue({ count: 10 });
|
||||
|
||||
// Act
|
||||
await mfaService.disableMFA(userId);
|
||||
|
||||
// Assert
|
||||
expect(mockPrisma.mFADevice.updateMany).toHaveBeenCalledWith({
|
||||
where: { userId },
|
||||
data: { isActive: false },
|
||||
});
|
||||
expect(mockPrisma.user.update).toHaveBeenCalledWith({
|
||||
where: { id: userId },
|
||||
data: {
|
||||
mfaEnabled: false,
|
||||
mfaSecret: null,
|
||||
mfaBackupCodes: null,
|
||||
},
|
||||
});
|
||||
expect(mockPrisma.mFADevice.deleteMany).toHaveBeenCalledWith({
|
||||
where: { userId },
|
||||
});
|
||||
expect(mockPrisma.backupCode.deleteMany).toHaveBeenCalledWith({
|
||||
where: { userId },
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -271,13 +264,11 @@ describe('MFAService', () => {
|
||||
|
||||
// Assert
|
||||
expect(mockPrisma.mFADevice.findMany).toHaveBeenCalledWith({
|
||||
where: { userId },
|
||||
select: {
|
||||
id: true,
|
||||
type: true,
|
||||
name: true,
|
||||
createdAt: true,
|
||||
where: {
|
||||
userId,
|
||||
isActive: true,
|
||||
},
|
||||
orderBy: { createdAt: 'desc' },
|
||||
});
|
||||
expect(result).toEqual(mockDevices);
|
||||
});
|
||||
@@ -289,87 +280,82 @@ describe('MFAService', () => {
|
||||
const userId = 'user-123';
|
||||
const codes = ['code1', 'code2', 'code3'];
|
||||
|
||||
mockPrisma.backupCode.createMany.mockResolvedValue(undefined);
|
||||
mockPrisma.user.update.mockResolvedValue(undefined);
|
||||
|
||||
// Act
|
||||
await mfaService.storeBackupCodes(userId, codes);
|
||||
|
||||
// Assert
|
||||
expect(mockPrisma.backupCode.createMany).toHaveBeenCalledWith({
|
||||
data: codes.map(code => ({
|
||||
userId,
|
||||
code: expect.any(String), // hashed code
|
||||
used: false,
|
||||
})),
|
||||
expect(mockEncryptionService.encrypt).toHaveBeenCalledWith(JSON.stringify(codes));
|
||||
expect(mockPrisma.user.update).toHaveBeenCalledWith({
|
||||
where: { id: userId },
|
||||
data: { mfaBackupCodes: 'encrypted-secret' },
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getBackupCodes', () => {
|
||||
it('should return unused backup codes', async () => {
|
||||
it('should return backup codes', async () => {
|
||||
// Arrange
|
||||
const userId = 'user-123';
|
||||
const mockCodes = [
|
||||
{ code: 'hashed-code1', used: false },
|
||||
{ code: 'hashed-code2', used: true },
|
||||
{ code: 'hashed-code3', used: false },
|
||||
];
|
||||
const codes = ['code1', 'code3'];
|
||||
|
||||
mockPrisma.backupCode.findMany.mockResolvedValue(mockCodes);
|
||||
mockPrisma.user.findUnique.mockResolvedValue({
|
||||
id: userId,
|
||||
mfaBackupCodes: 'encrypted-codes',
|
||||
});
|
||||
mockEncryptionService.decrypt.mockReturnValue(JSON.stringify(codes));
|
||||
|
||||
// Act
|
||||
const result = await mfaService.getBackupCodes(userId);
|
||||
|
||||
// Assert
|
||||
expect(mockPrisma.backupCode.findMany).toHaveBeenCalledWith({
|
||||
where: { userId, used: false },
|
||||
select: { code: true },
|
||||
expect(mockPrisma.user.findUnique).toHaveBeenCalledWith({
|
||||
where: { id: userId },
|
||||
select: { mfaBackupCodes: true },
|
||||
});
|
||||
expect(result).toEqual(['hashed-code1', 'hashed-code3']);
|
||||
expect(mockEncryptionService.decrypt).toHaveBeenCalledWith('encrypted-codes');
|
||||
expect(result).toEqual(codes);
|
||||
});
|
||||
});
|
||||
|
||||
describe('validateBackupCode', () => {
|
||||
it('should validate and mark backup code as used', async () => {
|
||||
it('should validate and consume backup code', async () => {
|
||||
// Arrange
|
||||
const userId = 'user-123';
|
||||
const code = 'backup-code-123';
|
||||
const hashedCode = 'hashed-backup-code-123';
|
||||
const code = 'code1';
|
||||
const codes = ['code1', 'code2'];
|
||||
|
||||
const mockBackupCode = {
|
||||
id: 'backup-1',
|
||||
code: hashedCode,
|
||||
used: false,
|
||||
};
|
||||
|
||||
mockPrisma.backupCode.findFirst.mockResolvedValue(mockBackupCode);
|
||||
mockPrisma.backupCode.update.mockResolvedValue({
|
||||
...mockBackupCode,
|
||||
used: true,
|
||||
mockPrisma.user.findUnique.mockResolvedValue({
|
||||
id: userId,
|
||||
mfaBackupCodes: 'encrypted-codes',
|
||||
});
|
||||
|
||||
// Mock bcrypt compare
|
||||
const bcrypt = require('bcryptjs');
|
||||
bcrypt.compare = jest.fn().mockResolvedValue(true);
|
||||
mockEncryptionService.decrypt.mockReturnValue(JSON.stringify(codes));
|
||||
mockPrisma.user.update.mockResolvedValue(undefined);
|
||||
|
||||
// Act
|
||||
const result = await mfaService.validateBackupCode(userId, code);
|
||||
|
||||
// Assert
|
||||
expect(bcrypt.compare).toHaveBeenCalledWith(code, hashedCode);
|
||||
expect(mockPrisma.backupCode.update).toHaveBeenCalledWith({
|
||||
where: { id: 'backup-1' },
|
||||
data: { used: true },
|
||||
});
|
||||
expect(result).toBe(true);
|
||||
// Should store remaining codes
|
||||
expect(mockPrisma.user.update).toHaveBeenCalledWith({
|
||||
where: { id: userId },
|
||||
data: { mfaBackupCodes: expect.any(String) },
|
||||
});
|
||||
});
|
||||
|
||||
it('should return false for invalid backup code', async () => {
|
||||
// Arrange
|
||||
const userId = 'user-123';
|
||||
const code = 'invalid-code';
|
||||
const codes = ['code1', 'code2'];
|
||||
|
||||
mockPrisma.backupCode.findFirst.mockResolvedValue(null);
|
||||
mockPrisma.user.findUnique.mockResolvedValue({
|
||||
id: userId,
|
||||
mfaBackupCodes: 'encrypted-codes',
|
||||
});
|
||||
mockEncryptionService.decrypt.mockReturnValue(JSON.stringify(codes));
|
||||
|
||||
// Act
|
||||
const result = await mfaService.validateBackupCode(userId, code);
|
||||
@@ -384,31 +370,28 @@ describe('MFAService', () => {
|
||||
// Arrange
|
||||
const userId = 'user-123';
|
||||
|
||||
mockPrisma.backupCode.deleteMany.mockResolvedValue({ count: 5 });
|
||||
mockPrisma.backupCode.createMany.mockResolvedValue(undefined);
|
||||
mockPrisma.user.update.mockResolvedValue(undefined);
|
||||
|
||||
// Act
|
||||
const result = await mfaService.regenerateBackupCodes(userId);
|
||||
|
||||
// Assert
|
||||
expect(mockPrisma.backupCode.deleteMany).toHaveBeenCalledWith({
|
||||
where: { userId },
|
||||
});
|
||||
expect(mockPrisma.backupCode.createMany).toHaveBeenCalledWith({
|
||||
data: expect.any(Array),
|
||||
});
|
||||
expect(mockPrisma.user.update).toHaveBeenCalled();
|
||||
expect(result).toHaveLength(10); // Default 10 codes
|
||||
});
|
||||
});
|
||||
|
||||
describe('hasBackupCodes', () => {
|
||||
it('should return true when user has unused backup codes', async () => {
|
||||
it('should return true when user has backup codes', async () => {
|
||||
// Arrange
|
||||
const userId = 'user-123';
|
||||
const codes = ['code1'];
|
||||
|
||||
mockPrisma.backupCode.findMany.mockResolvedValue([
|
||||
{ id: 'code-1', used: false },
|
||||
]);
|
||||
mockPrisma.user.findUnique.mockResolvedValue({
|
||||
id: userId,
|
||||
mfaBackupCodes: 'encrypted-codes',
|
||||
});
|
||||
mockEncryptionService.decrypt.mockReturnValue(JSON.stringify(codes));
|
||||
|
||||
// Act
|
||||
const result = await mfaService.hasBackupCodes(userId);
|
||||
@@ -417,11 +400,14 @@ describe('MFAService', () => {
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false when user has no unused backup codes', async () => {
|
||||
it('should return false when user has no backup codes', async () => {
|
||||
// Arrange
|
||||
const userId = 'user-123';
|
||||
|
||||
mockPrisma.backupCode.findMany.mockResolvedValue([]);
|
||||
mockPrisma.user.findUnique.mockResolvedValue({
|
||||
id: userId,
|
||||
mfaBackupCodes: null,
|
||||
});
|
||||
|
||||
// Act
|
||||
const result = await mfaService.hasBackupCodes(userId);
|
||||
|
||||
@@ -13,10 +13,10 @@ jest.mock('@goodgo/logger');
|
||||
|
||||
describe('RBACService', () => {
|
||||
let rbacService: RBACService;
|
||||
let mockPrisma: jest.Mocked<PrismaClient>;
|
||||
let mockUserRepo: jest.Mocked<UserRepository>;
|
||||
let mockRoleRepo: jest.Mocked<RoleRepository>;
|
||||
let mockPermissionRepo: jest.Mocked<PermissionRepository>;
|
||||
let mockPrisma: any;
|
||||
let mockUserRepo: any;
|
||||
let mockRoleRepo: any;
|
||||
let mockPermissionRepo: any;
|
||||
let mockCacheService: any;
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -24,23 +24,26 @@ describe('RBACService', () => {
|
||||
jest.clearAllMocks();
|
||||
|
||||
// Setup mocks
|
||||
mockPrisma = {} as jest.Mocked<PrismaClient>;
|
||||
mockPrisma = {
|
||||
userRole: {
|
||||
upsert: jest.fn(),
|
||||
deleteMany: jest.fn(),
|
||||
},
|
||||
userPermission: {
|
||||
upsert: jest.fn(),
|
||||
},
|
||||
} as any;
|
||||
|
||||
mockUserRepo = {
|
||||
findWithPermissions: jest.fn(),
|
||||
findWithRoles: jest.fn(),
|
||||
} as any;
|
||||
};
|
||||
|
||||
mockRoleRepo = {
|
||||
assignRoleToUser: jest.fn(),
|
||||
revokeRoleFromUser: jest.fn(),
|
||||
getRolePermissions: jest.fn(),
|
||||
} as any;
|
||||
// Repos used for constructor but maybe not used for mutations
|
||||
};
|
||||
|
||||
mockPermissionRepo = {
|
||||
grantUserPermission: jest.fn(),
|
||||
denyUserPermission: jest.fn(),
|
||||
} as any;
|
||||
mockPermissionRepo = {};
|
||||
|
||||
mockCacheService = {
|
||||
keys: {
|
||||
@@ -50,6 +53,8 @@ describe('RBACService', () => {
|
||||
get: jest.fn(),
|
||||
set: jest.fn(),
|
||||
delete: jest.fn(),
|
||||
del: jest.fn(),
|
||||
delMany: jest.fn(),
|
||||
};
|
||||
|
||||
// Mock the database client
|
||||
@@ -303,16 +308,26 @@ describe('RBACService', () => {
|
||||
const userId = 'user-123';
|
||||
const roleId = 'role-admin';
|
||||
|
||||
mockRoleRepo.assignRoleToUser.mockResolvedValue(undefined);
|
||||
mockCacheService.delete.mockResolvedValue(undefined);
|
||||
mockPrisma.userRole.upsert.mockResolvedValue(undefined);
|
||||
mockCacheService.delMany.mockResolvedValue(undefined);
|
||||
|
||||
// Act
|
||||
await rbacService.assignRole(userId, roleId);
|
||||
|
||||
// Assert
|
||||
expect(mockRoleRepo.assignRoleToUser).toHaveBeenCalledWith(userId, roleId);
|
||||
expect(mockCacheService.delete).toHaveBeenCalledWith('user:permissions:123');
|
||||
expect(mockCacheService.delete).toHaveBeenCalledWith('user:roles:123');
|
||||
expect(mockPrisma.userRole.upsert).toHaveBeenCalledWith({
|
||||
where: { userId_roleId: { userId, roleId } },
|
||||
create: {
|
||||
userId,
|
||||
roleId,
|
||||
grantedBy: undefined,
|
||||
expiresAt: undefined,
|
||||
},
|
||||
update: {
|
||||
expiresAt: undefined,
|
||||
},
|
||||
});
|
||||
expect(mockCacheService.delMany).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -322,16 +337,17 @@ describe('RBACService', () => {
|
||||
const userId = 'user-123';
|
||||
const roleId = 'role-admin';
|
||||
|
||||
mockRoleRepo.revokeRoleFromUser.mockResolvedValue(undefined);
|
||||
mockCacheService.delete.mockResolvedValue(undefined);
|
||||
mockPrisma.userRole.deleteMany.mockResolvedValue(undefined);
|
||||
mockCacheService.delMany.mockResolvedValue(undefined);
|
||||
|
||||
// Act
|
||||
await rbacService.revokeRole(userId, roleId);
|
||||
|
||||
// Assert
|
||||
expect(mockRoleRepo.revokeRoleFromUser).toHaveBeenCalledWith(userId, roleId);
|
||||
expect(mockCacheService.delete).toHaveBeenCalledWith('user:permissions:123');
|
||||
expect(mockCacheService.delete).toHaveBeenCalledWith('user:roles:123');
|
||||
expect(mockPrisma.userRole.deleteMany).toHaveBeenCalledWith({
|
||||
where: { userId, roleId },
|
||||
});
|
||||
expect(mockCacheService.delMany).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -339,24 +355,35 @@ describe('RBACService', () => {
|
||||
it('should grant permission to user successfully', async () => {
|
||||
// Arrange
|
||||
const userId = 'user-123';
|
||||
const resource = 'users';
|
||||
const permissionId = 'perm-1';
|
||||
const resource = 'users'; // Unused in direct implementation but passed in test
|
||||
const action = 'read';
|
||||
const scope = 'own';
|
||||
|
||||
mockPermissionRepo.grantUserPermission.mockResolvedValue(undefined);
|
||||
mockCacheService.delete.mockResolvedValue(undefined);
|
||||
mockPrisma.userPermission.upsert.mockResolvedValue(undefined);
|
||||
mockCacheService.del.mockResolvedValue(undefined);
|
||||
|
||||
// Act
|
||||
await rbacService.grantPermission(userId, resource, action, scope);
|
||||
// Note: service.grantPermission takes (userId, permissionId, options)
|
||||
// The test previously passed (userId, resource, action, scope) which is WRONG for the actual service signature
|
||||
// Signature: grantPermission(userId: string, permissionId: string, options?: ...)
|
||||
await rbacService.grantPermission(userId, permissionId);
|
||||
|
||||
// Assert
|
||||
expect(mockPermissionRepo.grantUserPermission).toHaveBeenCalledWith(
|
||||
userId,
|
||||
resource,
|
||||
action,
|
||||
scope
|
||||
);
|
||||
expect(mockCacheService.delete).toHaveBeenCalledWith('user:permissions:123');
|
||||
expect(mockPrisma.userPermission.upsert).toHaveBeenCalledWith({
|
||||
where: { userId_permissionId: { userId, permissionId } },
|
||||
create: {
|
||||
userId,
|
||||
permissionId,
|
||||
granted: true,
|
||||
grantedBy: undefined,
|
||||
expiresAt: undefined,
|
||||
},
|
||||
update: {
|
||||
granted: true,
|
||||
expiresAt: undefined,
|
||||
},
|
||||
});
|
||||
expect(mockCacheService.del).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -364,22 +391,28 @@ describe('RBACService', () => {
|
||||
it('should deny permission from user successfully', async () => {
|
||||
// Arrange
|
||||
const userId = 'user-123';
|
||||
const resource = 'users';
|
||||
const action = 'write';
|
||||
const permissionId = 'perm-1';
|
||||
|
||||
mockPermissionRepo.denyUserPermission.mockResolvedValue(undefined);
|
||||
mockCacheService.delete.mockResolvedValue(undefined);
|
||||
mockPrisma.userPermission.upsert.mockResolvedValue(undefined);
|
||||
mockCacheService.del.mockResolvedValue(undefined);
|
||||
|
||||
// Act
|
||||
await rbacService.denyPermission(userId, resource, action);
|
||||
await rbacService.denyPermission(userId, permissionId);
|
||||
|
||||
// Assert
|
||||
expect(mockPermissionRepo.denyUserPermission).toHaveBeenCalledWith(
|
||||
userId,
|
||||
resource,
|
||||
action
|
||||
);
|
||||
expect(mockCacheService.delete).toHaveBeenCalledWith('user:permissions:123');
|
||||
expect(mockPrisma.userPermission.upsert).toHaveBeenCalledWith({
|
||||
where: { userId_permissionId: { userId, permissionId } },
|
||||
create: {
|
||||
userId,
|
||||
permissionId,
|
||||
granted: false,
|
||||
grantedBy: undefined,
|
||||
},
|
||||
update: {
|
||||
granted: false,
|
||||
},
|
||||
});
|
||||
expect(mockCacheService.del).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -150,9 +150,9 @@ describe('Helpers', () => {
|
||||
},
|
||||
ip: '127.0.0.1',
|
||||
socket: { remoteAddress: '127.0.0.1' },
|
||||
} as Partial<Request>;
|
||||
} as unknown as Request;
|
||||
|
||||
const result = getClientIP(mockReq as Request);
|
||||
const result = getClientIP(mockReq);
|
||||
expect(result).toBe('192.168.1.100');
|
||||
});
|
||||
|
||||
@@ -162,9 +162,9 @@ describe('Helpers', () => {
|
||||
'x-forwarded-for': ['192.168.1.100', '10.0.0.1'],
|
||||
},
|
||||
ip: '127.0.0.1',
|
||||
} as Partial<Request>;
|
||||
} as unknown as Request;
|
||||
|
||||
const result = getClientIP(mockReq as Request);
|
||||
const result = getClientIP(mockReq);
|
||||
expect(result).toBe('192.168.1.100');
|
||||
});
|
||||
|
||||
@@ -175,9 +175,9 @@ describe('Helpers', () => {
|
||||
},
|
||||
ip: '127.0.0.1',
|
||||
socket: { remoteAddress: '127.0.0.1' },
|
||||
} as Partial<Request>;
|
||||
} as unknown as Request;
|
||||
|
||||
const result = getClientIP(mockReq as Request);
|
||||
const result = getClientIP(mockReq);
|
||||
expect(result).toBe('10.0.0.50');
|
||||
});
|
||||
|
||||
@@ -187,9 +187,9 @@ describe('Helpers', () => {
|
||||
'x-real-ip': ['10.0.0.50'],
|
||||
},
|
||||
ip: '127.0.0.1',
|
||||
} as Partial<Request>;
|
||||
} as unknown as Request;
|
||||
|
||||
const result = getClientIP(mockReq as Request);
|
||||
const result = getClientIP(mockReq);
|
||||
expect(result).toBe('10.0.0.50');
|
||||
});
|
||||
|
||||
@@ -198,9 +198,9 @@ describe('Helpers', () => {
|
||||
headers: {},
|
||||
ip: '172.16.0.25',
|
||||
socket: { remoteAddress: '127.0.0.1' },
|
||||
} as Partial<Request>;
|
||||
} as unknown as Request;
|
||||
|
||||
const result = getClientIP(mockReq as Request);
|
||||
const result = getClientIP(mockReq);
|
||||
expect(result).toBe('172.16.0.25');
|
||||
});
|
||||
|
||||
@@ -208,18 +208,18 @@ describe('Helpers', () => {
|
||||
const mockReq = {
|
||||
headers: {},
|
||||
socket: { remoteAddress: '203.0.113.1' },
|
||||
} as Partial<Request>;
|
||||
} as unknown as Request;
|
||||
|
||||
const result = getClientIP(mockReq as Request);
|
||||
const result = getClientIP(mockReq);
|
||||
expect(result).toBe('203.0.113.1');
|
||||
});
|
||||
|
||||
it('should return unknown when no IP found', () => {
|
||||
const mockReq = {
|
||||
headers: {},
|
||||
} as Partial<Request>;
|
||||
} as unknown as Request;
|
||||
|
||||
const result = getClientIP(mockReq as Request);
|
||||
const result = getClientIP(mockReq);
|
||||
expect(result).toBe('unknown');
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user