import { describe, expect, it, vi } from 'vitest'; import { CatalogService } from './catalog.service.js'; import type { PrismaService } from '../prisma/prisma.service.js'; import { ErrorCodes } from '../common/error-codes.js'; import { runWithRequestContext } from '../common/context/request-context.js'; import type { RequestContext } from '../common/context/request-context.js'; import type { ConfigSvcOrgDto } from './dto.js'; const ctx: RequestContext = { tenantCode: 'T001', userId: 'u1', username: 'admin', super: false, dataScope: 'self', orgCode: null, orgCodes: [], }; function makeService(overrides: { svcLocalFindMany?: ReturnType; svcNationalFindMany?: ReturnType; svcOrgFindMany?: ReturnType; svcOrgFindFirst?: ReturnType; svcOrgUpdate?: ReturnType; } = {}) { const svcLocalFindMany = overrides.svcLocalFindMany ?? vi.fn().mockResolvedValue([{ id: 'l1', locCode: '530100', localItemName: '头面清洁' }]); const svcNationalFindMany = overrides.svcNationalFindMany ?? vi.fn().mockResolvedValue([{ id: 'n1', natName: '擦浴' }]); const svcOrgFindMany = overrides.svcOrgFindMany ?? vi.fn().mockResolvedValue([{ id: 'o1', tenantCode: 'T001', isEnabled: true }]); const svcOrgFindFirst = overrides.svcOrgFindFirst ?? vi.fn().mockResolvedValue({ id: 'o1', tenantCode: 'T001' }); const svcOrgUpdate = overrides.svcOrgUpdate ?? vi.fn().mockResolvedValue({ id: 'o1', isEnabled: false }); const prisma = { client: { svcLocal: { findMany: svcLocalFindMany }, svcNational: { findMany: svcNationalFindMany }, svcOrg: { findMany: svcOrgFindMany, findFirst: svcOrgFindFirst, update: svcOrgUpdate }, }, }; return { service: new CatalogService(prisma as unknown as PrismaService), svcLocalFindMany, svcNationalFindMany, svcOrgFindMany, svcOrgFindFirst, svcOrgUpdate, }; } describe('CatalogService', () => { it('listLocal 按 locCode 读全局 SvcLocal(不注入 tenantCode)', async () => { const { service, svcLocalFindMany, svcNationalFindMany } = makeService(); const result = await service.listLocal('530100'); expect(svcLocalFindMany).toHaveBeenCalledWith({ where: { locCode: '530100' } }); expect(svcNationalFindMany).not.toHaveBeenCalled(); expect(result).toEqual([{ id: 'l1', locCode: '530100', localItemName: '头面清洁' }]); }); it('listLocal 无本地目录时回退 SvcNational', async () => { const svcLocalFindMany = vi.fn().mockResolvedValue([]); const svcNationalFindMany = vi.fn().mockResolvedValue([{ id: 'n1', natName: '擦浴' }]); const { service } = makeService({ svcLocalFindMany, svcNationalFindMany }); const result = await service.listLocal('530200'); expect(svcNationalFindMany).toHaveBeenCalled(); expect(result).toEqual([{ id: 'n1', natName: '擦浴' }]); }); it('listOrg 读当前租户 SvcOrg', async () => { const { service, svcOrgFindMany } = makeService(); const result = await runWithRequestContext(ctx, () => service.listOrg()); expect(svcOrgFindMany).toHaveBeenCalledWith({ where: { tenantCode: 'T001' } }); expect(result).toEqual([{ id: 'o1', tenantCode: 'T001', isEnabled: true }]); }); it('config 只允许改 isEnabled/price,写操作按当前租户定位', async () => { const { service, svcOrgFindFirst, svcOrgUpdate } = makeService(); await runWithRequestContext(ctx, () => service.config({ id: 'o1', isEnabled: false, price: '50' } as ConfigSvcOrgDto), ); expect(svcOrgFindFirst).toHaveBeenCalledWith({ where: { id: 'o1', tenantCode: 'T001' } }); expect(svcOrgUpdate).toHaveBeenCalledWith({ where: { id: 'o1' }, data: { isEnabled: false, price: '50' } }); }); it.each([ ['srcId', 'n1'], ['srcType', 'national'], ])('config 夹带 %s 抛 SVC_LOC_IMMUTABLE', async (key, value) => { const { service, svcOrgFindFirst, svcOrgUpdate } = makeService(); await expect( runWithRequestContext(ctx, () => service.config({ id: 'o1', [key]: value } as ConfigSvcOrgDto)), ).rejects.toMatchObject({ response: { code: ErrorCodes.SVC_LOC_IMMUTABLE } }); expect(svcOrgFindFirst).not.toHaveBeenCalled(); expect(svcOrgUpdate).not.toHaveBeenCalled(); }); it('config 目标不存在抛 SVC_ORG_NOT_FOUND', async () => { const svcOrgFindFirst = vi.fn().mockResolvedValue(null); const { service } = makeService({ svcOrgFindFirst }); await expect( runWithRequestContext(ctx, () => service.config({ id: 'missing', isEnabled: true } as ConfigSvcOrgDto)), ).rejects.toMatchObject({ response: { code: ErrorCodes.SVC_ORG_NOT_FOUND } }); }); });