207 lines
6.6 KiB
TypeScript
207 lines
6.6 KiB
TypeScript
import { describe, it } from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { BadRequestException, ForbiddenException, NotFoundException } from '@nestjs/common';
|
|
import { DealsService } from '../src/pipelines/deals.service';
|
|
import { ActivitiesService } from '../src/activities/activities.service';
|
|
import type { Actor } from '../src/common/record-scope';
|
|
|
|
const cutoff = new Date('2026-10-01T00:00:00.000Z');
|
|
const priorCreatedAt = new Date('2026-09-01T00:00:00.000Z');
|
|
const currentCreatedAt = new Date('2026-10-02T00:00:00.000Z');
|
|
const promotedAt = new Date('2026-10-03T00:00:00.000Z');
|
|
|
|
const actor: Actor = {
|
|
userId: 'u1',
|
|
role: 'sales',
|
|
organizationId: 'org1',
|
|
priorLens: 'archive',
|
|
priorCutoff: cutoff,
|
|
};
|
|
|
|
function dealRow(overrides: Record<string, unknown> = {}) {
|
|
return {
|
|
id: 'deal1',
|
|
organizationId: 'org1',
|
|
title: 'Prior deal',
|
|
createdAt: priorCreatedAt,
|
|
promotedFromPriorAt: null,
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
type DealPromotionProbe = ReturnType<typeof dealRow> | null;
|
|
|
|
function activityRow(overrides: Record<string, unknown> = {}) {
|
|
return {
|
|
id: 'act1',
|
|
organizationId: 'org1',
|
|
type: 'task',
|
|
status: 'open',
|
|
priority: 'medium',
|
|
title: 'Prior activity',
|
|
description: null,
|
|
dueAt: null,
|
|
durationMinutes: null,
|
|
reminderAt: null,
|
|
reminderNotifyApp: true,
|
|
reminderNotifySms: false,
|
|
reminderSentAt: null,
|
|
completedAt: null,
|
|
tags: [],
|
|
createdAt: priorCreatedAt,
|
|
updatedAt: priorCreatedAt,
|
|
promotedFromPriorAt: null,
|
|
person: null,
|
|
company: null,
|
|
deal: null,
|
|
lead: null,
|
|
assignee: null,
|
|
assignees: [],
|
|
createdBy: null,
|
|
participantIds: [],
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
type ActivityPromotionProbe = ReturnType<typeof activityRow> | null;
|
|
|
|
describe('promote from prior', () => {
|
|
it('promotes an accessible prior deal by setting only promotedFromPriorAt', async () => {
|
|
const calls: unknown[] = [];
|
|
const prisma = {
|
|
deal: {
|
|
findFirst: async (args: unknown) => {
|
|
calls.push(['findFirst', args]);
|
|
return dealRow();
|
|
},
|
|
update: async (args: unknown) => {
|
|
calls.push(['update', args]);
|
|
return dealRow({ promotedFromPriorAt: promotedAt });
|
|
},
|
|
},
|
|
};
|
|
const pipelines = {
|
|
toDealDto: (row: { id: string; createdAt: Date; promotedFromPriorAt: Date | null }) => ({
|
|
id: row.id,
|
|
createdAt: row.createdAt.toISOString(),
|
|
promotedFromPriorAt: row.promotedFromPriorAt?.toISOString() ?? null,
|
|
}),
|
|
};
|
|
const service = new DealsService(
|
|
prisma as never,
|
|
pipelines as never,
|
|
{} as never,
|
|
{} as never,
|
|
{} as never,
|
|
{} as never,
|
|
{} as never,
|
|
);
|
|
|
|
const dto = await service.promoteFromPrior(actor, 'deal1');
|
|
|
|
const updateArgs = (calls[1] as [string, { data: Record<string, unknown> }])[1];
|
|
assert.deepEqual(Object.keys(updateArgs.data), ['promotedFromPriorAt']);
|
|
assert.equal(dto.createdAt, priorCreatedAt.toISOString());
|
|
assert.equal(dto.promotedFromPriorAt, promotedAt.toISOString());
|
|
});
|
|
|
|
it('rejects deal promote when prior lens is off or cutoff missing', async () => {
|
|
const service = new DealsService({} as never, {} as never, {} as never, {} as never, {} as never, {} as never, {} as never);
|
|
|
|
await assert.rejects(
|
|
service.promoteFromPrior({ ...actor, priorLens: 'off' }, 'deal1'),
|
|
ForbiddenException,
|
|
);
|
|
await assert.rejects(
|
|
service.promoteFromPrior({ ...actor, priorCutoff: null }, 'deal1'),
|
|
ForbiddenException,
|
|
);
|
|
});
|
|
|
|
it('rejects deal promote unless the record is unpromoted prior data', async () => {
|
|
const prisma = {
|
|
deal: {
|
|
findFirst: async (): Promise<DealPromotionProbe> =>
|
|
dealRow({ promotedFromPriorAt: promotedAt }),
|
|
},
|
|
};
|
|
const service = new DealsService(prisma as never, {} as never, {} as never, {} as never, {} as never, {} as never, {} as never);
|
|
|
|
await assert.rejects(service.promoteFromPrior(actor, 'deal1'), BadRequestException);
|
|
|
|
prisma.deal.findFirst = async () => dealRow({ createdAt: currentCreatedAt });
|
|
await assert.rejects(service.promoteFromPrior(actor, 'deal1'), BadRequestException);
|
|
|
|
prisma.deal.findFirst = async () => null;
|
|
await assert.rejects(service.promoteFromPrior(actor, 'deal1'), NotFoundException);
|
|
});
|
|
|
|
it('promotes an accessible prior activity by setting only promotedFromPriorAt', async () => {
|
|
const calls: unknown[] = [];
|
|
const prisma = {
|
|
activity: {
|
|
findFirst: async (args: unknown) => {
|
|
calls.push(['findFirst', args]);
|
|
return activityRow();
|
|
},
|
|
update: async (args: unknown) => {
|
|
calls.push(['update', args]);
|
|
return activityRow({ promotedFromPriorAt: promotedAt });
|
|
},
|
|
},
|
|
};
|
|
const service = new ActivitiesService(
|
|
prisma as never,
|
|
{} as never,
|
|
{} as never,
|
|
{} as never,
|
|
{
|
|
boardContext: async () => ({ defaultBoardId: '', defaults: new Map() }),
|
|
effectiveBoardId: () => null,
|
|
effectiveStageId: () => null,
|
|
} as never,
|
|
{ system: async () => ({}), toSummary: (row: unknown) => row } as never,
|
|
{} as never,
|
|
{} as never,
|
|
{} as never,
|
|
);
|
|
|
|
const dto = await service.promoteFromPrior(actor, 'act1');
|
|
|
|
const updateArgs = (calls[1] as [string, { data: Record<string, unknown> }])[1];
|
|
assert.deepEqual(Object.keys(updateArgs.data), ['promotedFromPriorAt']);
|
|
assert.equal(dto.createdAt, priorCreatedAt.toISOString());
|
|
assert.equal(dto.promotedFromPriorAt, promotedAt.toISOString());
|
|
});
|
|
|
|
it('rejects activity promote unless the actor and record are eligible', async () => {
|
|
const prisma = {
|
|
activity: {
|
|
findFirst: async (): Promise<ActivityPromotionProbe> =>
|
|
activityRow({ createdAt: currentCreatedAt }),
|
|
},
|
|
};
|
|
const service = new ActivitiesService(
|
|
prisma as never,
|
|
{} as never,
|
|
{} as never,
|
|
{} as never,
|
|
{
|
|
boardContext: async () => ({ defaultBoardId: '', defaults: new Map() }),
|
|
effectiveBoardId: () => null,
|
|
effectiveStageId: () => null,
|
|
} as never,
|
|
{ system: async () => ({}), toSummary: (row: unknown) => row } as never,
|
|
{} as never,
|
|
{} as never,
|
|
{} as never,
|
|
);
|
|
|
|
await assert.rejects(service.promoteFromPrior({ ...actor, priorLens: 'off' }, 'act1'), ForbiddenException);
|
|
await assert.rejects(service.promoteFromPrior(actor, 'act1'), BadRequestException);
|
|
|
|
prisma.activity.findFirst = async () => null;
|
|
await assert.rejects(service.promoteFromPrior(actor, 'act1'), NotFoundException);
|
|
});
|
|
});
|