import { PlayerMessagesService } from './player-messages.service'; describe('PlayerMessagesService', () => { // eslint-disable-next-line @typescript-eslint/no-explicit-any const prisma: any = { playerMessage: { create: jest.fn(), findMany: jest.fn(), count: jest.fn(), findFirst: jest.fn(), update: jest.fn(), updateMany: jest.fn(), delete: jest.fn(), deleteMany: jest.fn(), createMany: jest.fn(), }, playerMessageBroadcast: { findMany: jest.fn(), count: jest.fn(), findUnique: jest.fn(), create: jest.fn(), delete: jest.fn(), }, user: { findUnique: jest.fn(), findMany: jest.fn(), findFirst: jest.fn(), }, $transaction: jest.fn(async (fn: (tx: typeof prisma) => Promise) => fn(prisma)), }; let service: PlayerMessagesService; beforeEach(() => { jest.clearAllMocks(); service = new PlayerMessagesService(prisma as never); }); it('creates localized deposit approved messages', async () => { prisma.playerMessage.create.mockResolvedValue({ id: 1n, type: 'DEPOSIT_APPROVED', title: '充值已到账', body: 'body', payload: { orderNo: 'DEP-1' }, readAt: null, createdAt: new Date('2026-06-17T10:00:00.000Z'), }); const result = await service.createDepositApprovedMessage(7n, { depositOrderId: 10n, orderNo: 'DEP-1', amount: '100', approvedAmount: '100', locale: 'zh-CN', }); expect(prisma.playerMessage.create).toHaveBeenCalledWith( expect.objectContaining({ data: expect.objectContaining({ userId: 7n, type: 'DEPOSIT_APPROVED', title: '充值已到账', }), }), ); expect(result.type).toBe('DEPOSIT_APPROVED'); expect(result.isRead).toBe(false); }); it('returns paginated inbox with unread count', async () => { prisma.playerMessage.findMany.mockResolvedValue([ { id: 2n, type: 'DEPOSIT_REJECTED', title: 'Deposit rejected', body: 'Rejected', payload: null, readAt: null, createdAt: new Date('2026-06-17T11:00:00.000Z'), }, ]); prisma.playerMessage.count.mockResolvedValueOnce(1).mockResolvedValueOnce(1); const result = await service.listForPlayer(7n, 1, 20); expect(result.items).toHaveLength(1); expect(result.unreadCount).toBe(1); expect(result.total).toBe(1); }); it('deletes a single message for the player', async () => { prisma.playerMessage.findFirst.mockResolvedValue({ id: 3n, type: 'DEPOSIT_APPROVED', title: 'Deposit approved', body: 'body', payload: null, readAt: null, createdAt: new Date('2026-06-17T12:00:00.000Z'), }); prisma.playerMessage.delete.mockResolvedValue({ id: 3n }); const result = await service.deleteForPlayer(7n, 3n); expect(prisma.playerMessage.delete).toHaveBeenCalledWith({ where: { id: 3n } }); expect(result).toEqual({ deleted: true, wasUnread: true }); }); it('deletes all messages for the player', async () => { prisma.playerMessage.deleteMany.mockResolvedValue({ count: 4 }); const result = await service.deleteAllForPlayer(7n); expect(prisma.playerMessage.deleteMany).toHaveBeenCalledWith({ where: { userId: 7n } }); expect(result).toEqual({ deleted: 4 }); }); it('broadcasts banner promotion inbox messages to active players', async () => { prisma.user.findMany.mockResolvedValue([ { id: 10n, locale: 'zh-CN', preferences: { locale: 'zh-CN' } }, { id: 11n, locale: 'en-US', preferences: null }, ]); prisma.playerMessage.createMany.mockResolvedValue({ count: 2 }); const count = await service.broadcastBannerPromotion({ contentId: 99n, translations: [ { locale: 'zh-CN', title: '夏季活动', body: '

限时优惠

' }, { locale: 'en-US', title: 'Summer promo', body: '' }, ], }); expect(count).toBe(2); expect(prisma.playerMessage.createMany).toHaveBeenCalledWith({ data: expect.arrayContaining([ expect.objectContaining({ userId: 10n, type: 'BANNER_PROMO', title: '夏季活动', body: '限时优惠', }), expect.objectContaining({ userId: 11n, type: 'BANNER_PROMO', title: 'Summer promo', }), ]), }); }); it('broadcasts announcement promotion inbox messages to active players', async () => { prisma.user.findMany.mockResolvedValue([ { id: 10n, locale: 'zh-CN', preferences: { locale: 'zh-CN' } }, ]); prisma.playerMessage.createMany.mockResolvedValue({ count: 1 }); const count = await service.broadcastAnnouncementPromotion({ contentId: 100n, translations: [{ locale: 'zh-CN', title: '维护通知', body: '系统维护中' }], }); expect(count).toBe(1); expect(prisma.playerMessage.createMany).toHaveBeenCalledWith({ data: [ expect.objectContaining({ userId: 10n, type: 'ANNOUNCEMENT_PROMO', title: '维护通知', body: '系统维护中', }), ], }); }); it('creates custom broadcast to all active players', async () => { prisma.user.findMany.mockResolvedValue([ { id: 1n, username: 'p1', locale: 'zh-CN', preferences: { locale: 'zh-CN' } }, { id: 2n, username: 'p2', locale: 'en-US', preferences: null }, ]); prisma.playerMessageBroadcast.create.mockResolvedValue({ id: 9n, title: 'Hello', body: '

World

', translations: { 'en-US': { title: 'Hello', body: '

World

' }, 'zh-CN': { title: '你好', body: '

内容

' }, }, targetType: 'ALL', targetUserId: null, targetUsername: null, recipientCount: 2, createdById: 99n, createdByUsername: 'admin', createdAt: new Date('2026-06-22T10:00:00.000Z'), }); const result = await service.createCustomBroadcast({ translations: [ { locale: 'en-US', title: 'Hello', body: '

World

' }, { locale: 'zh-CN', title: '你好', body: '

内容

' }, ], targetType: 'ALL', createdById: 99n, createdByUsername: 'admin', }); expect(result.recipientCount).toBe(2); expect(prisma.playerMessage.createMany).toHaveBeenCalledWith({ data: [ expect.objectContaining({ userId: 1n, type: 'ADMIN_CUSTOM', title: '你好', broadcastId: 9n }), expect.objectContaining({ userId: 2n, type: 'ADMIN_CUSTOM', title: 'Hello', broadcastId: 9n }), ], }); }); it('deletes broadcast and cascades player messages', async () => { prisma.playerMessageBroadcast.findUnique.mockResolvedValue({ id: 5n, recipientCount: 3, }); prisma.playerMessageBroadcast.delete.mockResolvedValue({ id: 5n }); const result = await service.deleteBroadcast(5n); expect(result).toEqual({ deleted: true, recipientCount: 3 }); expect(prisma.playerMessageBroadcast.delete).toHaveBeenCalledWith({ where: { id: 5n } }); }); });