feat(v2.4): 切换计划与开发任务主写
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
import { NotFoundException } from '@nestjs/common';
|
||||
import { VersionPlanService } from './version-plan.service';
|
||||
|
||||
describe('VersionPlanService domain writes', () => {
|
||||
const makeService = () => {
|
||||
const workActivity = {
|
||||
record: jest.fn().mockResolvedValue({ id: 'activity-1' }),
|
||||
};
|
||||
const prisma = {
|
||||
version: {
|
||||
findUnique: jest.fn(),
|
||||
},
|
||||
versionPlan: {
|
||||
create: jest.fn(),
|
||||
delete: jest.fn(),
|
||||
findFirst: jest.fn(),
|
||||
findMany: jest.fn(),
|
||||
update: jest.fn(),
|
||||
},
|
||||
};
|
||||
|
||||
return {
|
||||
prisma,
|
||||
workActivity,
|
||||
service: new VersionPlanService(prisma as any, workActivity as any),
|
||||
};
|
||||
};
|
||||
|
||||
it('creates version plans directly under a version partition', async () => {
|
||||
const { prisma, workActivity, service } = makeService();
|
||||
prisma.version.findUnique.mockResolvedValue({ id: 'version-1', productId: 'product-1', projectId: 'project-1' });
|
||||
prisma.versionPlan.create.mockResolvedValue({ id: 'plan-1', versionId: 'version-1', title: '产品方案' });
|
||||
|
||||
await service.create('version-1', {
|
||||
type: 'product',
|
||||
title: '产品方案',
|
||||
owner: 'member-1',
|
||||
startTime: '2026-07-08T09:00:00.000Z',
|
||||
endTime: '2026-07-08T18:00:00.000Z',
|
||||
linkedRequirementIds: ['req-1'],
|
||||
} as any);
|
||||
|
||||
expect(prisma.versionPlan.create).toHaveBeenCalledWith({
|
||||
data: expect.objectContaining({
|
||||
versionId: 'version-1',
|
||||
productId: 'product-1',
|
||||
projectId: 'project-1',
|
||||
type: 'product',
|
||||
title: '产品方案',
|
||||
ownerId: 'member-1',
|
||||
expectedStartAt: new Date('2026-07-08T09:00:00.000Z'),
|
||||
expectedEndAt: new Date('2026-07-08T18:00:00.000Z'),
|
||||
requirementCoverage: [{ requirementId: 'req-1', status: 'not_started' }],
|
||||
}),
|
||||
});
|
||||
expect(workActivity.record).toHaveBeenCalledWith(expect.objectContaining({
|
||||
versionId: 'version-1',
|
||||
sourceType: 'version_plan',
|
||||
sourceId: 'plan-1',
|
||||
action: 'version_plan_created',
|
||||
}));
|
||||
});
|
||||
|
||||
it('updates plan status inside version scope and records activity evidence', async () => {
|
||||
const { prisma, workActivity, service } = makeService();
|
||||
prisma.versionPlan.findFirst.mockResolvedValue({
|
||||
id: 'plan-1',
|
||||
versionId: 'version-1',
|
||||
productId: 'product-1',
|
||||
projectId: 'project-1',
|
||||
status: 'pending',
|
||||
title: '产品方案',
|
||||
ownerId: 'member-1',
|
||||
});
|
||||
prisma.versionPlan.update.mockResolvedValue({
|
||||
id: 'plan-1',
|
||||
versionId: 'version-1',
|
||||
productId: 'product-1',
|
||||
projectId: 'project-1',
|
||||
status: 'in_progress',
|
||||
title: '产品方案',
|
||||
ownerId: 'member-1',
|
||||
});
|
||||
|
||||
await service.update('version-1', 'plan-1', { status: 'in_progress' });
|
||||
|
||||
expect(prisma.versionPlan.update).toHaveBeenCalledWith({
|
||||
where: { id: 'plan-1' },
|
||||
data: expect.objectContaining({
|
||||
status: 'in_progress',
|
||||
actualStartAt: expect.any(Date),
|
||||
}),
|
||||
});
|
||||
expect(workActivity.record).toHaveBeenCalledWith(expect.objectContaining({
|
||||
versionId: 'version-1',
|
||||
sourceType: 'version_plan',
|
||||
sourceId: 'plan-1',
|
||||
action: 'version_plan_started',
|
||||
}));
|
||||
});
|
||||
|
||||
it('rejects plan updates outside the version partition', async () => {
|
||||
const { prisma, service } = makeService();
|
||||
prisma.versionPlan.findFirst.mockResolvedValue(null);
|
||||
|
||||
await expect(service.update('version-1', 'missing-plan', { title: 'Ghost' })).rejects.toBeInstanceOf(NotFoundException);
|
||||
expect(prisma.versionPlan.update).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user