140 行
3.8 KiB
JavaScript
140 行
3.8 KiB
JavaScript
const integrationsRoutes = require('../../../src/routes/integrations');
|
|
|
|
describe('Integrations asset ERP route helpers', () => {
|
|
const { canReadAsset, enqueueAssetExportAndMaybeRun, parseRunNow } = integrationsRoutes.__testables;
|
|
|
|
function createAsset(overrides = {}) {
|
|
return {
|
|
id: 'asset-1',
|
|
asset_no: 'ZC-001',
|
|
assignee_employee_id: 'employee-1',
|
|
legal_entity_id: 'le-1',
|
|
cost_center_id: 'cc-1',
|
|
status: 'in_use',
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
it('respects asset read permission rules', () => {
|
|
const asset = createAsset();
|
|
|
|
expect(
|
|
canReadAsset(
|
|
{ employee: { id: 'employee-1', permissionCodes: ['asset.view_own'] } },
|
|
asset,
|
|
),
|
|
).toBe(true);
|
|
expect(
|
|
canReadAsset(
|
|
{ employee: { id: 'other', permissionCodes: ['asset.view_own'] } },
|
|
asset,
|
|
),
|
|
).toBe(false);
|
|
expect(
|
|
canReadAsset(
|
|
{ employee: { id: 'other', permissionCodes: ['asset.view_all'] } },
|
|
asset,
|
|
),
|
|
).toBe(true);
|
|
});
|
|
|
|
it('retries same asset payload without triggering runNow when duplicated', async () => {
|
|
const enqueueExport = vi.fn().mockResolvedValue({
|
|
id: 'outbox-1',
|
|
status: 'succeeded',
|
|
duplicated: true,
|
|
requeued: false,
|
|
eventType: 'erp.asset.export',
|
|
});
|
|
const runOutboxOnce = vi.fn();
|
|
|
|
const result = await enqueueAssetExportAndMaybeRun({
|
|
prisma: {},
|
|
config: {},
|
|
logger: {},
|
|
context: { asset: createAsset() },
|
|
actorId: 'actor-1',
|
|
runNow: true,
|
|
findExistingOutbox: vi.fn().mockResolvedValue({
|
|
id: 'outbox-1',
|
|
status: 'succeeded',
|
|
payload: {
|
|
exportType: 'asset_ledger_summary',
|
|
entityId: 'asset-1',
|
|
assetNo: 'ZC-001',
|
|
legalEntity: { id: 'le-1' },
|
|
costCenter: { id: 'cc-1' },
|
|
status: 'in_use',
|
|
},
|
|
}),
|
|
enqueueExport,
|
|
runOutboxOnce,
|
|
});
|
|
|
|
expect(enqueueExport).toHaveBeenCalledWith(
|
|
{},
|
|
{},
|
|
expect.objectContaining({
|
|
payload: expect.objectContaining({
|
|
assetNo: 'ZC-001',
|
|
status: 'in_use',
|
|
}),
|
|
requeueOnFailure: true,
|
|
}),
|
|
);
|
|
expect(runOutboxOnce).not.toHaveBeenCalled();
|
|
expect(result.duplicated).toBe(true);
|
|
});
|
|
|
|
it('resyncs current asset snapshot when financial drift exists', async () => {
|
|
const enqueueExport = vi.fn().mockResolvedValue({
|
|
id: 'outbox-1',
|
|
status: 'pending',
|
|
duplicated: false,
|
|
requeued: true,
|
|
eventType: 'erp.asset.export',
|
|
});
|
|
const runOutboxOnce = vi.fn().mockResolvedValue({ total: 1, succeeded: 1, failed: 0 });
|
|
|
|
const result = await enqueueAssetExportAndMaybeRun({
|
|
prisma: {},
|
|
config: {},
|
|
logger: {},
|
|
context: { asset: createAsset({ cost_center_id: 'cc-2', status: 'retired' }) },
|
|
actorId: 'actor-1',
|
|
runNow: true,
|
|
findExistingOutbox: vi.fn().mockResolvedValue({
|
|
id: 'outbox-1',
|
|
status: 'succeeded',
|
|
payload: {
|
|
exportType: 'asset_ledger_summary',
|
|
entityId: 'asset-1',
|
|
assetNo: 'ZC-001',
|
|
legalEntity: { id: 'le-1' },
|
|
costCenter: { id: 'cc-1' },
|
|
status: 'in_use',
|
|
},
|
|
}),
|
|
enqueueExport,
|
|
runOutboxOnce,
|
|
});
|
|
|
|
expect(enqueueExport).toHaveBeenCalledWith(
|
|
{},
|
|
{},
|
|
expect.objectContaining({
|
|
payload: undefined,
|
|
changeType: 'retired',
|
|
changedFields: expect.arrayContaining(['costCenterId', 'status']),
|
|
}),
|
|
);
|
|
expect(runOutboxOnce).toHaveBeenCalled();
|
|
expect(result.requeued).toBe(true);
|
|
});
|
|
|
|
it('parses runNow flags for asset requeue route', () => {
|
|
expect(parseRunNow({ runNow: '1' })).toBe(true);
|
|
expect(parseRunNow({ runNow: '0' })).toBe(false);
|
|
});
|
|
});
|