2026-10-09 22:19:35 +00:00
import assert from "node:assert/strict" ;
import { randomUUID } from "node:crypto" ;
import type { Sequelize } from "sequelize" ;
import { QueryTypes } from "sequelize" ;
type Request = ( path : string , body? : unknown ) = > Promise < Response > ;
export async function verifyRecordDeletion ( request : Request , database : Sequelize ) {
const counts = async ( ) = > {
const result : number [ ] = [ ] ;
2026-10-10 10:01:58 +00:00
2026-10-09 22:19:35 +00:00
for ( const table of [
"company_contacts" ,
"assets" ,
"resources" ,
"work_profiles" ,
"access_items" ,
"mailbox_uses" ,
] ) {
const rows = await database . query < { count : number } > (
` SELECT COUNT(*) AS count FROM ${ table } ` ,
{ type : QueryTypes . SELECT }
) ;
2026-10-10 10:01:58 +00:00
2026-10-09 22:19:35 +00:00
result . push ( Number ( rows [ 0 ] ? . count ) ) ;
}
2026-10-10 10:01:58 +00:00
2026-10-09 22:19:35 +00:00
return result ;
} ;
2026-10-10 10:01:58 +00:00
2026-10-09 22:19:35 +00:00
const folderSubjects = await database . query < {
profileId : string ;
companyId : string ;
needId : string ;
actorId : string ;
} > (
"SELECT p.id AS profileId,p.companyId,i.id AS needId,u.id AS actorId FROM work_profiles p JOIN access_workspaces w ON w.companyId=p.companyId JOIN access_items i ON i.workspaceId=w.id CROSS JOIN users u WHERE JSON_UNQUOTE(JSON_EXTRACT(i.`values`, '$.kind'))='need' LIMIT 1" ,
{ type : QueryTypes . SELECT }
) ;
const subject = folderSubjects [ 0 ] ;
2026-10-10 10:01:58 +00:00
2026-10-09 22:19:35 +00:00
assert . ok ( subject , "Debe existir un perfil y una necesidad de recurso para la prueba de carpeta" ) ;
const mappingId = randomUUID ( ) ;
const values = JSON . stringify ( {
profileId : subject.profileId ,
needId : subject.needId ,
path : "\\\\SERVER\\Test" ,
driveLetter : null ,
state : "pending" ,
notes : null ,
} ) ;
2026-10-10 10:01:58 +00:00
2026-10-09 22:19:35 +00:00
await database . query (
"INSERT INTO folder_mappings (id,companyId,profileId,needId,`values`,currentKey,driveKey) VALUES (:id,:companyId,:profileId,:needId,:values,:currentKey,NULL)" ,
{
replacements : {
. . . subject ,
id : mappingId ,
values ,
currentKey : ` ${ subject . profileId } : ${ subject . needId } ` ,
} ,
}
) ;
await database . query (
"INSERT INTO folder_mapping_revisions (id,mappingId,position,`values`,actorId,recordedAt) VALUES (:id,:mappingId,0,:values,:actorId,:recordedAt)" ,
{
replacements : {
id : randomUUID ( ) ,
mappingId ,
values ,
actorId : subject.actorId ,
recordedAt : new Date ( ) . toISOString ( ) ,
} ,
}
) ;
const before = await counts ( ) ;
const specs = [
[
"application_configurations" ,
"application_configuration_revisions" ,
"configurationId" ,
"application-configurations" ,
] ,
[
"certificate_configurations" ,
"certificate_configuration_revisions" ,
"configurationId" ,
"certificate-configurations" ,
] ,
[
"printer_configurations" ,
"printer_configuration_revisions" ,
"configurationId" ,
"printer-configurations" ,
] ,
[
"mail_configurations" ,
"mail_configuration_revisions" ,
"configurationId" ,
"mail-configurations" ,
] ,
[ "folder_mappings" , "folder_mapping_revisions" , "mappingId" , "folder-mappings" ] ,
[ "asset_support" , "asset_support_revisions" , "connectionId" , "asset-support" ] ,
[ "asset_relationships" , null , null , "assets" ] ,
] as const ;
2026-10-10 10:01:58 +00:00
2026-10-09 22:19:35 +00:00
for ( const [ table , history , foreignKey , endpoint ] of specs ) {
const rows = await database . query < { id : string ; companyId : string ; assetId? : string } > (
` SELECT * FROM ${ table } ` ,
{ type : QueryTypes . SELECT }
) ;
2026-10-10 10:01:58 +00:00
2026-10-09 22:19:35 +00:00
assert . ok ( rows . length > 0 , ` No existen datos para probar ${ table } ` ) ;
// Todos los estados, incluidos los cerrados, admiten corregir un alta equivocada.
for ( const row of rows ) {
const suffix =
endpoint === "asset-support"
? ` ${ row . assetId } / ${ row . id } `
: endpoint === "assets"
? ` relationships/ ${ row . id } `
: row . id ;
const path = ` / ${ endpoint } / ${ row . companyId } / ${ suffix } /delete ` ;
2026-10-10 10:01:58 +00:00
2026-10-09 22:19:35 +00:00
assert . equal (
( await request ( ` / ${ endpoint } / ${ randomUUID ( ) } / ${ suffix } /delete ` , { } ) ) . status ,
404
) ;
assert . equal ( ( await request ( path . replace ( row . companyId , "invalid" ) , { } ) ) . status , 400 ) ;
assert . equal ( ( await request ( path , { unexpected : true } ) ) . status , 400 ) ;
assert . equal ( ( await request ( path , { } ) ) . status , 200 ) ;
assert . equal ( ( await request ( path , { } ) ) . status , 404 ) ;
const remaining = await database . query ( ` SELECT id FROM ${ table } WHERE id=:id ` , {
replacements : { id : row.id } ,
type : QueryTypes . SELECT ,
} ) ;
2026-10-10 10:01:58 +00:00
2026-10-09 22:19:35 +00:00
assert . equal ( remaining . length , 0 ) ;
if ( history && foreignKey ) {
const revisions = await database . query (
` SELECT id FROM ${ history } WHERE ${ foreignKey } =:id ` ,
{ replacements : { id : row.id } , type : QueryTypes . SELECT }
) ;
2026-10-10 10:01:58 +00:00
2026-10-09 22:19:35 +00:00
assert . equal ( revisions . length , 0 ) ;
}
}
}
assert . deepEqual ( await counts ( ) , before , "La eliminación debe conservar las entidades de origen" ) ;
}