mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-29 23:28:11 +00:00
116 lines
3.9 KiB
TypeScript
116 lines
3.9 KiB
TypeScript
import { ContractDocumentHistoryService } from './contract-document-history.service';
|
|
|
|
/**
|
|
* `iam.users.name` is a localized jsonb object, not a string. Reading it
|
|
* naively puts "[object Object]" in the audit trail — or, worse, throws and
|
|
* leaves every revision anonymous. These specs pin the resolution rules.
|
|
*/
|
|
describe('ContractDocumentHistoryService actor names', () => {
|
|
const build = (rows: unknown[]) => {
|
|
const saved: Array<Record<string, unknown>> = [];
|
|
const service = Object.create(
|
|
ContractDocumentHistoryService.prototype,
|
|
) as ContractDocumentHistoryService;
|
|
Object.assign(service, {
|
|
logger: { warn: jest.fn(), error: jest.fn() },
|
|
dataSource: { query: jest.fn().mockResolvedValue(rows) },
|
|
revisionRepo: {
|
|
create: (row: Record<string, unknown>) => row,
|
|
save: jest.fn((row: Record<string, unknown>) => {
|
|
saved.push(row);
|
|
return Promise.resolve(row);
|
|
}),
|
|
find: jest.fn().mockResolvedValue([]),
|
|
},
|
|
});
|
|
return { service, saved };
|
|
};
|
|
|
|
const change = {
|
|
kind: 'FIELD_CHANGED' as const,
|
|
field: 'routes',
|
|
label: 'Routes',
|
|
from: 'A → B',
|
|
to: 'A → C',
|
|
};
|
|
|
|
it('prefers the English label from the localized name object', async () => {
|
|
const { service, saved } = build([
|
|
{ id: 'u-1', name: { am: 'ሱፐር አድሚን', en: 'Super Admin' }, username: 'superadmin' },
|
|
]);
|
|
|
|
await service.recordChanges({ contractId: 'c-1', changes: [change], actorId: 'u-1' });
|
|
|
|
expect(saved[0].actorName).toBe('Super Admin');
|
|
});
|
|
|
|
it('falls back to another locale, then username, then email', async () => {
|
|
const onlyAmharic = build([{ id: 'u-1', name: { am: 'ሱፐር' }, username: 'x' }]);
|
|
await onlyAmharic.service.recordChanges({
|
|
contractId: 'c-1',
|
|
changes: [change],
|
|
actorId: 'u-1',
|
|
});
|
|
expect(onlyAmharic.saved[0].actorName).toBe('ሱፐር');
|
|
|
|
const noName = build([{ id: 'u-1', name: null, username: 'operator', email: 'o@edr' }]);
|
|
await noName.service.recordChanges({
|
|
contractId: 'c-1',
|
|
changes: [change],
|
|
actorId: 'u-1',
|
|
});
|
|
expect(noName.saved[0].actorName).toBe('operator');
|
|
|
|
const emailOnly = build([{ id: 'u-1', name: {}, username: null, email: 'o@edr.local' }]);
|
|
await emailOnly.service.recordChanges({
|
|
contractId: 'c-1',
|
|
changes: [change],
|
|
actorId: 'u-1',
|
|
});
|
|
expect(emailOnly.saved[0].actorName).toBe('o@edr.local');
|
|
});
|
|
|
|
it('never writes "[object Object]" as the actor name', async () => {
|
|
const { service, saved } = build([{ id: 'u-1', name: { en: 'Real Name' } }]);
|
|
|
|
await service.recordChanges({ contractId: 'c-1', changes: [change], actorId: 'u-1' });
|
|
|
|
expect(String(saved[0].actorName)).not.toContain('object Object');
|
|
});
|
|
|
|
it('records nothing when the change set is empty', async () => {
|
|
const { service, saved } = build([]);
|
|
|
|
await service.recordChanges({ contractId: 'c-1', changes: [], actorId: 'u-1' });
|
|
|
|
expect(saved).toHaveLength(0);
|
|
});
|
|
|
|
it('still records the revision when the user lookup fails', async () => {
|
|
const { service, saved } = build([]);
|
|
Object.assign(service, {
|
|
dataSource: { query: jest.fn().mockRejectedValue(new Error('iam down')) },
|
|
});
|
|
|
|
await service.recordChanges({ contractId: 'c-1', changes: [change], actorId: 'u-1' });
|
|
|
|
expect(saved).toHaveLength(1);
|
|
expect(saved[0].actorName).toBeNull();
|
|
});
|
|
|
|
it('resolves names for legacy rows that predate the actor_name column', async () => {
|
|
const { service } = build([{ id: 'u-1', name: { en: 'Abenezer Haile' } }]);
|
|
Object.assign(service, {
|
|
revisionRepo: {
|
|
find: jest
|
|
.fn()
|
|
.mockResolvedValue([{ id: 'r-1', actorId: 'u-1', actorName: null }]),
|
|
},
|
|
});
|
|
|
|
const [revision] = await service.list('c-1');
|
|
|
|
expect(revision.actorName).toBe('Abenezer Haile');
|
|
});
|
|
});
|