| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165 |
- import { ConflictException, NotFoundException } from '@nestjs/common';
- import { Test } from '@nestjs/testing';
- import { PrismaService } from '../../prisma/prisma.service';
- import { StylistService } from './stylist.service';
- describe('StylistService', () => {
- let service: StylistService;
- const prisma = {
- hairstylistUser: {
- findFirst: jest.fn(),
- update: jest.fn(),
- },
- hairTemplate: {
- count: jest.fn(),
- findMany: jest.fn(),
- },
- };
- const stylist = {
- id: BigInt(7),
- username: 'Yasuaki',
- email: 'yasuaki@example.com',
- simulationInviteCode: 'ABCD1234',
- displayName: 'YASUAKI OGAWA',
- slug: 'yasuaki-ogawa',
- photoUrl: 'https://cdn.example.com/photo.jpg',
- lead: '短介绍',
- bio: '详细介绍',
- bookingUrl: 'https://example.com/booking',
- experienceYears: 22,
- rating: '5.0',
- reviewCount: 124,
- specialties: '鲍勃',
- address: '东京',
- hours: '预约制',
- password: 'hashed',
- createTime: new Date('2026-09-17T00:00:00.000Z'),
- updateTime: new Date('2026-09-17T00:00:00.000Z'),
- isDeleted: 0,
- };
- beforeEach(async () => {
- jest.resetAllMocks();
- const moduleRef = await Test.createTestingModule({
- providers: [StylistService, { provide: PrismaService, useValue: prisma }],
- }).compile();
- service = moduleRef.get(StylistService);
- });
- it('returns the authenticated stylist profile with published template count', async () => {
- prisma.hairstylistUser.findFirst.mockResolvedValue(stylist);
- prisma.hairTemplate.count.mockResolvedValue(3);
- prisma.hairTemplate.findMany.mockResolvedValue([
- {
- id: BigInt(11),
- templateName: '柔和长发',
- templateNameI18n: { zh: '柔和长发', ja: '柔らかいロング', en: 'Soft long hair' },
- coverUrl: 'https://cdn.example.com/cover.jpg',
- category: 'LONG',
- description: '自然长发',
- },
- ]);
- await expect(service.getMyProfile(BigInt(7))).resolves.toMatchObject({
- id: '7',
- displayName: 'YASUAKI OGAWA',
- slug: 'yasuaki-ogawa',
- inviteCode: 'ABCD1234',
- publishedTemplateCount: 3,
- experienceYears: 22,
- });
- });
- it('rejects reserved public slugs', async () => {
- prisma.hairstylistUser.findFirst.mockResolvedValue(stylist);
- await expect(
- service.updateMyProfile(BigInt(7), {
- displayName: 'YASUAKI OGAWA',
- slug: 'manage',
- }),
- ).rejects.toThrow('该公开主页路径不可用');
- });
- it('rejects duplicate public slugs', async () => {
- prisma.hairstylistUser.findFirst
- .mockResolvedValueOnce(stylist)
- .mockResolvedValueOnce({ id: BigInt(8) });
- await expect(
- service.updateMyProfile(BigInt(7), {
- displayName: 'YASUAKI OGAWA',
- slug: 'taken-slug',
- }),
- ).rejects.toBeInstanceOf(ConflictException);
- });
- it('returns a public profile with published templates only', async () => {
- prisma.hairstylistUser.findFirst.mockResolvedValue(stylist);
- prisma.hairTemplate.count.mockResolvedValue(1);
- prisma.hairTemplate.findMany.mockResolvedValue([
- {
- id: BigInt(11),
- templateName: '柔和长发',
- templateNameI18n: { zh: '柔和长发', ja: '柔らかいロング', en: 'Soft long hair' },
- coverUrl: 'https://cdn.example.com/cover.jpg',
- category: 'LONG',
- description: '自然长发',
- },
- ]);
- const result = await service.getPublicProfile('yasuaki-ogawa');
- expect(result).not.toHaveProperty('email');
- expect(result).not.toHaveProperty('username');
- expect(result.templates).toEqual([
- expect.objectContaining({
- id: '11',
- templateName: '柔和长发',
- coverUrl: 'https://cdn.example.com/cover.jpg',
- }),
- ]);
- expect(prisma.hairTemplate.findMany).toHaveBeenCalledWith(
- expect.objectContaining({
- where: expect.objectContaining({
- status: 'PUBLIC',
- isSystemTemplate: 'NO',
- templateUrl: { not: '' },
- }),
- }),
- );
- });
- it('persists a public slug and invite code when they are missing', async () => {
- const pending = { ...stylist, slug: null, simulationInviteCode: null };
- prisma.hairstylistUser.findFirst
- .mockResolvedValueOnce(pending)
- .mockResolvedValueOnce(null)
- .mockResolvedValueOnce(null);
- prisma.hairstylistUser.update.mockResolvedValue({
- ...pending,
- slug: 'yasuaki',
- simulationInviteCode: 'ABCD2345',
- });
- prisma.hairTemplate.count.mockResolvedValue(0);
- prisma.hairTemplate.findMany.mockResolvedValue([]);
- await expect(service.getMyProfile(BigInt(7))).resolves.toMatchObject({
- slug: 'yasuaki',
- inviteCode: 'ABCD2345',
- });
- expect(prisma.hairstylistUser.update).toHaveBeenCalledWith({
- where: { id: stylist.id },
- data: {
- slug: 'yasuaki',
- simulationInviteCode: expect.stringMatching(/^[A-HJ-NP-Z2-9]{8}$/),
- },
- });
- });
- it('throws when the public slug does not exist', async () => {
- prisma.hairstylistUser.findFirst.mockResolvedValue(null);
- await expect(service.getPublicProfile('missing')).rejects.toBeInstanceOf(NotFoundException);
- });
- });
|