218 lines
6.7 KiB
JavaScript
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);
|
|
});
|