131 lines
3.7 KiB
TypeScript
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]);
|
|
});
|
|
});
|