semillero-special-hotel/prisma/test-phase6-rls.js

218 lines
6.7 KiB
JavaScript

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);
});