Files
CRM_Montzar/apps/api/test/organizations.test.ts
2026-10-17 17:01:52 +03:30

131 lines
3.7 KiB
TypeScript

import 'reflect-metadata';
import { describe, it } from 'node:test';
import assert from 'node:assert/strict';
import { BadRequestException } from '@nestjs/common';
import type { UpdatePriorArchiveCutoffRequest } from '@mento/shared';
import { ROLES_KEY } from '../src/auth/roles.decorator';
import { OrganizationsController } from '../src/organizations/organizations.controller';
import { OrganizationsService } from '../src/organizations/organizations.service';
const cutoff = new Date('2026-10-01T00:00:00.000Z');
function makeOrg(overrides: Record<string, unknown> = {}) {
return {
id: 'org-1',
name: 'Org',
slug: 'org',
businessField: 'personal',
baseCurrency: 'IRR',
enabledCurrencies: ['IRR'],
enabledModules: ['prior_data_archive'],
priorArchiveEnabledAt: cutoff,
exchangeRates: [],
...overrides,
};
}
function makeService(org = makeOrg()) {
const calls: Array<{ where: unknown; data: unknown }> = [];
const prisma = {
organization: {
findUnique: async () => org,
update: async (args: { where: unknown; data: unknown }) => {
calls.push(args);
return org;
},
},
};
const billing = {
normalizeModules: (modules?: string[]) => modules ?? [],
};
const service = new OrganizationsService(
prisma as never,
{} as never,
{} as never,
{} as never,
billing as never,
);
return { service, calls };
}
describe('OrganizationsService prior archive cutoff', () => {
it('returns the current cutoff in the organization dto', async () => {
const { service } = makeService();
const dto = await service.getCurrent('org-1');
assert.equal(dto.priorArchiveCutoffAt, cutoff.toISOString());
});
it('updates and clears the cutoff when prior archive is enabled', async () => {
const { service, calls } = makeService();
await service.updatePriorArchiveCutoff('org-1', '2026-11-02T00:00:00.000Z');
await service.updatePriorArchiveCutoff('org-1', null);
assert.deepEqual(calls.map((call) => call.data), [
{ priorArchiveEnabledAt: new Date('2026-11-02T00:00:00.000Z') },
{ priorArchiveEnabledAt: null },
]);
});
it('rejects invalid cutoffs and disabled modules', async () => {
const enabled = makeService();
await assert.rejects(
() => enabled.service.updatePriorArchiveCutoff('org-1', 'not-a-date'),
BadRequestException,
);
const disabled = makeService(makeOrg({ enabledModules: [] }));
await assert.rejects(
() =>
disabled.service.updatePriorArchiveCutoff(
'org-1',
'2026-11-02T00:00:00.000Z',
),
BadRequestException,
);
});
});
describe('OrganizationsController prior archive cutoff', () => {
it('exposes an owner-only explicit cutoff patch', async () => {
const calls: Array<UpdatePriorArchiveCutoffRequest['cutoffAt']> = [];
const controller = new OrganizationsController(
{
updatePriorArchiveCutoff: async (
_organizationId: string,
cutoffAt: UpdatePriorArchiveCutoffRequest['cutoffAt'],
) => {
calls.push(cutoffAt);
return makeOrg();
},
} as never,
{} as never,
);
assert.deepEqual(
Reflect.getMetadata(
ROLES_KEY,
controller.updatePriorArchiveCutoff,
),
['owner'],
);
assert.throws(
() =>
controller.updatePriorArchiveCutoff(
{ organizationId: 'org-1' },
{} as UpdatePriorArchiveCutoffRequest,
),
BadRequestException,
);
await controller.updatePriorArchiveCutoff(
{ organizationId: 'org-1' },
{ cutoffAt: null },
);
assert.deepEqual(calls, [null]);
});
});