131 lines
6.5 KiB
TypeScript
131 lines
6.5 KiB
TypeScript
import assert from 'node:assert/strict';
|
|
import fs from 'node:fs';
|
|
import path from 'node:path';
|
|
import test from 'node:test';
|
|
import { getMetadataArgsStorage, type QueryRunner } from 'typeorm';
|
|
import { InspectionVisit } from '../../src/database/entities/inspection-visit.entity';
|
|
import { F4RemoveInspectionLegacyFields1790000500000 } from '../../src/database/migrations/1790000500000-f4-remove-inspection-legacy-fields';
|
|
|
|
const root = process.cwd();
|
|
const read = (relative: string) => fs.readFileSync(path.join(root, relative), 'utf8');
|
|
|
|
test('F4: InspectionVisit no vuelve a mapear title ni plannedEndAt', () => {
|
|
const metadata = getMetadataArgsStorage();
|
|
const columns = metadata.columns
|
|
.filter((column) => column.target === InspectionVisit)
|
|
.map((column) => column.propertyName);
|
|
const indexes = metadata.indices
|
|
.filter((index) => index.target === InspectionVisit)
|
|
.map((index) => index.name);
|
|
|
|
assert.equal(columns.includes('title'), false);
|
|
assert.equal(columns.includes('plannedEndAt'), false);
|
|
assert.equal(columns.includes('plannedStartAt'), true);
|
|
assert.equal(indexes.includes('idx_inspection_visits_planned_dates'), false);
|
|
assert.equal(indexes.includes('idx_inspection_visits_planned_start_at'), true);
|
|
});
|
|
|
|
test('F4: los consumidores backend activos no consultan campos físicos legacy de la Inspección', () => {
|
|
const consumers = [
|
|
read('src/inspection-acts/inspection-acts.service.ts'),
|
|
read('src/inspection-findings/inspection-findings.service.ts'),
|
|
read('src/inspection-findings/inspection-finding-worklist.service.ts'),
|
|
read('src/inspection-verifications/inspection-verifications.service.ts'),
|
|
read('src/asset-master/assets.service.ts'),
|
|
];
|
|
|
|
for (const consumer of consumers) {
|
|
assert.doesNotMatch(consumer, /visit\.title/);
|
|
assert.doesNotMatch(consumer, /verification_target\.title/);
|
|
assert.doesNotMatch(consumer, /\bvisitTitle\b/);
|
|
assert.doesNotMatch(consumer, /\bplanned_end_at\b/);
|
|
}
|
|
});
|
|
|
|
test('F4: el contrato WEB de Inspecciones no reintroduce title, visitTitle ni plannedEndAt', () => {
|
|
const webApi = read('../web-v2/src/lib/api.ts');
|
|
const start = webApi.indexOf('export type InspectionVisitStatus');
|
|
const end = webApi.indexOf('let csrfToken: string | null');
|
|
assert.notEqual(start, -1);
|
|
assert.notEqual(end, -1);
|
|
const inspectionContract = webApi.slice(start, end);
|
|
const visitListStart = inspectionContract.indexOf('export interface InspectionVisitListItem');
|
|
const visitListEnd = inspectionContract.indexOf('export type InspectionVerificationOutcome');
|
|
assert.notEqual(visitListStart, -1);
|
|
assert.notEqual(visitListEnd, -1);
|
|
const visitListContract = inspectionContract.slice(visitListStart, visitListEnd);
|
|
|
|
assert.doesNotMatch(visitListContract, /\btitle\s*:/);
|
|
assert.doesNotMatch(visitListContract, /\bplannedEndAt\b/);
|
|
assert.doesNotMatch(inspectionContract, /\bvisitTitle\b/);
|
|
assert.doesNotMatch(inspectionContract, /\bplannedEndAt\b/);
|
|
});
|
|
|
|
test('F4: el contrato Android de Inspecciones no reintroduce title ni plannedEndAt', () => {
|
|
const android = read('../android-app/app/src/main/java/com/korexlabs/dhinspeccion/data/DhMobile.kt');
|
|
const summaryStart = android.indexOf('data class VisitSummary(');
|
|
const summaryEnd = android.indexOf('data class VisitMeta(');
|
|
const detailStart = android.indexOf('data class VisitDetail(');
|
|
const detailEnd = android.indexOf('// ---------- Field inventory ----------');
|
|
assert.notEqual(summaryStart, -1);
|
|
assert.notEqual(summaryEnd, -1);
|
|
assert.notEqual(detailStart, -1);
|
|
assert.notEqual(detailEnd, -1);
|
|
|
|
const visitContracts = `${android.slice(summaryStart, summaryEnd)}\n${android.slice(detailStart, detailEnd)}`;
|
|
assert.doesNotMatch(visitContracts, /\bval title\s*:/);
|
|
assert.doesNotMatch(visitContracts, /\bplannedEndAt\b/);
|
|
});
|
|
|
|
test('F4: la migración respalda datos antes de retirar columnas legacy', async () => {
|
|
const statements: string[] = [];
|
|
const queryRunner = {
|
|
query: async (sql: string) => {
|
|
statements.push(sql.replace(/\s+/g, ' ').trim());
|
|
return [];
|
|
},
|
|
} as unknown as QueryRunner;
|
|
|
|
await new F4RemoveInspectionLegacyFields1790000500000().up(queryRunner);
|
|
const sql = statements.join('\n');
|
|
|
|
assert.match(sql, /CREATE TABLE IF NOT EXISTS f4_inspection_visit_legacy_fields_backup/i);
|
|
assert.match(sql, /INSERT INTO f4_inspection_visit_legacy_fields_backup/i);
|
|
assert.match(sql, /SELECT id, title, planned_end_at, CURRENT_TIMESTAMP FROM inspection_visits/i);
|
|
assert.match(sql, /DROP COLUMN IF EXISTS planned_end_at/i);
|
|
assert.match(sql, /DROP COLUMN IF EXISTS title/i);
|
|
assert.match(sql, /DROP CONSTRAINT IF EXISTS chk_inspection_visits_planned_dates/i);
|
|
assert.match(sql, /CREATE INDEX IF NOT EXISTS idx_inspection_visits_planned_start_at/i);
|
|
assert.doesNotMatch(sql, /CREATE INDEX IF NOT EXISTS idx_inspection_visits_planned_dates/i);
|
|
|
|
const backupAt = sql.indexOf('INSERT INTO f4_inspection_visit_legacy_fields_backup');
|
|
const dropAt = sql.indexOf('DROP COLUMN IF EXISTS planned_end_at');
|
|
assert.ok(backupAt >= 0 && dropAt > backupAt, 'el respaldo debe ocurrir antes de eliminar columnas');
|
|
});
|
|
|
|
test('F4: el rollback restaura valores históricos exactos y cubre filas nuevas', async () => {
|
|
const statements: string[] = [];
|
|
const queryRunner = {
|
|
query: async (sql: string) => {
|
|
statements.push(sql.replace(/\s+/g, ' ').trim());
|
|
return [];
|
|
},
|
|
} as unknown as QueryRunner;
|
|
|
|
await new F4RemoveInspectionLegacyFields1790000500000().down(queryRunner);
|
|
const sql = statements.join('\n');
|
|
|
|
assert.match(sql, /ADD COLUMN IF NOT EXISTS title varchar\(200\)/i);
|
|
assert.match(sql, /ADD COLUMN IF NOT EXISTS planned_end_at timestamptz/i);
|
|
assert.match(sql, /UPDATE inspection_visits visit SET title = backup\.title, planned_end_at = backup\.planned_end_at FROM f4_inspection_visit_legacy_fields_backup backup WHERE backup\.visit_id = visit\.id/i);
|
|
assert.match(sql, /UPDATE inspection_visits SET title = code WHERE title IS NULL/i);
|
|
assert.match(sql, /ALTER COLUMN title SET NOT NULL/i);
|
|
assert.match(sql, /ADD CONSTRAINT chk_inspection_visits_planned_dates CHECK/i);
|
|
assert.match(sql, /CREATE INDEX IF NOT EXISTS idx_inspection_visits_planned_dates/i);
|
|
assert.match(sql, /DROP TABLE IF EXISTS f4_inspection_visit_legacy_fields_backup/i);
|
|
|
|
const restoreAt = sql.indexOf('UPDATE inspection_visits visit SET title = backup.title');
|
|
const backupDropAt = sql.indexOf('DROP TABLE IF EXISTS f4_inspection_visit_legacy_fields_backup');
|
|
assert.ok(restoreAt >= 0 && backupDropAt > restoreAt, 'el respaldo sólo se elimina después de restaurar los datos');
|
|
});
|