import * as bcrypt from 'bcryptjs'; import { AuthService } from './auth.service'; import { expectAppError } from '../../testing/prisma-mock'; describe('AuthService player login', () => { const prisma = { user: { findUnique: jest.fn(), }, userAuth: { update: jest.fn(), }, }; const jwt = { sign: jest.fn().mockReturnValue('token') }; const config = { get: jest.fn().mockReturnValue('1h') }; const systemConfig = {}; const invites = {}; const sms = {}; const audit = { log: jest.fn() }; let service: AuthService; beforeEach(() => { jest.clearAllMocks(); service = new AuthService( prisma as never, jwt as never, config as never, systemConfig as never, invites as never, sms as never, audit as never, ); }); it('blocks player login when parent agent is suspended with blockDirectPlayerLogin', async () => { const passwordHash = await bcrypt.hash('Player@123', 4); prisma.user.findUnique .mockResolvedValueOnce({ id: 10n, username: 'player1', userType: 'PLAYER', status: 'ACTIVE', parentId: 20n, auth: { passwordHash, loginFailCount: 0, lockedUntil: null }, adminRole: null, }) .mockResolvedValueOnce({ userType: 'AGENT', status: 'SUSPENDED', agentProfile: { blockDirectPlayerLogin: true }, }); await expect(service.login('player1', 'Player@123', 'player')).rejects.toMatchObject( expectAppError('PARENT_AGENT_SUSPENDED'), ); expect(jwt.sign).not.toHaveBeenCalled(); }); it('allows player login when parent agent suspended without blockDirectPlayerLogin', async () => { const passwordHash = await bcrypt.hash('Player@123', 4); prisma.user.findUnique .mockResolvedValueOnce({ id: 10n, username: 'player1', userType: 'PLAYER', status: 'ACTIVE', parentId: 20n, locale: 'zh-CN', auth: { passwordHash, loginFailCount: 0, lockedUntil: null }, adminRole: null, }) .mockResolvedValueOnce({ userType: 'AGENT', status: 'SUSPENDED', agentProfile: { blockDirectPlayerLogin: false }, }); prisma.userAuth.update.mockResolvedValue({}); const result = await service.login('player1', 'Player@123', 'player'); expect(result.token).toBe('token'); }); });