require('dotenv').config(); const { PrismaClient } = require('@prisma/client'); const { PrismaPg } = require('@prisma/adapter-pg'); const { Pool } = require('pg'); const dbUrl = new URL(process.env.DATABASE_URL); const pool = new Pool({ host: dbUrl.hostname, port: dbUrl.port ? parseInt(dbUrl.port) : 5432, user: decodeURIComponent(dbUrl.username), password: decodeURIComponent(dbUrl.password), database: dbUrl.pathname.substring(1).split('?')[0], ssl: false }); const adapter = new PrismaPg(pool); const prisma = new PrismaClient({ adapter }); async function runTests() { console.log("=== STARTING PHASE 6 RLS INTEGRATION TEST ==="); // 1. Fetch seeded users (Run as ADMIN role to bypass RLS) const { users, plan } = await prisma.$transaction(async (tx) => { await tx.$executeRawUnsafe(`SET LOCAL app.current_user_role = 'admin';`); const users = await tx.user.findMany({ include: { hotel: true } }); // Ensure we have at least one active compensation plan let plan = await tx.compensationPlan.findFirst(); if (!plan) { const adminUser = users.find(u => u.role === 'admin'); plan = await tx.compensationPlan.create({ data: { name: 'Test Plan', code: 'TEST-PLAN', validityStart: new Date(), type: 'PERCENTAGE', status: 'ACTIVE', createdBy: adminUser.id } }); } return { users, plan }; }); const adminUser = users.find(u => u.username === 'admin'); const gerenteMde = users.find(u => u.username === 'gerente_mde'); const colaboradorMde = users.find(u => u.username === 'colaborador_mde'); const liderCtg = users.find(u => u.username === 'lider_ctg'); if (!adminUser || !gerenteMde || !colaboradorMde || !liderCtg) { console.error("Error: Could not find all required seeded users."); process.exit(1); } let passed = 0; let failed = 0; function assert(condition, message) { if (condition) { console.log(` ✓ PASS: ${message}`); passed++; } else { console.error(` ✗ FAIL: ${message}`); failed++; } } // Helper to run query with context async function runWithContext(user, queryFn) { return prisma.$transaction(async (tx) => { await tx.$executeRawUnsafe(`SET LOCAL app.current_user_id = '${user.id}';`); await tx.$executeRawUnsafe(`SET LOCAL app.current_user_role = '${user.role}';`); await tx.$executeRawUnsafe(`SET LOCAL app.current_hotel_id = '${user.hotelId}';`); await tx.$executeRawUnsafe(`SET LOCAL app.current_region_id = '${user.hotel.regionId}';`); return queryFn(tx); }); } let settlementMde, settlementCtg; try { // 2. Clean up old test settlements await runWithContext(adminUser, async (tx) => { await tx.settlement.deleteMany({ where: { period: '2026-99' } }); }); // 3. Create test settlements (as Admin) settlementMde = await runWithContext(adminUser, async (tx) => { return tx.settlement.create({ data: { period: '2026-99', planId: plan.id, userId: colaboradorMde.id, salesAmount: 10000.00, goalAmount: 8000.00, achievementPercentage: 1.25, calculatedCommission: 1200.00, calculatedBonus: 0, adjustmentAmount: 0, totalPayout: 1200.00, status: 'APPROVED' } }); }); settlementCtg = await runWithContext(adminUser, async (tx) => { return tx.settlement.create({ data: { period: '2026-99', planId: plan.id, userId: liderCtg.id, salesAmount: 15000.00, goalAmount: 12000.00, achievementPercentage: 1.25, calculatedCommission: 1800.00, calculatedBonus: 0, adjustmentAmount: 0, totalPayout: 1800.00, status: 'APPROVED' } }); }); // 4. Test RLS on Settlements for Colaborador MDE const colabSettlements = await runWithContext(colaboradorMde, async (tx) => { return tx.settlement.findMany({ where: { period: '2026-99' } }); }); assert( colabSettlements.length === 1 && colabSettlements[0].id === settlementMde.id, "Colaborador MDE can only see their own history record and not Lider CTG's record" ); // 5. Test RLS on Settlements for Gerente MDE (should see MDE settlements) const gerenteSettlements = await runWithContext(gerenteMde, async (tx) => { return tx.settlement.findMany({ where: { period: '2026-99' } }); }); assert( gerenteSettlements.length === 1 && gerenteSettlements[0].id === settlementMde.id, "Gerente MDE can see settlements in their hotel but not CTG's record" ); // 6. Test RLS on Settlements for Admin (should see both) const adminSettlements = await runWithContext(adminUser, async (tx) => { return tx.settlement.findMany({ where: { period: '2026-99' } }); }); assert( adminSettlements.length === 2, "Admin can see all history records" ); // 7. Verify audit_logs table write-only restriction const log = await runWithContext(adminUser, async (tx) => { return tx.auditLog.create({ data: { userId: adminUser.id, action: 'CREATE', targetTable: 'compensation_plans', targetId: 9999, ipAddress: '127.0.0.1' } }); }); let updateFailed = false; try { await runWithContext(adminUser, async (tx) => { await tx.auditLog.update({ where: { id: log.id }, data: { ipAddress: '192.168.1.1' } }); }); } catch (e) { updateFailed = true; } assert(updateFailed, "AuditLog update is blocked by RLS even for admin"); let deleteFailed = false; try { await runWithContext(adminUser, async (tx) => { await tx.auditLog.delete({ where: { id: log.id } }); }); } catch (e) { deleteFailed = true; } assert(deleteFailed, "AuditLog delete is blocked by RLS even for admin"); } catch (err) { console.error("Error in Phase 6 RLS integration tests:", err); failed++; } finally { // 8. Cleanup console.log("\nCleaning up test settlements..."); try { await runWithContext(adminUser, async (tx) => { await tx.settlement.deleteMany({ where: { id: { in: [settlementMde?.id, settlementCtg?.id].filter(Boolean) } } }); }); } catch (err) { console.error("Cleanup failed", err); } } console.log(`\n=== PHASE 6 TEST RESULTS: ${passed} PASSED, ${failed} FAILED ===`); if (failed > 0) { process.exit(1); } else { process.exit(0); } } runTests().catch(err => { console.error("Fatal test runner error:", err); process.exit(1); });