113 lines
4.6 KiB
TypeScript
113 lines
4.6 KiB
TypeScript
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<typeof vi.fn>;
|
||
svcNationalFindMany?: ReturnType<typeof vi.fn>;
|
||
svcOrgFindMany?: ReturnType<typeof vi.fn>;
|
||
svcOrgFindFirst?: ReturnType<typeof vi.fn>;
|
||
svcOrgUpdate?: ReturnType<typeof vi.fn>;
|
||
} = {}) {
|
||
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 } });
|
||
});
|
||
});
|