52 lines
2.8 KiB
TypeScript
52 lines
2.8 KiB
TypeScript
import assert from 'node:assert/strict';
|
|
import test from 'node:test';
|
|
import { readFileSync } from 'node:fs';
|
|
import { resolve } from 'node:path';
|
|
|
|
const read = (path: string) => readFileSync(resolve(process.cwd(), path), 'utf8');
|
|
const migration = read('src/database/migrations/1788804000000-phase-d5-3-24-asset-context-history.ts');
|
|
const assets = read('src/asset-master/assets.service.ts');
|
|
const controller = read('src/asset-master/assets.controller.ts');
|
|
const temporal = read('src/asset-master/asset-temporal.service.ts');
|
|
const versionEntity = read('src/database/entities/asset-version.entity.ts');
|
|
|
|
test('D5.3.24 stores parent area and operator as explicit non-overlapping temporal context', () => {
|
|
assert.match(migration, /CREATE TABLE asset_context_history/);
|
|
assert.match(migration, /valid_from timestamptz NOT NULL/);
|
|
assert.match(migration, /valid_until timestamptz/);
|
|
assert.match(migration, /uq_asset_context_history_active/);
|
|
assert.match(migration, /operational_area_id IS NULL AND operator_company_id IS NULL/);
|
|
assert.match(migration, /REVOKE DELETE ON TABLE asset_context_history/);
|
|
});
|
|
|
|
test('D5.3.24 reconstructs previous context from immutable asset versions during migration', () => {
|
|
assert.match(migration, /FROM asset_versions version/);
|
|
assert.match(migration, /LAG\(NULLIF\(version\.snapshot #>> '\{parent,id\}'/);
|
|
assert.match(migration, /LEAD\(changes\.occurred_at\)/);
|
|
assert.match(migration, /Cambio de contexto reconstruido desde el historial versionado/);
|
|
});
|
|
|
|
test('D5.3.24 separates ordinary editing from an audited context transfer', () => {
|
|
assert.match(controller, /@Get\(':id\/context-history'\)/);
|
|
assert.match(controller, /@Post\(':id\/context'\)/);
|
|
assert.match(controller, /@RequirePermissions\('assets\.manage_context'\)/);
|
|
assert.match(assets, /ASSET_CONTEXT_CHANGE_REQUIRES_TRANSFER/);
|
|
assert.match(assets, /AssetVersionChangeType\.CONTEXT_CHANGED/);
|
|
assert.match(versionEntity, /CONTEXT_CHANGED = 'CONTEXT_CHANGED'/);
|
|
assert.match(assets, /AuditAction\.ASSET_CONTEXT_CHANGED/);
|
|
});
|
|
|
|
test('D5.3.24 closes the previous interval and appends the replacement without deleting history', () => {
|
|
assert.match(assets, /UPDATE asset_context_history[\s\S]*SET valid_until=\$2/);
|
|
assert.match(assets, /INSERT INTO asset_context_history/);
|
|
assert.match(assets, /change_reason/);
|
|
assert.doesNotMatch(assets, /DELETE FROM asset_context_history/);
|
|
});
|
|
|
|
test('D5.3.24 temporal reconstruction resolves context by effective validity at the requested instant', () => {
|
|
assert.match(temporal, /FROM asset_context_history history/);
|
|
assert.match(temporal, /history\.valid_from <= \$2/);
|
|
assert.match(temporal, /history\.valid_until IS NULL OR history\.valid_until > \$2/);
|
|
assert.match(temporal, /operatorCompany: context\.operatorCompany/);
|
|
});
|