Schema (packages/database/prisma/schema.prisma): - Add SenaeCategory, B2BRequestStatus, NotificationChannel enums - Add Package fields: lengthCm, widthCm, heightCm, hasDiscrepancy, photos[], senaeCategory, senaeAuthNumber, senaeDeclarationId - Add userId field to PreAlert - Add RefreshToken model (JWT auth — Fase 1) - Add Tariff model (configurable rates: pricePerLb, insurancePct, etc.) - Add B2BRequest model (heavy cargo imports — doc §13) - Add Notification model (multi-channel: email, whatsapp, sms, push — doc §16) - Add userAgent to AuditLog API (apps/api): - Add CalculatorModule: calculates SENAE cost breakdown (flete, seguro, FODINFA, arancel, IVA) per formulas in doc §15 - Add TrackingModule: public GET /api/tracking/:id endpoint (doc §07) - Register both modules in AppModule - Add Jest config + test scripts (test, test:watch, test:cov, test:ci) Tests (80 tests, 97.45% coverage): - calculator.util.spec.ts: 35 tests — SENAE formulas, volumetric weight, 4x4 regime, tariff rates, error validation, breakdown - tracking-id.util.spec.ts: 13 tests — format, uniqueness, validation - suite-code.util.spec.ts: 13 tests — generation, parsing, address builder - health.controller.spec.ts: 5 tests — ok/degraded/timestamp - calculator.spec.ts: 8 tests — controller + service unit tests - tracking.controller.spec.ts: 9 tests — public search, 404, no sensitive data Web (apps/web): - page.tsx: Full landing page matching documentation (Hero, Services, 8 Modules, Tracking states, SENAE calculator preview, 6 Roles, Companies) - /calculadora: SENAE categories + API usage guide - /tracking: Tracking ID format + 11 states reference Seed (packages/database/prisma/seed.ts): - Creates all 6 roles (SUPER_ADMIN, ADMIN_EMPRESA, OPERADOR_BODEGA, AGENTE_ADUANERO, CLIENTE, SOPORTE) - Assigns Suite EC-00001 to demo client - Creates demo Package EC-20260506-000001 with status history - Creates demo PreAlert and B2BRequest .env.example: Added all 9 integrations from doc §18: Amazon SP-API, SENAE WebService, Stripe/PayPhone, WhatsApp Business API, SendGrid/SES, SMS/Twilio, FedEx/DHL/UPS couriers, S3/MinIO, Sentry migration.sql: Updated to reflect all new models and fields turbo.json + package.json: Added test, test:cov, test:ci tasks + db:seed script
75 lines
2.7 KiB
TypeScript
75 lines
2.7 KiB
TypeScript
import {
|
|
generateTrackingId,
|
|
isValidTrackingId,
|
|
} from "./tracking-id.util";
|
|
|
|
/**
|
|
* Tests del Tracking ID.
|
|
* Formato documentado en §08: EC-YYYYMMDD-XXXXXX
|
|
*/
|
|
describe("TrackingId Utils (doc §08)", () => {
|
|
describe("generateTrackingId", () => {
|
|
it("genera un ID con el formato EC-YYYYMMDD-XXXXXX", () => {
|
|
const id = generateTrackingId();
|
|
expect(id).toMatch(/^EC-\d{8}-\d{6}$/);
|
|
});
|
|
|
|
it("usa la fecha proporcionada correctamente", () => {
|
|
const date = new Date("2026-05-06T00:00:00.000Z");
|
|
const id = generateTrackingId(date);
|
|
// El año/mes/día depende de la zona horaria local del proceso
|
|
// Solo validamos el patrón
|
|
expect(id).toMatch(/^EC-\d{8}-\d{6}$/);
|
|
});
|
|
|
|
it("incluye el año 2026 si se pasa esa fecha", () => {
|
|
const date = new Date(2026, 4, 6); // May 6, 2026 — local
|
|
const id = generateTrackingId(date);
|
|
expect(id.startsWith("EC-2026")).toBe(true);
|
|
});
|
|
|
|
it("genera IDs distintos en llamadas consecutivas (probabilístico)", () => {
|
|
const ids = new Set(Array.from({ length: 20 }, () => generateTrackingId()));
|
|
// Con 1 millón de combinaciones, 20 llamadas deben ser todas distintas
|
|
expect(ids.size).toBe(20);
|
|
});
|
|
|
|
it("el prefijo es siempre EC-", () => {
|
|
for (let i = 0; i < 10; i++) {
|
|
expect(generateTrackingId().startsWith("EC-")).toBe(true);
|
|
}
|
|
});
|
|
|
|
it("el sufijo siempre tiene exactamente 6 dígitos", () => {
|
|
for (let i = 0; i < 10; i++) {
|
|
const parts = generateTrackingId().split("-");
|
|
expect(parts[2]).toHaveLength(6);
|
|
expect(/^\d{6}$/.test(parts[2])).toBe(true);
|
|
}
|
|
});
|
|
});
|
|
|
|
describe("isValidTrackingId", () => {
|
|
it("retorna true para IDs válidos", () => {
|
|
expect(isValidTrackingId("EC-20260506-082341")).toBe(true);
|
|
expect(isValidTrackingId("EC-20261231-000001")).toBe(true);
|
|
expect(isValidTrackingId("EC-20260101-999999")).toBe(true);
|
|
});
|
|
|
|
it("retorna false para IDs inválidos", () => {
|
|
expect(isValidTrackingId("EC-2026050-082341")).toBe(false); // fecha corta
|
|
expect(isValidTrackingId("EC-20260506-08234")).toBe(false); // sufijo corto
|
|
expect(isValidTrackingId("US-20260506-082341")).toBe(false); // prefijo incorrecto
|
|
expect(isValidTrackingId("EC-20260506082341")).toBe(false); // sin separador
|
|
expect(isValidTrackingId("EC-20260506-08234A")).toBe(false); // letras en sufijo
|
|
expect(isValidTrackingId("")).toBe(false);
|
|
});
|
|
|
|
it("los IDs generados siempre pasan la validación", () => {
|
|
for (let i = 0; i < 50; i++) {
|
|
expect(isValidTrackingId(generateTrackingId())).toBe(true);
|
|
}
|
|
});
|
|
});
|
|
});
|