Files
thebet365/apps/api/src/domains/player-messages/player-messages.service.spec.ts
Mars cffad00652 sync(theme-4): 同步 main 最新 API/Admin 与玩家端逻辑
从 main 同步站内信手动发送、媒体库选择、列表子路由重构等 API/Admin 改动;玩家端仅合并 ADMIN_CUSTOM 富文本、消息预览与充值截图压缩逻辑,保留 theme-4 样式。
2026-06-22 14:12:40 +08:00

230 lines
7.0 KiB
TypeScript

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<unknown>) => 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: '<p>限时优惠</p>' },
{ 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: '<p>World</p>',
translations: {
'en-US': { title: 'Hello', body: '<p>World</p>' },
'zh-CN': { title: '你好', body: '<p>内容</p>' },
},
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: '<p>World</p>' },
{ locale: 'zh-CN', title: '你好', body: '<p>内容</p>' },
],
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 } });
});
});