metis-desk/apps/server/src/asset-inventory.integration.test.ts
David 37110d87ed feat: add optional registeredOn field to asset model and update related components
- Introduced a new optional field `registeredOn` to the asset input schema.
- Updated asset values and persistence layers to handle the new field.
- Enhanced tests to validate the behavior of the `registeredOn` field, including various date formats and null values.
- Modified UI components to display the registration date in asset details and forms.
- Added localization support for the new field in English and Spanish.
- Implemented helper functions for formatting the registration date.
- Created integration tests for reading and writing assets with the new field in the database.
2026-10-15 18:39:37 +02:00

130 lines
5.5 KiB
TypeScript

import assert from "node:assert/strict";
import { test } from "node:test";
import { Asset } from "../../../modules/assets/src/api/domain/asset.aggregate";
import {
AssetSpecification,
AssetValues,
} from "../../../modules/assets/src/api/domain/value-objects/asset-values.vo";
import {
readInventory,
writeInventory,
} from "../../../modules/assets/src/api/infrastructure/persistence/asset-inventory-relational";
import { AssetPersistenceMapper } from "../../../modules/assets/src/api/infrastructure/persistence/asset-persistence.mapper";
import { UniqueID } from "../../../packages/rdx-ddd/src/index";
import { createDatabase, readDatabaseEnvironment } from "./config/database";
// Tablas temporales de la conexión: no se modifica el esquema ni el inventario de la base de pruebas.
const TABLES = {
assets:
"id CHAR(36) PRIMARY KEY, companyId CHAR(36), templateId CHAR(36), siteId CHAR(36), brandId CHAR(36), name VARCHAR(100), registeredOn DATE, status VARCHAR(15), notes TEXT, brandDefined BOOLEAN, templateName VARCHAR(100), sectionsDefined BOOLEAN",
asset_revisions:
"id CHAR(36) PRIMARY KEY, assetId CHAR(36), actorId CHAR(36), recordedAt VARCHAR(30), position INTEGER, templateId CHAR(36), siteId CHAR(36), brandId CHAR(36), name VARCHAR(100), registeredOn DATE, status VARCHAR(15), notes TEXT, brandDefined BOOLEAN",
asset_specification_fields:
"assetId CHAR(36), fieldId CHAR(36), name VARCHAR(100), fieldType VARCHAR(12), unit VARCHAR(30), required BOOLEAN, sectionId CHAR(36), position INTEGER, PRIMARY KEY (assetId,fieldId)",
asset_specification_sections:
"assetId CHAR(36), id CHAR(36), name VARCHAR(100), description TEXT, position INTEGER, PRIMARY KEY (assetId,id)",
asset_specification_field_choices:
"assetId CHAR(36), fieldId CHAR(36), position INTEGER, value VARCHAR(100), PRIMARY KEY (assetId,fieldId,position)",
asset_characteristics:
"assetId CHAR(36), fieldId CHAR(36), valueType VARCHAR(12), textValue TEXT, numberValue DOUBLE, booleanValue BOOLEAN, dateValue DATE, PRIMARY KEY (assetId,fieldId)",
asset_revision_characteristics:
"revisionId CHAR(36), assetId CHAR(36), fieldId CHAR(36), valueType VARCHAR(12), textValue TEXT, numberValue DOUBLE, booleanValue BOOLEAN, dateValue DATE, PRIMARY KEY (revisionId,fieldId)",
};
test("MariaDB: lectura de activos e historial con fecha de alta ISO y nula sin columnas duplicadas", async () => {
const environment = readDatabaseEnvironment(process.env);
assert.equal(environment.NODE_ENV, "test");
assert.equal(environment.DB_NAME, "metis_desk_test");
const database = createDatabase(environment);
try {
await database.transaction(async (transaction) => {
try {
for (const [table, columns] of Object.entries(TABLES)) {
await database.query(`CREATE TEMPORARY TABLE ${table} (${columns})`, { transaction });
}
const companyId = UniqueID.generateNewID();
const actorId = UniqueID.generateNewID();
const templateId = UniqueID.generateNewID().toPrimitive();
const fieldId = UniqueID.generateNewID().toPrimitive();
const specification = AssetSpecification.create({
templateId,
templateName: "Equipo",
fields: [
{ fieldId, name: "Compra", type: "date", unit: "", choices: [], required: false },
],
});
assert.ok(specification.isSuccess);
const expected: Asset[] = [];
const mapper = new AssetPersistenceMapper();
for (const registeredOn of ["2024-02-29", null]) {
const values = AssetValues.create(
{
name: registeredOn ? "Con fecha" : "Sin fecha",
templateId,
siteId: null,
registeredOn,
status: "operational",
notes: "",
characteristics: { [fieldId]: "2024-01-15" },
},
specification.data
);
assert.ok(values.isSuccess);
const created = Asset.create(
companyId,
specification.data,
values.data,
actorId,
"2026-10-15T12:00:00.000Z"
);
assert.ok(created.isSuccess);
expected.push(created.data);
await writeInventory(
database,
mapper.toPersistence(created.data),
new Set(),
transaction
);
}
const inventory = await readInventory(database, companyId.toPrimitive(), transaction);
assert.equal(inventory.rows.length, 2);
assert.equal(inventory.revisions.length, 2);
for (const asset of expected) {
const row = inventory.rows.find((candidate) => candidate.id === asset.id.toPrimitive());
assert.ok(row);
const restored = mapper.toDomain(
row,
inventory.revisions.filter((revision) => revision.assetId === row.id)
);
assert.deepEqual(restored.values.toPrimitive(), asset.values.toPrimitive());
assert.deepEqual(
restored.history[0]?.values.toPrimitive(),
asset.history[0]?.values.toPrimitive()
);
}
assert.deepEqual(
await readInventory(database, UniqueID.generateNewID().toPrimitive(), transaction),
{ rows: [], revisions: [] }
);
} finally {
for (const table of Object.keys(TABLES).reverse()) {
await database.query(`DROP TEMPORARY TABLE IF EXISTS ${table}`, { transaction });
}
}
});
} finally {
await database.close();
}
});