#!/usr/bin/env node /** * Test script to verify tenant isolation for lista propia flags * when recycling fast-check results between tenants */ import mongoose from 'mongoose'; import { Result } from './src/models/Result.js'; import { Lpalto } from './src/models/Lpalto.js'; import { Lpmedio } from './src/models/Lpmedio.js'; // Test configuration const TEST_RUT = '12345678-9'; const TENANT_A = 'tenant_a_test'; const TENANT_B = 'tenant_b_test'; async function connectDB() { try { await mongoose.connect(process.env.MONGODB_URI || 'mongodb://localhost:27017/duxiter'); console.log('โœ… Connected to MongoDB'); } catch (error) { console.error('โŒ MongoDB connection error:', error); process.exit(1); } } async function cleanup() { console.log('\n๐Ÿงน Cleaning up test data...'); // Remove test results await Result.deleteMany({ rut: { $in: [TEST_RUT, TEST_RUT.replace('-', '')] }, tenantId: { $in: [TENANT_A, TENANT_B] } }); // Remove test lista propia entries await Lpalto.deleteMany({ rut: { $in: [TEST_RUT, TEST_RUT.replace('-', '')] }, tenant: { $in: [TENANT_A, TENANT_B] } }); await Lpmedio.deleteMany({ rut: { $in: [TEST_RUT, TEST_RUT.replace('-', '')] }, tenant: { $in: [TENANT_A, TENANT_B] } }); console.log('โœ… Cleanup completed'); } async function setupTestData() { console.log('\n๐Ÿ“ Setting up test data...'); // Create lista propia entries for TENANT_A only await Lpalto.create({ rut: TEST_RUT.replace('-', ''), tenant: TENANT_A, razonSocial: 'Test Company Alto Impacto', createdAt: new Date() }); await Lpmedio.create({ rut: TEST_RUT.replace('-', ''), tenant: TENANT_A, razonSocial: 'Test Company Mediano Impacto', createdAt: new Date() }); // Create a result for TENANT_A with lista propia flags set to true const resultTenantA = await Result.create({ rut: TEST_RUT, tenantId: TENANT_A, markdownResume: 'Test result for Tenant A', sheriffLogData: { summaryData: { data: { lpaltosDetected: true, lpmediosDetected: true } } }, createdAt: new Date() }); console.log(`โœ… Created test data for ${TENANT_A}`); console.log(` - Lpalto entry: ${TEST_RUT}`); console.log(` - Lpmedio entry: ${TEST_RUT}`); console.log(` - Result with lpaltosDetected: true, lpmediosDetected: true`); return resultTenantA; } async function testResultRecycling() { console.log('\n๐Ÿ”„ Testing result recycling with tenant isolation...'); // Simulate what happens when TENANT_B requests the same RUT // This should trigger the recycling logic in rutController.ts // First, verify that TENANT_B has no lista propia entries const lpaltosForTenantB = await Lpalto.find({ rut: TEST_RUT.replace('-', ''), tenant: TENANT_B }); const lpmediosForTenantB = await Lpmedio.find({ rut: TEST_RUT.replace('-', ''), tenant: TENANT_B }); console.log(`๐Ÿ“Š TENANT_B lista propia entries:`); console.log(` - Lpaltos: ${lpaltosForTenantB.length}`); console.log(` - Lpmedios: ${lpmediosForTenantB.length}`); // Find the existing result (from TENANT_A) const existingResult = await Result.findOne({ $or: [{ rut: TEST_RUT }, { rut: TEST_RUT.replace('-', '') }] }).sort({ createdAt: -1 }); if (!existingResult) { throw new Error('No existing result found for recycling test'); } console.log(`๐Ÿ“‹ Found existing result from tenant: ${existingResult.tenantId}`); console.log(` - Original lpaltosDetected: ${existingResult.sheriffLogData?.summaryData?.data?.lpaltosDetected}`); console.log(` - Original lpmediosDetected: ${existingResult.sheriffLogData?.summaryData?.data?.lpmediosDetected}`); // Simulate the recycling logic from rutController.ts if (existingResult.tenantId !== TENANT_B) { console.log('\n๐Ÿ”„ Simulating result recycling for TENANT_B...'); // Create a copy of the result object (excluding _id) const resultObject = existingResult.toObject(); delete resultObject._id; // Set the new tenant resultObject.tenantId = TENANT_B; // CRITICAL: Recalculate tenant-specific lista propia flags const cleanRut = TEST_RUT.replace(/\./g, '').replace(/-/g, ''); try { // Query lpaltos collection for current tenant const lpaltosRecords = await Lpalto.find({ rut: cleanRut, tenant: TENANT_B }); // Query lpmedios collection for current tenant const lpmediosRecords = await Lpmedio.find({ rut: cleanRut, tenant: TENANT_B }); // Update the flags based on current tenant's data if (resultObject.sheriffLogData?.summaryData?.data) { resultObject.sheriffLogData.summaryData.data.lpaltosDetected = lpaltosRecords.length > 0; resultObject.sheriffLogData.summaryData.data.lpmediosDetected = lpmediosRecords.length > 0; console.log(`โœ… Recalculated lista propia flags for ${TENANT_B}:`); console.log(` - lpaltosDetected: ${lpaltosRecords.length > 0} (${lpaltosRecords.length} records found)`); console.log(` - lpmediosDetected: ${lpmediosRecords.length > 0} (${lpmediosRecords.length} records found)`); } else { console.log('โš ๏ธ No sheriffLogData.summaryData.data found in result'); return false; } // Save the recycled result for TENANT_B const recycledResult = await Result.create(resultObject); console.log(`โœ… Created recycled result for ${TENANT_B} with ID: ${recycledResult._id}`); // Verify the recycled result has correct flags const verificationResult = await Result.findById(recycledResult._id); if (!verificationResult) { console.log('โŒ Could not find recycled result for verification'); return false; } const finalLpaltosDetected = verificationResult.sheriffLogData?.summaryData?.data?.lpaltosDetected; const finalLpmediosDetected = verificationResult.sheriffLogData?.summaryData?.data?.lpmediosDetected; console.log('\n๐Ÿ” Verification of recycled result:'); console.log(` - Final lpaltosDetected: ${finalLpaltosDetected}`); console.log(` - Final lpmediosDetected: ${finalLpmediosDetected}`); // Test assertions const expectedLpaltos = false; // TENANT_B has no lpaltos entries const expectedLpmedios = false; // TENANT_B has no lpmedios entries if (finalLpaltosDetected === expectedLpaltos && finalLpmediosDetected === expectedLpmedios) { console.log('โœ… TEST PASSED: Lista propia flags correctly recalculated for tenant isolation'); return true; } else { console.log('โŒ TEST FAILED: Lista propia flags not correctly recalculated'); console.log(` Expected: lpaltosDetected=${expectedLpaltos}, lpmediosDetected=${expectedLpmedios}`); console.log(` Actual: lpaltosDetected=${finalLpaltosDetected}, lpmediosDetected=${finalLpmediosDetected}`); return false; } } catch (error) { console.error('โŒ Error during lista propia recalculation:', error); return false; } } return false; } async function runTest() { console.log('๐Ÿงช Starting tenant isolation test for lista propia flags...'); console.log(`๐Ÿ“‹ Test RUT: ${TEST_RUT}`); console.log(`๐Ÿข Tenant A: ${TENANT_A} (has lista propia entries)`); console.log(`๐Ÿข Tenant B: ${TENANT_B} (no lista propia entries)`); try { await connectDB(); await cleanup(); await setupTestData(); const testPassed = await testResultRecycling(); console.log('\n' + '='.repeat(60)); if (testPassed) { console.log('๐ŸŽ‰ ALL TESTS PASSED: Tenant isolation working correctly!'); console.log('โœ… Lista propia flags are properly recalculated when recycling results'); } else { console.log('๐Ÿ’ฅ TEST FAILED: Tenant isolation not working correctly'); console.log('โŒ Lista propia flags are not being recalculated properly'); } console.log('='.repeat(60)); } catch (error) { console.error('โŒ Test execution error:', error); } finally { await cleanup(); await mongoose.disconnect(); console.log('๐Ÿ‘‹ Disconnected from MongoDB'); } } // Run the test runTest().catch(console.error);