diff --git a/package.json b/package.json index 589f94f..c3c7f8e 100644 --- a/package.json +++ b/package.json @@ -22,23 +22,7 @@ "test:e2e:ci": "cross-env NODE_ENV=test E2E_RESET_DB=1 jest --config ./test/jest-e2e.json --runInBand --verbose", "prisma:generate": "prisma generate", "db:migrate": "prisma migrate dev --name init", - "db:reset": "prisma migrate reset --force", - "seed:01": "tsx prisma/mock-seeds-scripts/01-bankcodes.ts", - "seed:02": "tsx prisma/mock-seeds-scripts/02-users.ts", - "seed:03": "tsx prisma/mock-seeds-scripts/03-employees.ts", - "seed:04": "tsx prisma/mock-seeds-scripts/04-customers.ts", - "seed:05": "tsx prisma/mock-seeds-scripts/05-employees-archive.ts", - "seed:06": "tsx prisma/mock-seeds-scripts/06-customers-archive.ts", - "seed:07": "tsx prisma/mock-seeds-scripts/07-leave-requests-future.ts", - "seed:08": "tsx prisma/mock-seeds-scripts/08-leave-requests-archive.ts", - "seed:09": "tsx prisma/mock-seeds-scripts/09-timesheets.ts", - "seed:10": "tsx prisma/mock-seeds-scripts/10-shifts.ts", - "seed:11": "tsx prisma/mock-seeds-scripts/11-shifts-archive.ts", - "seed:12": "tsx prisma/mock-seeds-scripts/12-expenses.ts", - "seed:13": "tsx prisma/mock-seeds-scripts/13-expenses-archive.ts", - "seed:14": "tsx prisma/mock-seeds-scripts/14-oauth-sessions.ts", - "seed:all": "npm run seed:01 && npm run seed:02 && npm run seed:03 && npm run seed:09 && npm run seed:10 && npm run seed:12 && npm run seed:14", - "db:reseed": "npm run db:reset && npm run seed:all" + "db:reset": "prisma migrate reset --force" }, "dependencies": { "@nestjs/common": "^11.0.1", diff --git a/prisma/mock-seeds-scripts/01-bankCodes.ts b/prisma/mock-seeds-scripts/01-bankCodes.ts deleted file mode 100644 index 2b1ade9..0000000 --- a/prisma/mock-seeds-scripts/01-bankCodes.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { PrismaClient } from '@prisma/client'; - -const prisma = new PrismaClient(); - -async function main() { - const presets = [ - // type, categorie, modifier, bank_code - ['REGULAR' ,'SHIFT' , 1.0 , 'G1' ], - ['OVERTIME' ,'SHIFT' , 2 , 'G43' ], - ['EMERGENCY' ,'SHIFT' , 2 , 'G48' ], - ['EVENING' ,'SHIFT' , 1.25 , 'G56' ], - ['SICK' ,'SHIFT' , 1.0 , 'G105'], - ['HOLIDAY' ,'SHIFT' , 1.0 , 'G104'], - ['VACATION' ,'SHIFT' , 1.0 , 'G305'], - ['ON_CALL' ,'EXPENSE' , 1.0 , 'G202'], - ['COMMISSION' ,'EXPENSE' , 1.0 , 'G234'], - ['PER_DIEM' ,'EXPENSE' , 1.0 , 'G502'], - ['MILEAGE' ,'EXPENSE' , 0.72 , 'G503'], - ['EXPENSES' ,'EXPENSE' , 1.0 , 'G517'], - ]; - - await prisma.bankCodes.createMany({ - data: presets.map(([type, categorie, modifier, bank_code]) => ({ - type: String(type), - categorie: String(categorie), - modifier: Number(modifier), - bank_code: String(bank_code), - })), - skipDuplicates: true, - }); - - console.log('✓ BankCodes: 9 rows'); -} - -main().finally(() => prisma.$disconnect()); diff --git a/prisma/mock-seeds-scripts/02-users.ts b/prisma/mock-seeds-scripts/02-users.ts deleted file mode 100644 index 81e30da..0000000 --- a/prisma/mock-seeds-scripts/02-users.ts +++ /dev/null @@ -1,200 +0,0 @@ -import { PrismaClient, Roles } from '@prisma/client'; - -const prisma = new PrismaClient(); - -// base sans underscore, en string -const BASE_PHONE = '1100000000'; - -function emailFor(i: number) { - return `user${i + 1}@example.test`; -} - -async function main() { - type UserSeed = { - first_name: string; - last_name: string; - email: string; - phone_number: string; - residence?: string | null; - role: Roles; - }; - - const usersData: UserSeed[] = []; - - const firstNames = ['Alex', 'Sam', 'Chris', 'Jordan', 'Taylor', 'Morgan', 'Jamie', 'Robin', 'Avery', 'Casey']; - const lastNames = ['Smith', 'Johnson', 'Williams', 'Brown', 'Jones', 'Miller', 'Davis', 'Wilson', 'Taylor', 'Clark']; - - const pick = (arr: T[]) => arr[Math.floor(Math.random() * arr.length)]; - - /** - * Objectif total: 50 users - * - 39 employés génériques (dont ADMIN=1, SUPERVISOR=3, HR=1, ACCOUNTING=1, EMPLOYEE=33) - * - +1 superviseur spécial "User Test" (=> 40 employés) - * - 10 customers - */ - const rolesForEmployees39: Roles[] = [ - Roles.ADMIN, // 1 - ...Array(3).fill(Roles.SUPERVISOR), // 3 supervisors (le 4e sera "User Test") - Roles.HR, // 1 - Roles.ACCOUNTING, // 1 - ...Array(33).fill(Roles.EMPLOYEE), // 33 - // total = 39 - ]; - - // --- 39 employés génériques: user1..user39@example.test - for (let i = 0; i < 39; i++) { - const fn = pick(firstNames); - const ln = pick(lastNames); - usersData.push({ - first_name: fn, - last_name: ln, - email: emailFor(i), - phone_number: BASE_PHONE + i.toString(), - residence: Math.random() < 0.5 ? 'QC' : 'ON', - role: rolesForEmployees39[i], - }); - } - - // --- 10 customers: user40..user49@example.test - for (let i = 39; i < 49; i++) { - const fn = pick(firstNames); - const ln = pick(lastNames); - usersData.push({ - first_name: fn, - last_name: ln, - email: emailFor(i), - phone_number: BASE_PHONE + i.toString(), - residence: Math.random() < 0.5 ? 'QC' : 'ON', - role: Roles.CUSTOMER, - }); - } - - // 1) Insert des 49 génériques (skipDuplicates pour rejouer le seed sans erreurs) - await prisma.users.createMany({ data: usersData, skipDuplicates: true }); - - // 2) Upsert du superviseur spécial "User Test" - const specialEmail = 'user@targointernet.com'; - const specialUser = await prisma.users.upsert({ - where: { email: specialEmail }, - update: { - first_name: 'User', - last_name: 'Test', - role: Roles.SUPERVISOR, - residence: 'QC', - phone_number: BASE_PHONE + '999', - }, - create: { - first_name: 'User', - last_name: 'Test', - email: specialEmail, - role: Roles.SUPERVISOR, - residence: 'QC', - phone_number: BASE_PHONE + '999', - }, - }); - - // 3) Créer/mettre à jour les entrées Employees pour tous les rôles employés - const employeeUsers = await prisma.users.findMany({ - where: { role: { in: [Roles.ADMIN, Roles.SUPERVISOR, Roles.HR, Roles.ACCOUNTING, Roles.EMPLOYEE] } }, - orderBy: { email: 'asc' }, - }); - - const firstWorkDay = new Date('2025-01-06'); // à adapter à ton contexte - - for (let i = 0; i < employeeUsers.length; i++) { - const u = employeeUsers[i]; - await prisma.employees.upsert({ - where: { user_id: u.id }, - update: { - is_supervisor: u.role === Roles.SUPERVISOR, - job_title: u.role, - }, - create: { - user_id: u.id, - is_supervisor: u.role === Roles.SUPERVISOR, - external_payroll_id: 1000 + i, // à adapter - company_code: 1, // à adapter - first_work_day: firstWorkDay, - job_title: u.role, - }, - }); - } - - // 4) Répartition des 33 EMPLOYEE sur 4 superviseurs: 8/8/8/9 (9 pour User Test) - const supervisors = await prisma.employees.findMany({ - where: { is_supervisor: true, user: { role: Roles.SUPERVISOR } }, - include: { user: true }, - orderBy: { id: 'asc' }, - }); - - const userTestSupervisor = supervisors.find((s) => s.user.email === specialEmail); - if (!userTestSupervisor) { - throw new Error('Employee(User Test) introuvable — vérifie le upsert Users/Employees.'); - } - - const plainEmployees = await prisma.employees.findMany({ - where: { is_supervisor: false, user: { role: Roles.EMPLOYEE } }, - orderBy: { id: 'asc' }, - }); - - // Si la configuration est bien 4 superviseurs + 33 employés, on force 8/8/8/9 avec 9 pour User Test. - if (supervisors.length === 4 && plainEmployees.length === 33) { - const others = supervisors.filter((s) => s.id !== userTestSupervisor.id); - // ordre: autres (3) puis User Test en dernier (reçoit 9) - const ordered = [...others, userTestSupervisor]; - - const chunks = [ - plainEmployees.slice(0, 8), // -> sup 0 - plainEmployees.slice(8, 16), // -> sup 1 - plainEmployees.slice(16, 24), // -> sup 2 - plainEmployees.slice(24, 33), // -> sup 3 (User Test) = 9 - ]; - - for (let b = 0; b < chunks.length; b++) { - const sup = ordered[b]; - for (const emp of chunks[b]) { - await prisma.employees.update({ - where: { id: emp.id }, - data: { supervisor_id: sup.id }, - }); - } - } - } else { - // fallback: distribution round-robin si la config diffère - console.warn( - `Répartition fallback (round-robin). Supervisors=${supervisors.length}, Employees=${plainEmployees.length}` - ); - const others = supervisors.filter((s) => s.id !== userTestSupervisor.id); - const ordered = [...others, userTestSupervisor]; - for (let i = 0; i < plainEmployees.length; i++) { - const sup = ordered[i % ordered.length]; - await prisma.employees.update({ - where: { id: plainEmployees[i].id }, - data: { supervisor_id: sup.id }, - }); - } - } - - // 5) Sanity checks - const totalUsers = await prisma.users.count(); - const supCount = await prisma.users.count({ where: { role: Roles.SUPERVISOR } }); - const empCount = await prisma.users.count({ where: { role: Roles.EMPLOYEE } }); - - const countForUserTest = await prisma.employees.count({ - where: { supervisor_id: userTestSupervisor.id, is_supervisor: false }, - }); - - console.log(`✓ Users total: ${totalUsers} (attendu 50)`); - console.log(`✓ Supervisors: ${supCount} (attendu 4)`); - console.log(`✓ Employees : ${empCount} (attendu 33)`); - console.log(`✓ Employés sous User Test: ${countForUserTest} (attendu 9)`); -} - -main() - .catch((e) => { - console.error(e); - process.exit(1); - }) - .finally(async () => { - await prisma.$disconnect(); - }); diff --git a/prisma/mock-seeds-scripts/03-employees.ts b/prisma/mock-seeds-scripts/03-employees.ts deleted file mode 100644 index a0267fd..0000000 --- a/prisma/mock-seeds-scripts/03-employees.ts +++ /dev/null @@ -1,93 +0,0 @@ -import { PrismaClient, Roles } from '@prisma/client'; - -const prisma = new PrismaClient(); - -function randInt(min: number, max: number) { - return Math.floor(Math.random() * (max - min + 1)) + min; -} - -function randomPastDate(yearsBack = 3) { - const now = new Date(); - const past = new Date(now); - past.setFullYear(now.getFullYear() - yearsBack); - const t = randInt(past.getTime(), now.getTime()); - const d = new Date(t); - d.setHours(0, 0, 0, 0); - return d; -} - -const jobTitles = [ - 'Directeur des ventes', - 'Directeur technique', - 'Programmeur', - 'Technicien', - 'Comptable', - 'Magasinier', - 'Responsable Resources Humaines', - 'Conseiller en vente', - 'Support technique', -]; - -function randomTitle() { - return jobTitles[randInt(0, jobTitles.length - 1)]; -} - -// Sélection aléatoire entre 271583 et 271585 -function randomCompanyCode() { - return Math.random() < 0.5 ? 271583 : 271585; -} - -async function main() { - const employeeUsers = await prisma.users.findMany({ - where: { role: { in: [Roles.ADMIN, Roles.SUPERVISOR, Roles.HR, Roles.ACCOUNTING, Roles.EMPLOYEE] } }, - orderBy: { email: 'asc' }, - }); - - // 1) Trouver le user qui sera le superviseur fixe - const supervisorUser = await prisma.users.findUnique({ - where: { email: 'user5@example.test' }, - }); - if (!supervisorUser) { - throw new Error("Le user 'user5@example.test' n'existe pas !"); - } - - // 2) Créer ou récupérer son employee avec is_supervisor = true - const supervisorEmp = await prisma.employees.upsert({ - where: { user_id: supervisorUser.id }, - update: { is_supervisor: true }, - create: { - user_id: supervisorUser.id, - external_payroll_id: randInt(10000, 99999), - company_code: randomCompanyCode(), - first_work_day: randomPastDate(3), - last_work_day: null, - job_title: randomTitle(), - is_supervisor: true, - }, - }); - - // 3) Créer tous les autres employés avec ce superviseur (sauf ADMIN qui n’a pas de superviseur) - for (const u of employeeUsers) { - const already = await prisma.employees.findUnique({ where: { user_id: u.id } }); - if (already) continue; - - const supervisor_id = u.role === Roles.ADMIN ? null : supervisorEmp.id; - - await prisma.employees.create({ - data: { - user_id: u.id, - external_payroll_id: randInt(10000, 99999), - company_code: randomCompanyCode(), - first_work_day: randomPastDate(3), - last_work_day: null, - supervisor_id, - job_title: randomTitle(), - }, - }); - } - - const total = await prisma.employees.count(); - console.log(`✓ Employees: ${total} rows (supervisor = ${supervisorUser.email})`); -} - -main().finally(() => prisma.$disconnect()); diff --git a/prisma/mock-seeds-scripts/04-customers.ts b/prisma/mock-seeds-scripts/04-customers.ts deleted file mode 100644 index b8319cf..0000000 --- a/prisma/mock-seeds-scripts/04-customers.ts +++ /dev/null @@ -1,28 +0,0 @@ -// import { PrismaClient, Roles } from '@prisma/client'; - -// const prisma = new PrismaClient(); - -// async function main() { -// const customerUsers = await prisma.users.findMany({ -// where: { role: Roles.CUSTOMER }, -// orderBy: { email: 'asc' }, -// }); - -// let i = 0; -// for (const u of customerUsers) { -// await prisma.customers.upsert({ -// where: { user_id: u.id }, -// update: {}, -// create: { -// user_id: u.id, -// invoice_id: i % 2 === 0 ? 100000 + i : null, // 1 sur 2 a un invoice_id -// }, -// }); -// i++; -// } - -// const total = await prisma.customers.count(); -// console.log(`✓ Customers: ${total} rows`); -// } - -// main().finally(() => prisma.$disconnect()); diff --git a/prisma/mock-seeds-scripts/05-employees-archive.ts b/prisma/mock-seeds-scripts/05-employees-archive.ts deleted file mode 100644 index b352a7f..0000000 --- a/prisma/mock-seeds-scripts/05-employees-archive.ts +++ /dev/null @@ -1,45 +0,0 @@ -// import { PrismaClient } from '@prisma/client'; - -// if (process.env.SKIP_LEAVE_REQUESTS === 'true') { -// console.log("⏭ Seed leave-requests ignoré (SKIP_LEAVE_REQUESTS=true)"); -// process.exit(0); -// } - -// const prisma = new PrismaClient(); - -// function daysAgo(n: number) { -// const d = new Date(); -// d.setDate(d.getDate() - n); -// d.setHours(0,0,0,0); -// return d; -// } - -// async function main() { -// const employees = await prisma.employees.findMany({ -// include: { user: true }, -// take: 10, // archive 10 -// }); - -// for (const e of employees) { -// await prisma.employeesArchive.create({ -// data: { -// employee_id: e.id, -// user_id: e.user_id, -// first_name: e.user.first_name, -// last_name: e.user.last_name, -// external_payroll_id: e.external_payroll_id, -// company_code: e.company_code, -// first_work_day: e.first_work_day, -// last_work_day: daysAgo(30), -// supervisor_id: e.supervisor_id ?? null, -// job_title: e.job_title, -// is_supervisor: e.is_supervisor, -// }, -// }); -// } - -// const total = await prisma.employeesArchive.count(); -// console.log(`✓ EmployeesArchive: ${total} rows (added 10)`); -// } - -// main().finally(() => prisma.$disconnect()); diff --git a/prisma/mock-seeds-scripts/06-customers-archive.ts b/prisma/mock-seeds-scripts/06-customers-archive.ts deleted file mode 100644 index 0c2a382..0000000 --- a/prisma/mock-seeds-scripts/06-customers-archive.ts +++ /dev/null @@ -1,35 +0,0 @@ -// // prisma/mock-seeds-scripts/06-customers-archive.ts -// import { PrismaClient } from '@prisma/client'; - -// if (process.env.SKIP_LEAVE_REQUESTS === 'true') { -// console.log("⏭ Seed leave-requests ignoré (SKIP_LEAVE_REQUESTS=true)"); -// process.exit(0); -// } - -// const prisma = new PrismaClient(); - -// async function main() { -// const customers = await prisma.customers.findMany({ -// orderBy: { id: 'asc' }, -// take: 5, -// }); - -// for (const c of customers) { -// const invoiceId = 200000 + c.id; // déterministe, stable entre runs - -// await prisma.customersArchive.upsert({ -// where: { invoice_id: invoiceId }, // invoice_id est unique -// update: {}, // idempotent -// create: { -// customer_id: c.id, -// user_id: c.user_id, -// invoice_id: invoiceId, -// }, -// }); -// } - -// const total = await prisma.customersArchive.count(); -// console.log(`✓ CustomersArchive upserted for ${customers.length} customers (total=${total})`); -// } - -// main().finally(() => prisma.$disconnect()); diff --git a/prisma/mock-seeds-scripts/07-leave-requests-future.ts b/prisma/mock-seeds-scripts/07-leave-requests-future.ts deleted file mode 100644 index 7e07a33..0000000 --- a/prisma/mock-seeds-scripts/07-leave-requests-future.ts +++ /dev/null @@ -1,82 +0,0 @@ -// import { PrismaClient, Prisma, LeaveTypes, LeaveApprovalStatus } from '@prisma/client'; - -// if (process.env.SKIP_LEAVE_REQUESTS === 'true') { -// console.log('?? Seed leave-requests ignor� (SKIP_LEAVE_REQUESTS=true)'); -// process.exit(0); -// } - -// const prisma = new PrismaClient(); - -// function dateOn(y: number, m: number, d: number) { -// // stocke une date (@db.Date) � minuit UTC -// return new Date(Date.UTC(y, m - 1, d, 0, 0, 0)); -// } - -// async function main() { -// const year = new Date().getFullYear(); -// const today = new Date(); - -// const employees = await prisma.employees.findMany({ select: { id: true } }); -// const bankCodes = await prisma.bankCodes.findMany({ -// where: { categorie: 'LEAVE' }, -// select: { id: true, type: true }, -// }); - -// if (!employees.length || !bankCodes.length) { -// console.warn('No employees or LEAVE bank codes; aborting'); -// return; -// } - -// const types: LeaveTypes[] = [ -// LeaveTypes.SICK, -// LeaveTypes.VACATION, -// LeaveTypes.UNPAID, -// LeaveTypes.BEREAVEMENT, -// LeaveTypes.PARENTAL, -// LeaveTypes.LEGAL, -// LeaveTypes.WEDDING, -// ]; -// const statuses: LeaveApprovalStatus[] = [ -// LeaveApprovalStatus.PENDING, -// LeaveApprovalStatus.APPROVED, -// LeaveApprovalStatus.DENIED, -// LeaveApprovalStatus.CANCELLED, -// LeaveApprovalStatus.ESCALATED, -// ]; - -// const futureMonths = [8, 9, 10, 11, 12]; // Ao�t ? D�c. (1-based) - -// const rows: Prisma.LeaveRequestsCreateManyInput[] = []; - -// for (let i = 0; i < 10; i++) { -// const emp = employees[i % employees.length]; -// const m = futureMonths[i % futureMonths.length]; -// const date = dateOn(year, m, 5 + i); // 5..14 -// if (date <= today) continue; // garantir � futur � - -// const type = types[i % types.length]; -// const status = statuses[i % statuses.length]; -// const bc = bankCodes[i % bankCodes.length]; -// const requestedHours = 4 + (i % 5); // 4 ? 8 h -// const payableHours = status === LeaveApprovalStatus.APPROVED ? Math.min(requestedHours, 8) : null; - -// rows.push({ -// employee_id: emp.id, -// bank_code_id: bc.id, -// leave_type: type, -// date, -// comment: `Future leave #${i + 1} (${bc.type})`, -// approval_status: status, -// requested_hours: requestedHours, -// payable_hours: payableHours, -// }); -// } - -// if (rows.length) { -// await prisma.leaveRequests.createMany({ data: rows }); -// } - -// console.log(`? LeaveRequests (future): ${rows.length} rows`); -// } - -// main().finally(() => prisma.$disconnect()); \ No newline at end of file diff --git a/prisma/mock-seeds-scripts/08-leave-requests-archive.ts b/prisma/mock-seeds-scripts/08-leave-requests-archive.ts deleted file mode 100644 index 0170635..0000000 --- a/prisma/mock-seeds-scripts/08-leave-requests-archive.ts +++ /dev/null @@ -1,87 +0,0 @@ -// import { PrismaClient, LeaveApprovalStatus, LeaveTypes } from '@prisma/client'; - -// if (process.env.SKIP_LEAVE_REQUESTS === 'true') { -// console.log('?? Seed leave-requests ignor� (SKIP_LEAVE_REQUESTS=true)'); -// process.exit(0); -// } - -// const prisma = new PrismaClient(); - -// function daysAgo(n: number) { -// const d = new Date(); -// d.setUTCDate(d.getUTCDate() - n); -// d.setUTCHours(0, 0, 0, 0); -// return d; -// } - -// async function main() { -// const employees = await prisma.employees.findMany({ select: { id: true } }); -// if (!employees.length) { -// throw new Error('Aucun employ� trouv�. Ex�cute le seed employees avant celui-ci.'); -// } - -// const leaveCodes = await prisma.bankCodes.findMany({ -// where: { type: { in: ['SICK', 'VACATION', 'HOLIDAY'] } }, -// select: { id: true, type: true }, -// }); -// if (!leaveCodes.length) { -// throw new Error("Aucun bank code trouv� avec type in ('SICK','VACATION','HOLIDAY'). V�rifie ta table bank_codes."); -// } - -// const statuses = Object.values(LeaveApprovalStatus); -// const created = [] as Array<{ id: number; employee_id: number; leave_type: LeaveTypes; date: Date; comment: string; approval_status: LeaveApprovalStatus; requested_hours: number; payable_hours: number | null }>; - -// const COUNT = 12; -// for (let i = 0; i < COUNT; i++) { -// const emp = employees[i % employees.length]; -// const leaveCode = leaveCodes[Math.floor(Math.random() * leaveCodes.length)]; - -// const date = daysAgo(120 - i * 3); -// const status = statuses[(i + 2) % statuses.length]; -// const requestedHours = 4 + (i % 5); // 4 ? 8 h -// const payableHours = status === LeaveApprovalStatus.APPROVED ? Math.min(requestedHours, 8) : null; - -// const lr = await prisma.leaveRequests.create({ -// data: { -// employee_id: emp.id, -// bank_code_id: leaveCode.id, -// leave_type: leaveCode.type as LeaveTypes, -// date, -// comment: `Past leave #${i + 1} (${leaveCode.type})`, -// approval_status: status, -// requested_hours: requestedHours, -// payable_hours: payableHours, -// }, -// }); - -// created.push({ -// id: lr.id, -// employee_id: lr.employee_id, -// leave_type: lr.leave_type, -// date: lr.date, -// comment: lr.comment, -// approval_status: lr.approval_status, -// requested_hours: requestedHours, -// payable_hours: payableHours, -// }); -// } - -// for (const lr of created) { -// await prisma.leaveRequestsArchive.create({ -// data: { -// leave_request_id: lr.id, -// employee_id: lr.employee_id, -// leave_type: lr.leave_type, -// date: lr.date, -// comment: lr.comment, -// approval_status: lr.approval_status, -// requested_hours: lr.requested_hours, -// payable_hours: lr.payable_hours, -// }, -// }); -// } - -// console.log(`? LeaveRequestsArchive: ${created.length} rows`); -// } - -// main().finally(() => prisma.$disconnect()); \ No newline at end of file diff --git a/prisma/mock-seeds-scripts/09-timesheets.ts b/prisma/mock-seeds-scripts/09-timesheets.ts deleted file mode 100644 index 4160de9..0000000 --- a/prisma/mock-seeds-scripts/09-timesheets.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { PrismaClient, Prisma } from '@prisma/client'; - -const prisma = new PrismaClient(); - -// ====== Config ====== -const PREVIOUS_WEEKS = 16; // nombre de semaines à créer (passé) -const INCLUDE_CURRENT = true; // true si tu veux aussi la semaine courante - -// Dimanche (UTC) de la semaine courante -function sundayOfThisWeekUTC(now = new Date()) { - // normalise à minuit UTC du jour courant - const d = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate())); - const day = d.getUTCDay(); // 0=Dim, 1=Lun, ... 6=Sam - // recule jusqu'au dimanche de cette semaine - d.setUTCDate(d.getUTCDate() - day); - d.setUTCHours(0, 0, 0, 0); - return d; -} - -function sundayNWeeksBefore(sunday: Date, n: number) { - const d = new Date(sunday); - d.setUTCDate(d.getUTCDate() - n * 7); - return d; -} - -async function main() { - const employees = await prisma.employees.findMany({ select: { id: true } }); - if (!employees.length) { - console.warn('Aucun employé — rien à insérer.'); - return; - } - - // Construit la liste des dimanches (1 par semaine) - const sundays: Date[] = []; - const sundayThisWeek = sundayOfThisWeekUTC(); - if (INCLUDE_CURRENT) sundays.push(sundayThisWeek); - for (let n = 1; n <= PREVIOUS_WEEKS; n++) { - sundays.push(sundayNWeeksBefore(sundayThisWeek, n)); - } - - // Prépare les lignes (1 timesheet / employé / semaine) - const rows: Prisma.TimesheetsCreateManyInput[] = []; - for (const e of employees) { - for (const sunday of sundays) { - rows.push({ - employee_id: e.id, - start_date: sunday, - is_approved: Math.random() < 0.3, - } as Prisma.TimesheetsCreateManyInput); - } - } - - // Insert en bulk et ignore les doublons si déjà présents - if (rows.length) { - await prisma.timesheets.createMany({ data: rows, skipDuplicates: true }); - } - - const total = await prisma.timesheets.count(); - console.log(`✓ Timesheets: ${total} rows (ajout potentiel: ${rows.length}, ${INCLUDE_CURRENT ? 'courante +' : ''}${PREVIOUS_WEEKS} semaines)`); -} - -main().finally(() => prisma.$disconnect()); diff --git a/prisma/mock-seeds-scripts/10-shifts.ts b/prisma/mock-seeds-scripts/10-shifts.ts deleted file mode 100644 index 8c0e095..0000000 --- a/prisma/mock-seeds-scripts/10-shifts.ts +++ /dev/null @@ -1,229 +0,0 @@ -import { PrismaClient } from '@prisma/client'; - -const prisma = new PrismaClient(); - -// ====== Config ====== -const PREVIOUS_WEEKS = 5; -const INCLUDE_CURRENT = true; -const INCR = 15; // incrément ferme de 15 minutes (0.25 h) -const DAY_MIN = 5 * 60; // 5h -const DAY_MAX = 11 * 60; // 11h -const HARD_END = 19 * 60 + 30; // 19:30 - -// ====== Helpers temps ====== -function timeAt(hour: number, minute: number) { - return new Date(Date.UTC(1970, 0, 1, hour, minute, 0)); -} - -// Ancre SEMAINE = DIMANCHE (UTC) -function sundayOfThisWeekUTC(now = new Date()) { - const d = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate())); - const day = d.getUTCDay(); // 0 = Dim - d.setUTCDate(d.getUTCDate() - day); // recule jusqu'au dimanche - d.setUTCHours(0, 0, 0, 0); - return d; -} -function sundayNWeeksBefore(sunday: Date, n: number) { - const d = new Date(sunday); - d.setUTCDate(d.getUTCDate() - n * 7); - return d; -} -// Génère L→V à partir du dimanche (Lundi = dimanche + 1) -function weekDatesMonToFriFromSunday(sunday: Date) { - return Array.from({ length: 5 }, (_, i) => { - const d = new Date(sunday); - d.setUTCDate(sunday.getUTCDate() + (i + 1)); // +1..+5 - return d; - }); -} - -function rndInt(min: number, max: number) { - return Math.floor(Math.random() * (max - min + 1)) + min; -} -function clamp(n: number, min: number, max: number) { - return Math.min(max, Math.max(min, n)); -} -function addMinutes(h: number, m: number, delta: number) { - const total = h * 60 + m + delta; - const hh = Math.floor(total / 60); - const mm = ((total % 60) + 60) % 60; - return { h: hh, m: mm }; -} -function quantize(mins: number): number { - return Math.round(mins / INCR) * INCR; -} -function rndQuantized(min: number, max: number): number { - const qmin = Math.ceil(min / INCR); - const qmax = Math.floor(max / INCR); - const q = rndInt(qmin, qmax); - return q * INCR; -} - -async function main() { - // --- Bank codes (pondérés: surtout G1 = régulier) --- - const BANKS = ['G1', 'G56', 'G48', 'G105', 'G104', 'G305'] as const; - const WEIGHTED_CODES = [ - 'G1','G1','G1','G1','G1','G1','G1','G1','G1','G1','G1','G1', - 'G56','G48','G104','G105','G305','G1','G1','G1','G1','G1','G1' - ] as const; - - const bcRows = await prisma.bankCodes.findMany({ - where: { bank_code: { in: BANKS as unknown as string[] } }, - select: { id: true, bank_code: true }, - }); - const bcMap = new Map(bcRows.map(b => [b.bank_code, b.id])); - for (const c of BANKS) { - if (!bcMap.has(c)) throw new Error(`Bank code manquant: ${c}`); - } - for (const c of Array.from(new Set(WEIGHTED_CODES))) { - if (!bcMap.has(c)) throw new Error(`Bank code manquant dans WEIGHTED_CODES: ${c}`); - } - - // ====== Fenêtre de semaines à remplir (d'après les timesheets existants) ====== - const sundayThisWeek = sundayOfThisWeekUTC(); - const minSunday = sundayNWeeksBefore(sundayThisWeek, PREVIOUS_WEEKS); - const maxSunday = sundayThisWeek; - - // Récupère les timesheets existants dans la fenêtre (sans en créer) - const timesheets = await prisma.timesheets.findMany({ - where: { - start_date: { - gte: minSunday, - lte: INCLUDE_CURRENT ? maxSunday : sundayNWeeksBefore(maxSunday, 1), // exclut la semaine courante si demandé - }, - }, - select: { id: true, employee_id: true, start_date: true }, - orderBy: [{ start_date: 'desc' }, { employee_id: 'asc' }], - }); - - if (!timesheets.length) { - console.log('Aucun timesheet existant trouvé dans la fenêtre demandée — aucun shift créé.'); - return; - } - - let created = 0; - - // Pour chaque timesheet existant, on génère les shifts L→V rattachés à son id - for (const ts of timesheets) { - const sunday = new Date(ts.start_date); // ancre = dimanche - const days = weekDatesMonToFriFromSunday(sunday); // L→V - - // Optionnel : si tu veux éviter de dupliquer des shifts, décommente : - // const existingCount = await prisma.shifts.count({ where: { timesheet_id: ts.id } }); - // if (existingCount > 0) continue; - - // On paramètre le pattern à partir de l'employee_id pour varier un peu - const baseStartH = 7 + (ts.employee_id % 3); // 7,8,9 - const baseStartM = ((ts.employee_id * 15) % 60); // 0,15,30,45 - const weeklyTargetMin = rndQuantized(35 * 60, 45 * 60); - - // Planification journalière (5 jours) ~8h ± 45 min - const plannedDaily: number[] = []; - for (let d = 0; d < 5; d++) { - const jitter = rndInt(-3, 3) * INCR; // -45..+45 - const base = 8 * 60 + jitter; - plannedDaily.push(quantize(clamp(base, DAY_MIN, DAY_MAX))); - } - // Ajuste le 5e jour pour matcher la cible hebdo - const sumFirst4 = plannedDaily.slice(0, 4).reduce((a, b) => a + b, 0); - plannedDaily[4] = quantize(clamp(weeklyTargetMin - sumFirst4, DAY_MIN, DAY_MAX)); - // Fine tuning ±15 - let diff = weeklyTargetMin - plannedDaily.reduce((a, b) => a + b, 0); - const step = diff > 0 ? INCR : -INCR; - let guard = 100; - while (diff !== 0 && guard-- > 0) { - for (let d = 0; d < 5 && diff !== 0; d++) { - const next = plannedDaily[d] + step; - if (next >= DAY_MIN && next <= DAY_MAX) { - plannedDaily[d] = next; - diff -= step; - } - } - } - - for (let di = 0; di < 5; di++) { - const date = days[di]; // Lundi..Vendredi - const targetWorkMin = plannedDaily[di]; - - // Départ ~ base + jitter - const startJitter = rndInt(-1, 2) * INCR; // -15,0,+15,+30 - const { h: startH, m: startM } = addMinutes(baseStartH, baseStartM, startJitter); - - // Pause: entre 11:00 et 14:00, bornée par start+3h .. start+6h - const earliestLunch = Math.max((startH * 60 + startM) + 3 * 60, 11 * 60); - const latestLunch = Math.min((startH * 60 + startM) + 6 * 60, 14 * 60); - const lunchStartMin = rndQuantized(earliestLunch, latestLunch); - const lunchDur = rndQuantized(30, 120); - const lunchEndMin = lunchStartMin + lunchDur; - - // Travail = (lunchStart - start) + (end - lunchEnd) - const morningWork = Math.max(0, lunchStartMin - (startH * 60 + startM)); - let afternoonWork = Math.max(60, targetWorkMin - morningWork); - if (afternoonWork % INCR !== 0) afternoonWork = quantize(afternoonWork); - - // Fin quantisée + borne max - const endMinRaw = lunchEndMin + afternoonWork; - const endMin = Math.min(endMinRaw, HARD_END); - - // Bank codes variés - const bcMorningCode = WEIGHTED_CODES[rndInt(0, WEIGHTED_CODES.length - 1)]; - const bcAfternoonCode= WEIGHTED_CODES[rndInt(0, WEIGHTED_CODES.length - 1)]; - const bcMorningId = bcMap.get(bcMorningCode)!; - const bcAfternoonId = bcMap.get(bcAfternoonCode)!; - - // Shift matin - const lunchStartHM = { h: Math.floor(lunchStartMin / 60), m: lunchStartMin % 60 }; - await prisma.shifts.create({ - data: { - timesheet_id: ts.id, - bank_code_id: bcMorningId, - comment: `Matin J${di + 1} (sem ${sunday.toISOString().slice(0, 10)}) emp ${ts.employee_id} - ${bcMorningCode}`, - date, - start_time: timeAt(startH, startM), - end_time: timeAt(lunchStartHM.h, lunchStartHM.m), - is_approved: Math.random() < 0.6, - }, - }); - created++; - - // Shift après-midi (si >= 30 min) - const pmDuration = endMin - lunchEndMin; - if (pmDuration >= 30) { - const lunchEndHM = { h: Math.floor(lunchEndMin / 60), m: lunchEndMin % 60 }; - const finalEndHM = { h: Math.floor(endMin / 60), m: endMin % 60 }; - await prisma.shifts.create({ - data: { - timesheet_id: ts.id, - bank_code_id: bcAfternoonId, - comment: `Après-midi J${di + 1} (sem ${sunday.toISOString().slice(0, 10)}) emp ${ts.employee_id} — ${bcAfternoonCode}`, - date, - start_time: timeAt(lunchEndHM.h, lunchEndHM.m), - end_time: timeAt(finalEndHM.h, finalEndHM.m), - is_approved: Math.random() < 0.6, - }, - }); - created++; - } else { - // Fallback: un seul shift couvrant la journée - const fallbackEnd = addMinutes(startH, startM, targetWorkMin + lunchDur); - await prisma.shifts.create({ - data: { - timesheet_id: ts.id, - bank_code_id: bcMap.get('G1')!, - comment: `Fallback J${di + 1} (sem ${sunday.toISOString().slice(0, 10)}) emp ${ts.employee_id} — G1`, - date, - start_time: timeAt(startH, startM), - end_time: timeAt(fallbackEnd.h, fallbackEnd.m), - is_approved: Math.random() < 0.6, - }, - }); - created++; - } - } - } - - const total = await prisma.shifts.count(); - console.log(`✓ Shifts créés: ${created} | total en DB: ${total} (${INCLUDE_CURRENT ? 'inclut semaine courante, ' : ''}${PREVIOUS_WEEKS} sem passées, Dim ancre + L→V, 2 shifts/jour, **aucun timesheet créé**})`); -} - -main().finally(() => prisma.$disconnect()); diff --git a/prisma/mock-seeds-scripts/11-shifts-archive.ts b/prisma/mock-seeds-scripts/11-shifts-archive.ts deleted file mode 100644 index 4259359..0000000 --- a/prisma/mock-seeds-scripts/11-shifts-archive.ts +++ /dev/null @@ -1,71 +0,0 @@ -// import { PrismaClient } from '@prisma/client'; - -// if (process.env.SKIP_LEAVE_REQUESTS === 'true') { -// console.log("⏭ Seed leave-requests ignoré (SKIP_LEAVE_REQUESTS=true)"); -// process.exit(0); -// } - -// const prisma = new PrismaClient(); - -// function timeAt(h:number,m:number) { -// return new Date(Date.UTC(1970,0,1,h,m,0)); -// } -// function daysAgo(n:number) { -// const d = new Date(); -// d.setUTCDate(d.getUTCDate() - n); -// d.setUTCHours(0,0,0,0); -// return d; -// } - -// async function main() { -// const bankCodes = await prisma.bankCodes.findMany({ where: { categorie: 'SHIFT' }, select: { id: true } }); -// const employees = await prisma.employees.findMany({ select: { id: true } }); - -// for (const e of employees) { -// const tss = await prisma.timesheets.findMany({ where: { employee_id: e.id }, select: { id: true } }); -// if (!tss.length) continue; - -// const createdShiftIds: number[] = []; -// for (let i = 0; i < 8; i++) { -// const ts = tss[i % tss.length]; -// const bc = bankCodes[i % bankCodes.length]; -// const date = daysAgo(200 + i); // bien dans le passé -// const startH = 7 + (i % 4); // 7..10 -// const endH = startH + 8; // 15..18 - -// const sh = await prisma.shifts.create({ -// data: { -// timesheet_id: ts.id, -// bank_code_id: bc.id, -// comment: `Archived-era shift ${i + 1} for emp ${e.id}`, -// date, -// start_time: timeAt(startH, 0), -// end_time: timeAt(endH, 0), -// is_approved: true, -// }, -// }); -// createdShiftIds.push(sh.id); -// } - -// for (const sid of createdShiftIds) { -// const s = await prisma.shifts.findUnique({ where: { id: sid } }); -// if (!s) continue; -// await prisma.shiftsArchive.create({ -// data: { -// shift_id: s.id, -// timesheet_id: s.timesheet_id, -// bank_code_id: s.bank_code_id, -// comment: s.comment, -// date: s.date, -// start_time: s.start_time, -// end_time: s.end_time, -// }, -// }); -// } -// } - -// const total = await prisma.shiftsArchive.count(); -// console.log(`✓ ShiftsArchive: ${total} rows total`); -// } - -// main().finally(() => prisma.$disconnect()); diff --git a/prisma/mock-seeds-scripts/12-expenses.ts b/prisma/mock-seeds-scripts/12-expenses.ts deleted file mode 100644 index 9b7bf22..0000000 --- a/prisma/mock-seeds-scripts/12-expenses.ts +++ /dev/null @@ -1,163 +0,0 @@ -import { NotFoundException } from '@nestjs/common'; -import { PrismaClient } from '@prisma/client'; - -const prisma = new PrismaClient(); - -// ====== Config ====== -const WEEKS_BACK = 4; // 4 semaines avant + semaine courante -const INCLUDE_CURRENT = true; // inclure la semaine courante -const STEP_CENTS = 25; // montants en quarts de dollar (.00/.25/.50/.75) - -// ====== Helpers dates (ancre DIMANCHE UTC) ====== -function sundayOfThisWeekUTC(now = new Date()) { - const d = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate())); - const day = d.getUTCDay(); // 0=Dim, 1=Lun, ... - d.setUTCDate(d.getUTCDate() - day); // recule jusqu'au dimanche - d.setUTCHours(0, 0, 0, 0); - return d; -} -function sundayNWeeksBefore(sunday: Date, n: number) { - const d = new Date(sunday); - d.setUTCDate(d.getUTCDate() - n * 7); - return d; -} -// Génère L→V à partir du dimanche (Lundi = dimanche + 1) -function weekDatesMonToFriFromSunday(sunday: Date) { - return Array.from({ length: 5 }, (_, i) => { - const d = new Date(sunday); - d.setUTCDate(sunday.getUTCDate() + (i + 1)); // +1..+5 - return d; - }); -} - -// ====== Helpers random / amount ====== -function rndInt(min: number, max: number) { - return Math.floor(Math.random() * (max - min + 1)) + min; -} -// String "xx.yy" à partir de cents entiers (pas de float binaire en DB) -function centsToAmountString(cents: number): string { - const sign = cents < 0 ? '-' : ''; - const abs = Math.abs(cents); - const dollars = Math.floor(abs / 100); - const c = abs % 100; - return `${sign}${dollars}.${c.toString().padStart(2, '0')}`; -} -function to2(value: string): string { - return (Math.round(parseFloat(value) * 100) / 100).toFixed(2); -} -// Tire un multiple de STEP_CENTS entre minCents et maxCents (inclus) -function rndQuantizedCents(minCents: number, maxCents: number, step = STEP_CENTS): number { - const qmin = Math.ceil(minCents / step); - const qmax = Math.floor(maxCents / step); - const q = rndInt(qmin, qmax); - return q * step; -} -function rndAmount(minCents: number, maxCents: number): string { - return centsToAmountString(rndQuantizedCents(minCents, maxCents)); -} - -// ====== Lookup timesheet (AUCUNE création ici) ====== -async function findTimesheet(employee_id: number, start_date: Date) { - return prisma.timesheets.findUnique({ - where: { employee_id_start_date: { employee_id, start_date } }, - select: { id: true }, - }); -} - -async function main() { - // Codes d'EXPENSES (exemples) - const BANKS = ['G517', 'G503', 'G502', 'G202'] as const; - - // Précharger les bank codes - const bcRows = await prisma.bankCodes.findMany({ - where: { bank_code: { in: BANKS as unknown as string[] } }, - select: { id: true, bank_code: true }, - }); - const bcMap = new Map(bcRows.map(c => [c.bank_code, c.id])); - for (const c of BANKS) { - if (!bcMap.has(c)) throw new Error(`Bank code manquant: ${c}`); - } - - // Employés - const employees = await prisma.employees.findMany({ select: { id: true } }); - if (!employees.length) { - console.warn('Aucun employé — rien à insérer.'); - return; - } - - // Fenêtre de semaines ancrées au DIMANCHE - const sundayThisWeek = sundayOfThisWeekUTC(); - const sundays: Date[] = []; - if (INCLUDE_CURRENT) sundays.push(sundayThisWeek); - for (let n = 1; n <= WEEKS_BACK; n++) sundays.push(sundayNWeeksBefore(sundayThisWeek, n)); - - let created = 0; - - for (const sunday of sundays) { - const weekDays = weekDatesMonToFriFromSunday(sunday); // L→V - const monday = weekDays[0]; - const friday = weekDays[4]; - - for (const e of employees) { - // Utiliser le timesheet EXISTANT (ancré au DIMANCHE) - const ts = await findTimesheet(e.id, sunday); - if (!ts) throw new NotFoundException(`Timesheet manquant pour emp ${e.id} @ ${sunday.toISOString().slice(0,10)}`); - - // Idempotence: si déjà au moins une expense L→V, on skip la semaine - const already = await prisma.expenses.findFirst({ - where: { timesheet_id: ts.id, date: { gte: monday, lte: friday } }, - select: { id: true }, - }); - if (already) continue; - - // 1 à 3 expenses (jours distincts) - const count = rndInt(1, 3); - const dayIndexes = [0, 1, 2, 3, 4].sort(() => Math.random() - 0.5).slice(0, count); - - for (const idx of dayIndexes) { - const date = weekDays[idx]; - const code = BANKS[rndInt(0, BANKS.length - 1)]; - const bank_code_id = bcMap.get(code)!; - - // Montants (cents) quantisés à 25¢ => aucun flottant binaire en DB - let amount: string = '0.00'; - let mileage: string = '0.00'; - switch (code) { - case 'G503': // kilométrage - mileage = to2(rndAmount(1000, 7500)); // 10.00 à 75.00 - break; - case 'G502': // per_diem - amount = to2(rndAmount(1500, 3000)); // 15.00 à 30.00 - break; - case 'G202': // on_call / prime de garde - amount = to2(rndAmount(2000, 15000)); // 20.00 à 150.00 - break; - case 'G517': // expenses - default: - amount = to2(rndAmount(500, 5000)); // 5.00 à 50.00 - break; - } - - await prisma.expenses.create({ - data: { - timesheet_id: ts.id, - bank_code_id, - date, - amount, - mileage, - attachment: null, - comment: `Expense ${code} (emp ${e.id})`, - is_approved: Math.random() < 0.65, - supervisor_comment: Math.random() < 0.25 ? 'OK' : null, - }, - }); - created++; - } - } - } - - const total = await prisma.expenses.count(); - console.log(`✓ Expenses: ${created} nouvelles lignes, ${total} total rows (ancre dimanche, L→V, sem courante ${INCLUDE_CURRENT ? 'incluse' : 'exclue'} + ${WEEKS_BACK} précédentes)`); -} - -main().finally(() => prisma.$disconnect()); diff --git a/prisma/mock-seeds-scripts/13-expenses-archive.ts b/prisma/mock-seeds-scripts/13-expenses-archive.ts deleted file mode 100644 index c56520e..0000000 --- a/prisma/mock-seeds-scripts/13-expenses-archive.ts +++ /dev/null @@ -1,65 +0,0 @@ -// // 13-expenses-archive.ts -// import { PrismaClient, Expenses } from '@prisma/client'; - -// if (process.env.SKIP_LEAVE_REQUESTS === 'true') { -// console.log("⏭ Seed leave-requests ignoré (SKIP_LEAVE_REQUESTS=true)"); -// process.exit(0); -// } - -// const prisma = new PrismaClient(); - -// function daysAgo(n:number) { -// const d = new Date(); -// d.setUTCDate(d.getUTCDate() - n); -// d.setUTCHours(0,0,0,0); -// return d; -// } - -// async function main() { -// const expenseCodes = await prisma.bankCodes.findMany({ where: { categorie: 'EXPENSE' }, select: { id: true } }); -// const timesheets = await prisma.timesheets.findMany({ select: { id: true } }); - -// // ✅ typer pour éviter never[] -// const created: Expenses[] = []; - -// for (let i = 0; i < 4; i++) { -// const ts = timesheets[i % timesheets.length]; -// const bc = expenseCodes[i % expenseCodes.length]; - -// const exp = await prisma.expenses.create({ -// data: { -// timesheet_id: ts.id, -// bank_code_id: bc.id, -// date: daysAgo(60 + i), -// amount: (20 + i * 3.5).toFixed(2), // ok: Decimal accepte string -// attachment: null, -// comment: `Old expense #${i + 1}`, -// is_approved: true, -// supervisor_comment: null, -// }, -// }); - -// created.push(exp); -// } - -// for (const e of created) { -// await prisma.expensesArchive.create({ -// data: { -// expense_id: e.id, -// timesheet_id: e.timesheet_id, -// bank_code_id: e.bank_code_id, -// date: e.date, -// amount: e.amount, -// attachment: e.attachment, -// comment: e.comment, -// is_approved: e.is_approved, -// supervisor_comment: e.supervisor_comment, -// }, -// }); -// } - -// const total = await prisma.expensesArchive.count(); -// console.log(`✓ ExpensesArchive: ${total} total rows (added ${created.length})`); -// } - -// main().finally(() => prisma.$disconnect()); diff --git a/prisma/mock-seeds-scripts/14-oauth-sessions.ts b/prisma/mock-seeds-scripts/14-oauth-sessions.ts deleted file mode 100644 index 93652cb..0000000 --- a/prisma/mock-seeds-scripts/14-oauth-sessions.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { PrismaClient } from '@prisma/client'; -import crypto from 'crypto'; - -const prisma = new PrismaClient(); - -function token() { - return crypto.randomBytes(24).toString('hex'); -} -function futureHours(h:number) { - const d = new Date(); - d.setHours(d.getHours() + h); - return d; -} - -async function main() { - const users = await prisma.users.findMany({ select: { id: true } }); - let created = 0; - - for (const u of users) { - await prisma.oAuthSessions.create({ - data: { - user_id: u.id, - application: 'targo-2.0', - access_token: token(), - refresh_token: token(), - sid: token(), - access_token_expiry: futureHours(2), - refresh_token_expiry: futureHours(24 * 30), - is_revoked: false, - scopes: [], - }, - }); - created++; - } - - const total = await prisma.oAuthSessions.count(); - console.log(`✓ OAuthSessions: ${total} total (added ${created})`); -} - -main().finally(() => prisma.$disconnect()); diff --git a/prisma/mock-seeds-scripts/READMEmockseeds.md b/prisma/mock-seeds-scripts/READMEmockseeds.md deleted file mode 100644 index 3579198..0000000 --- a/prisma/mock-seeds-scripts/READMEmockseeds.md +++ /dev/null @@ -1,50 +0,0 @@ -# 1) Générer Prisma Client -npm run prisma:generate - -# 2) Appliquer les migrations (crée les tables, vues, etc.) -npm run db:migrate - -# 3) Lancer tous les seeds dans l’ordre -npm run seed:all - - - - -Complet reseed : npm run db:reseed - - -Run a specific seed : npm run seed:07 # leave-requests-future - -Open prisma studio : npx prisma studio - - -Data: - -users = 50 - -employees ≈ 40 - -customers ≈ 10 - -employees_archive = 10 - -customers_archive = 5 - -leave_requests (futur) = 10 (tous > aujourd’hui) - -leave_requests_archive = 10 (tous < aujourd’hui) - -timesheets = 8 × (#employees) - -shifts = 10 × (#employees) - -shifts_archive = 30 × (#employees) - -bank_codes = 9 - -expenses = 5 - -expenses_archive = 20 - -oauth_sessions = 50 - diff --git a/src/modules/archival/archival.module.ts b/src/shared/archival/archival.module.ts similarity index 100% rename from src/modules/archival/archival.module.ts rename to src/shared/archival/archival.module.ts diff --git a/src/modules/archival/archival.service.ts b/src/shared/archival/archival.service.ts similarity index 100% rename from src/modules/archival/archival.service.ts rename to src/shared/archival/archival.service.ts diff --git a/src/modules/archival/controllers/employees-archive.controller.ts b/src/shared/archival/controllers/employees-archive.controller.ts similarity index 100% rename from src/modules/archival/controllers/employees-archive.controller.ts rename to src/shared/archival/controllers/employees-archive.controller.ts diff --git a/src/modules/archival/controllers/expenses-archive.controller.ts b/src/shared/archival/controllers/expenses-archive.controller.ts similarity index 100% rename from src/modules/archival/controllers/expenses-archive.controller.ts rename to src/shared/archival/controllers/expenses-archive.controller.ts diff --git a/src/modules/archival/controllers/leave-requests-archive.controller.ts b/src/shared/archival/controllers/leave-requests-archive.controller.ts similarity index 100% rename from src/modules/archival/controllers/leave-requests-archive.controller.ts rename to src/shared/archival/controllers/leave-requests-archive.controller.ts diff --git a/src/modules/archival/controllers/shifts-archive.controller.ts b/src/shared/archival/controllers/shifts-archive.controller.ts similarity index 100% rename from src/modules/archival/controllers/shifts-archive.controller.ts rename to src/shared/archival/controllers/shifts-archive.controller.ts diff --git a/src/modules/archival/controllers/timesheets-archive.controller.ts b/src/shared/archival/controllers/timesheets-archive.controller.ts similarity index 100% rename from src/modules/archival/controllers/timesheets-archive.controller.ts rename to src/shared/archival/controllers/timesheets-archive.controller.ts diff --git a/test/app.e2e-spec.ts b/test/app.e2e-spec.ts deleted file mode 100644 index 816ef4b..0000000 --- a/test/app.e2e-spec.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { INestApplication } from '@nestjs/common'; -import * as request from 'supertest'; -import { App } from 'supertest/types'; -import { AppModule } from './../src/app.module'; -import { PrismaService } from 'src/prisma/prisma.service'; - -describe('AppController (e2e)', () => { - let app: INestApplication; - - beforeEach(async () => { - const moduleFixture: TestingModule = await Test.createTestingModule({ - imports: [AppModule], - }).compile(); - - app = moduleFixture.createNestApplication(); - await app.init(); - }); - - it('/ (GET)', () => { - return request(app.getHttpServer()) - .get('/') - .expect(200) - .expect('Hello World!'); - }); - - afterAll(async () => { - await app.close(); - const prisma = app.get(PrismaService); - await prisma.$disconnect(); - }); -}); diff --git a/test/bank-codes.e2e-spec.ts b/test/bank-codes.e2e-spec.ts deleted file mode 100644 index ddbc1ee..0000000 --- a/test/bank-codes.e2e-spec.ts +++ /dev/null @@ -1,125 +0,0 @@ -import * as request from 'supertest'; -import { INestApplication } from '@nestjs/common'; -import { PrismaService } from 'src/prisma/prisma.service'; -import { createApp } from './utils/testing-app'; -// import { resetDb } from './utils/reset-db'; -import { makeBankCode, makeInvalidBankCode } from './factories/bank-code.factory'; - -describe('BankCodes (e2e)', () => { - let app: INestApplication; - const BASE = '/bank-codes'; - const RESET = process.env.E2E_RESET_DB === '1'; - - beforeAll(async () => { - app = await createApp(); - }); - - beforeEach(async () => { - // if (RESET) await resetDb(app); - }); - - afterAll(async () => { - const prisma = app.get(PrismaService); - await app.close(); - await prisma.$disconnect(); - }); - - it(`GET ${BASE} → 200 (array)`, async () => { - const res = await request(app.getHttpServer()).get(BASE); - expect(res.status).toBe(200); - expect(Array.isArray(res.body)).toBe(true); - }); - - it(`POST ${BASE} (valid) → 201 puis GET /:id → 200`, async () => { - const payload = makeBankCode(); - - const createRes = await request(app.getHttpServer()) - .post(BASE) - .send(payload); - expect(createRes.status).toBe(201); - expect(createRes.body).toEqual( - expect.objectContaining({ - id: expect.any(Number), - type: payload.type, - categorie: payload.categorie, - modifier: payload.modifier, - bank_code: payload.bank_code, - }), - ); - - const id = createRes.body.id; - - const getRes = await request(app.getHttpServer()).get(`${BASE}/${id}`); - expect(getRes.status).toBe(200); - expect(getRes.body).toEqual( - expect.objectContaining({ - id, - type: payload.type, - categorie: payload.categorie, - modifier: payload.modifier, - bank_code: payload.bank_code, - }), - ); - }); - - it(`PATCH ${BASE}/:id → 200 (modifier mis à jour)`, async () => { - const create = await request(app.getHttpServer()) - .post(BASE) - .send(makeBankCode()); - expect(create.status).toBe(201); - const id = create.body.id; - - const updated = { modifier: 2 }; - - const patchRes = await request(app.getHttpServer()) - .patch(`${BASE}/${id}`) - .send(updated); - expect([200, 204]).toContain(patchRes.status); - - const getRes = await request(app.getHttpServer()).get(`${BASE}/${id}`); - expect(getRes.status).toBe(200); - expect(getRes.body.modifier).toBe(updated.modifier); - }); - - it(`POST ${BASE} (invalid payload) → 400`, async () => { - const res = await request(app.getHttpServer()) - .post(BASE) - .send(makeInvalidBankCode()); - - expect(res.status).toBeGreaterThanOrEqual(400); - expect(res.status).toBeLessThan(500); - }); - - it(`POST ${BASE} (duplicate bank_code) → 409 (ou 400)`, async () => { - const payload = makeBankCode(); - - const first = await request(app.getHttpServer()).post(BASE).send(payload); - expect(first.status).toBe(201); - - const dup = await request(app.getHttpServer()).post(BASE).send({ - ...payload, - type: 'ANOTHER_TYPE_USING_SAME_BANK_CODE', - }); - expect(dup.status).toBe(201); - }); - - it(`DELETE ${BASE}/:id → 200/204`, async () => { - const create = await request(app.getHttpServer()) - .post(BASE) - .send(makeBankCode()); - expect(create.status).toBe(201); - - const id = create.body.id; - - const del = await request(app.getHttpServer()).delete(`${BASE}/${id}`); - expect([200, 204]).toContain(del.status); - - const after = await request(app.getHttpServer()).get(`${BASE}/${id}`); - expect(after.status).toBe(404); - }); - - it(`GET ${BASE}/:id (not found) → 404`, async () => { - const res = await request(app.getHttpServer()).get(`${BASE}/999999`); - expect([404, 400]).toContain(res.status); - }); -}); diff --git a/test/customers.e2e-spec.ts b/test/customers.e2e-spec.ts deleted file mode 100644 index 98172db..0000000 --- a/test/customers.e2e-spec.ts +++ /dev/null @@ -1,125 +0,0 @@ -// import * as request from 'supertest'; -// import { INestApplication } from '@nestjs/common'; -// import { createApp } from './utils/testing-app'; -// import { PrismaService } from 'src/prisma/prisma.service'; - -// type CustomerPayload = { -// user_id?: string; -// first_name: string; -// last_name: string; -// email?: string; -// phone_number: number; -// residence?: string; -// invoice_id: number; -// }; - -// const BASE = '/customers'; - -// const uniqueEmail = () => -// `customer+${Date.now()}_${Math.random().toString(36).slice(2,8)}@test.local`; -// const uniquePhone = () => -// Math.floor(100_000_000 + Math.random() * 900_000_000); - -// function makeCustomerPayload(overrides: Partial = {}): CustomerPayload { -// return { -// first_name: 'Gandalf', -// last_name: 'TheGray', -// email: uniqueEmail(), -// phone_number: uniquePhone(), -// residence: '1 Ringbearer’s Way, Mount Doom, ME', -// invoice_id: Math.floor(1_000_000 + Math.random() * 9_000_000), -// ...overrides, -// }; -// } - -// describe('Customers (e2e) — autonome', () => { -// let app: INestApplication; -// let prisma: PrismaService; -// let createdId: number | null = null; - -// beforeAll(async () => { -// app = await createApp(); -// prisma = app.get(PrismaService); -// }); - -// afterAll(async () => { -// if (createdId) { -// try { await prisma.customers.delete({ where: { id: createdId } }); } catch {} -// } -// await app.close(); -// await prisma.$disconnect(); -// }); - -// it(`GET ${BASE} → 200 (array)`, async () => { -// const res = await request(app.getHttpServer()).get(BASE); -// expect(res.status).toBe(200); -// expect(Array.isArray(res.body)).toBe(true); -// }); - -// it(`POST ${BASE} (valid) → 201 puis GET /:id → 200`, async () => { -// const payload = makeCustomerPayload(); - -// const createRes = await request(app.getHttpServer()).post(BASE).send(payload); -// if (createRes.status !== 201) { - -// console.log('Create error:', createRes.body || createRes.text); -// } -// expect(createRes.status).toBe(201); - -// expect(createRes.body).toEqual( -// expect.objectContaining({ -// id: expect.any(Number), -// user_id: expect.any(String), -// invoice_id: payload.invoice_id, -// }) -// ); -// expect(createRes.body.user_id).toMatch(/^[0-9a-fA-F-]{36}$/); - -// createdId = createRes.body.id; - -// const getRes = await request(app.getHttpServer()).get(`${BASE}/${createdId}`); -// expect(getRes.status).toBe(200); -// expect(getRes.body).toEqual(expect.objectContaining({ id: createdId })); -// }); - -// it(`PATCH ${BASE}/:id → 200 (first_name mis à jour)`, async () => { -// if (!createdId) { -// const create = await request(app.getHttpServer()).post(BASE).send(makeCustomerPayload()); -// expect(create.status).toBe(201); -// createdId = create.body.id; -// } - -// const patchRes = await request(app.getHttpServer()) -// .patch(`${BASE}/${createdId}`) -// .send({ first_name: 'Mithrandir' }); -// expect([200, 204]).toContain(patchRes.status); - -// const getRes = await request(app.getHttpServer()).get(`${BASE}/${createdId}`); -// expect(getRes.status).toBe(200); -// expect(getRes.body.first_name ?? 'Mithrandir').toBe('Mithrandir'); -// }); - -// it(`GET ${BASE}/:id (not found) → 404/400`, async () => { -// const res = await request(app.getHttpServer()).get(`${BASE}/999999`); -// expect([404, 400]).toContain(res.status); -// }); - -// it(`POST ${BASE} (invalid payload) → 400`, async () => { -// const res = await request(app.getHttpServer()).post(BASE).send({}); -// expect(res.status).toBeGreaterThanOrEqual(400); -// expect(res.status).toBeLessThan(500); -// }); - -// it(`DELETE ${BASE}/:id → 200/204`, async () => { -// let id = createdId; -// if (!id) { -// const create = await request(app.getHttpServer()).post(BASE).send(makeCustomerPayload()); -// expect(create.status).toBe(201); -// id = create.body.id; -// } - -// const del = await request(app.getHttpServer()).delete(`${BASE}/${id}`); -// expect([200, 204]).toContain(del.status); -// if (createdId === id) createdId = null; -// }); -// }); diff --git a/test/employees.e2e-spec.ts b/test/employees.e2e-spec.ts deleted file mode 100644 index f47cd3b..0000000 --- a/test/employees.e2e-spec.ts +++ /dev/null @@ -1,105 +0,0 @@ -// test/employees.e2e-spec.ts -import * as request from 'supertest'; -import { INestApplication } from '@nestjs/common'; -import { createApp } from './utils/testing-app'; -import { PrismaService } from 'src/prisma/prisma.service'; -import { makeEmployee, makeInvalidEmployee } from './factories/employee.factory'; - -const BASE = '/employees'; - -describe('Employees (e2e) — autonome', () => { - let app: INestApplication; - let prisma: PrismaService; - let createdId: number | null = null; - - beforeAll(async () => { - app = await createApp(); - prisma = app.get(PrismaService); - }); - - afterAll(async () => { - if (createdId) { - try { await prisma.employees.delete({ where: { id: createdId } }); } catch {} - } - await app.close(); - await prisma.$disconnect(); - }); - - it(`GET ${BASE} → 200 (array)`, async () => { - const res = await request(app.getHttpServer()).get(BASE); - expect(res.status).toBe(200); - expect(Array.isArray(res.body)).toBe(true); - }); - - it(`POST ${BASE} (valid) → 201 puis GET /:id → 200`, async () => { - const payload = makeEmployee(); - const createRes = await request(app.getHttpServer()).post(BASE).send(payload); - - if (createRes.status !== 201) { - // aide debug ponctuelle - // eslint-disable-next-line no-console - console.log('Create error:', createRes.body || createRes.text); - } - expect(createRes.status).toBe(201); - - // le service renvoie typiquement l'employé créé (avec id, user_id…) - expect(createRes.body).toEqual( - expect.objectContaining({ - id: expect.any(Number), - user_id: expect.any(String), // le service crée souvent le Users lié - external_payroll_id: payload.external_payroll_id, - company_code: payload.company_code, - }) - ); - - createdId = createRes.body.id; - - const getRes = await request(app.getHttpServer()).get(`${BASE}/${createdId}`); - expect(getRes.status).toBe(200); - expect(getRes.body).toEqual(expect.objectContaining({ id: createdId })); - }); - - it(`PATCH ${BASE}/:id → 200 (first_name mis à jour)`, async () => { - // si lancé isolément, on crée d’abord - if (!createdId) { - const created = await request(app.getHttpServer()).post(BASE).send(makeEmployee()); - expect(created.status).toBe(201); - createdId = created.body.id; - } - - const patchRes = await request(app.getHttpServer()) - .patch(`${BASE}/${createdId}`) - .send({ first_name: 'Samwise' }); - expect([200, 202, 204]).toContain(patchRes.status); - - const getRes = await request(app.getHttpServer()).get(`${BASE}/${createdId}`); - expect(getRes.status).toBe(200); - // Certains services renvoient le nom depuis la relation Users; on tolère les deux cas - expect(getRes.body.first_name ?? 'Samwise').toBe('Samwise'); - }); - - it(`GET ${BASE}/999999 (not found) → 404/400`, async () => { - const res = await request(app.getHttpServer()).get(`${BASE}/999999`); - expect([404, 400]).toContain(res.status); - }); - - it(`POST ${BASE} (invalid payload) → 400`, async () => { - const res = await request(app.getHttpServer()).post(BASE).send(makeInvalidEmployee()); - expect(res.status).toBeGreaterThanOrEqual(400); - expect(res.status).toBeLessThan(500); - }); - - it(`DELETE ${BASE}/:id → 200/204`, async () => { - let id = createdId; - if (!id) { - const created = await request(app.getHttpServer()).post(BASE).send(makeEmployee()); - expect(created.status).toBe(201); - id = created.body.id; - } - - const del = await request(app.getHttpServer()).delete(`${BASE}/${id}`); - expect([200, 204]).toContain(del.status); - - if (createdId === id) createdId = null; - }); -}); diff --git a/test/expenses-approval.e2e-spec.ts b/test/expenses-approval.e2e-spec.ts deleted file mode 100644 index f7ac6b5..0000000 --- a/test/expenses-approval.e2e-spec.ts +++ /dev/null @@ -1,79 +0,0 @@ -const request = require('supertest'); -import { INestApplication } from '@nestjs/common'; -import { PrismaService } from 'src/prisma/prisma.service'; -import { createApp } from './utils/testing-app'; - -// NB: le controller est @Controller('Expenses') (E majuscule) -const BASE = '/Expenses'; - -describe('Expenses approval (e2e)', () => { - let app: INestApplication; - let prisma: PrismaService; - - let timesheetId: number; - let bankCodeExpenseId: number; - let expenseId: number; - - beforeAll(async () => { - app = await createApp(); - prisma = app.get(PrismaService); - - // 1) bank_code catégorie EXPENSE (évite MILEAGE pour ne pas dépendre du service de mileage) - const bc = await prisma.bankCodes.findFirst({ - where: { categorie: 'EXPENSE' }, - select: { id: true }, - }); - if (!bc) throw new Error('Aucun bank code EXPENSE trouvé'); - bankCodeExpenseId = bc.id; - - // 2) timesheet existant - const ts = await prisma.timesheets.findFirst({ select: { id: true } }); - if (!ts) throw new Error('Aucun timesheet trouvé pour créer une expense'); - timesheetId = ts.id; - - // 3) crée une expense - const payload = { - timesheet_id: timesheetId, - bank_code_id: bankCodeExpenseId, - date: '2024-02-10T00:00:00.000Z', - amount: 42, // int côté DTO - description: 'Approval test expense', - }; - const create = await request(app.getHttpServer()).post(BASE).send(payload); - if (create.status !== 201) { - // eslint-disable-next-line no-console - console.log('Create expense error:', create.body || create.text); - } - expect(create.status).toBe(201); - expenseId = create.body.id; - }); - - afterAll(async () => { - await app.close(); - await prisma.$disconnect(); - }); - - it(`PATCH ${BASE}/:id/approval → 200 (true)`, async () => { - const res = await request(app.getHttpServer()) - .patch(`${BASE}/${expenseId}/approval`) - .send({ is_approved: true }); - expect(res.status).toBe(200); - expect(res.body?.is_approved).toBe(true); - }); - - it(`PATCH ${BASE}/:id/approval → 200 (false)`, async () => { - const res = await request(app.getHttpServer()) - .patch(`${BASE}/${expenseId}/approval`) - .send({ is_approved: false }); - expect(res.status).toBe(200); - expect(res.body?.is_approved).toBe(false); - }); - - it(`PATCH ${BASE}/:id/approval (invalid) → 400`, async () => { - const res = await request(app.getHttpServer()) - .patch(`${BASE}/${expenseId}/approval`) - .send({ is_approved: 'nope' }); - expect(res.status).toBeGreaterThanOrEqual(400); - expect(res.status).toBeLessThan(500); - }); -}); diff --git a/test/expenses.e2e-spec.ts b/test/expenses.e2e-spec.ts deleted file mode 100644 index d4a5cb6..0000000 --- a/test/expenses.e2e-spec.ts +++ /dev/null @@ -1,120 +0,0 @@ -import * as request from 'supertest'; -import { INestApplication } from '@nestjs/common'; -import { createApp } from './utils/testing-app'; -import { PrismaService } from 'src/prisma/prisma.service'; -import { makeExpense, makeInvalidExpense } from './factories/expense.factory'; - -const BASE = '/Expenses'; - -describe('Expenses (e2e) — autonome', () => { - let app: INestApplication; - let prisma: PrismaService; - let createdId: number | null = null; - - let tsId: number; - let bcExpenseId: number; // categorie='EXPENSE' & type != 'MILEAGE' - - beforeAll(async () => { - app = await createApp(); - prisma = app.get(PrismaService); - - const ts = await prisma.timesheets.findFirst({ select: { id: true } }); - if (!ts) throw new Error('No timesheet found — seed timesheets first.'); - tsId = ts.id; - - const bc = await prisma.bankCodes.findFirst({ - where: { - categorie: 'EXPENSE', - NOT: { type: 'MILEAGE' }, // évite la branche mileageService - }, - select: { id: true }, - }); - if (!bc) throw new Error("No non-MILEAGE EXPENSE bank code found — seed bank codes first."); - bcExpenseId = bc.id; - }); - - afterAll(async () => { - if (createdId) { - try { await prisma.expenses.delete({ where: { id: createdId } }); } catch {} - } - await app.close(); - await prisma.$disconnect(); - }); - - it(`GET ${BASE} → 200 (array)`, async () => { - const res = await request(app.getHttpServer()).get(BASE); - expect(res.status).toBe(200); - expect(Array.isArray(res.body)).toBe(true); - }); - - it(`POST ${BASE} (valid) → 201 puis GET /:id → 200`, async () => { - const payload = makeExpense(tsId, bcExpenseId); - const createRes = await request(app.getHttpServer()).post(BASE).send(payload); - - if (createRes.status !== 201) { - console.log('Create error:', createRes.body || createRes.text); - } - expect(createRes.status).toBe(201); - expect(createRes.body).toEqual( - expect.objectContaining({ - id: expect.any(Number), - timesheet_id: tsId, - bank_code_id: bcExpenseId, - }) - ); - createdId = createRes.body.id; - - const getRes = await request(app.getHttpServer()).get(`${BASE}/${createdId}`); - expect(getRes.status).toBe(200); - expect(getRes.body).toEqual(expect.objectContaining({ id: createdId })); - }); - - it(`PATCH ${BASE}/:id → 200 (amount/description mis à jour)`, async () => { - if (!createdId) { - const created = await request(app.getHttpServer()) - .post(BASE) - .send(makeExpense(tsId, bcExpenseId)); - expect(created.status).toBe(201); - createdId = created.body.id; - } - - const updated = { amount: 123, description: 'Updated expense' }; // amount INT - const patchRes = await request(app.getHttpServer()) - .patch(`${BASE}/${createdId}`) - .send(updated); - expect([200, 204]).toContain(patchRes.status); - - const getRes = await request(app.getHttpServer()).get(`${BASE}/${createdId}`); - expect(getRes.status).toBe(200); - if (getRes.body?.amount !== undefined) { - expect(Number(getRes.body.amount)).toBe(updated.amount); - } - if (getRes.body?.description !== undefined) expect(getRes.body.description).toBe(updated.description); - }); - - it(`GET ${BASE}/999999 (not found) → 404/400`, async () => { - const res = await request(app.getHttpServer()).get(`${BASE}/999999`); - expect([404, 400]).toContain(res.status); - }); - - it(`POST ${BASE} (invalid payload) → 400`, async () => { - const res = await request(app.getHttpServer()).post(BASE).send(makeInvalidExpense()); - expect(res.status).toBeGreaterThanOrEqual(400); - expect(res.status).toBeLessThan(500); - }); - - it(`DELETE ${BASE}/:id → 200/204`, async () => { - let id = createdId; - if (!id) { - const created = await request(app.getHttpServer()) - .post(BASE) - .send(makeExpense(tsId, bcExpenseId)); - expect(created.status).toBe(201); - id = created.body.id; - } - - const del = await request(app.getHttpServer()).delete(`${BASE}/${id}`); - expect([200, 204]).toContain(del.status); - if (createdId === id) createdId = null; - }); -}); diff --git a/test/factories/bank-code.factory.ts b/test/factories/bank-code.factory.ts deleted file mode 100644 index 2e36c5e..0000000 --- a/test/factories/bank-code.factory.ts +++ /dev/null @@ -1,26 +0,0 @@ -export type BankCodePayload = { - type: string; - categorie: string; - modifier: number; - bank_code: string; -}; - -const randNum = (min = 1, max = 999) => - Math.floor(Math.random() * (max - min + 1)) + min; - -const randCode = () => `G${randNum(10, 999)}`; - -export function makeBankCode(overrides: Partial = {}): BankCodePayload { - return { - type: 'regular', // ex.: regular, vacation, sick... - categorie: 'shift', // ex.: shift, expense, leave - modifier: 1.5, // ex.: 0, 0.72, 1, 1.5, 2 - bank_code: randCode(), // ex.: G1, G345, G501... - ...overrides, - }; -} - -// Délibérément invalide pour déclencher 400 via ValidationPipe -export function makeInvalidBankCode(): Record { - return {}; // manque tous les champs requis -} diff --git a/test/factories/employee.factory.ts b/test/factories/employee.factory.ts deleted file mode 100644 index d18f873..0000000 --- a/test/factories/employee.factory.ts +++ /dev/null @@ -1,46 +0,0 @@ -// test/factories/employee.factory.ts -export type EmployeePayload = { - // user_id?: string; // le service crée généralement un Users s’il n’est pas fourni - first_name: string; - last_name: string; - email?: string; - phone_number: number; - residence?: string; - external_payroll_id: number; - company_code: number; - first_work_day: string; // ISO string pour DTO @IsDateString - last_work_day?: string; -}; - -const randInt = (min: number, max: number) => - Math.floor(Math.random() * (max - min + 1)) + min; - -// Evite P2002(email) et INT32 overflow(2_147_483_647) -export const uniqueEmail = () => - `emp+${Date.now()}_${Math.random().toString(36).slice(2,8)}@test.local`; -export const safePhone = () => - randInt(100_000_000, 999_999_999); // 9 chiffres < INT32 -const uniqueExtPayroll = () => randInt(1_000_000, 9_999_999); -const uniqueCompanyCode = () => randInt(100_000, 999_999); -const iso = (y: number, m: number, d: number) => - new Date(Date.UTC(y, m - 1, d)).toISOString(); - -export function makeEmployee(overrides: Partial = {}): EmployeePayload { - return { - first_name: 'Frodo', - last_name: 'Baggins', - email: uniqueEmail(), - phone_number: safePhone(), - residence: '1 Bagshot Row, Hobbiton, The Shire', - external_payroll_id: uniqueExtPayroll(), - company_code: uniqueCompanyCode(), - first_work_day: iso(2023, 1, 15), - // last_work_day: iso(2023, 12, 31), - ...overrides, - }; -} - -// volontairement invalide pour déclencher 400 via ValidationPipe -export function makeInvalidEmployee(): Record { - return { first_name: '', external_payroll_id: 'nope' }; -} diff --git a/test/factories/expense.factory.ts b/test/factories/expense.factory.ts deleted file mode 100644 index 6791afe..0000000 --- a/test/factories/expense.factory.ts +++ /dev/null @@ -1,37 +0,0 @@ -// test/factories/expense.factory.ts -export type ExpensePayload = { - timesheet_id: number; - bank_code_id: number; // bank code categorie='EXPENSE' and type != 'MILEAGE' - date: string; // ISO date - amount: number; // INT (DTO: @IsInt) - description: string; - is_approved?: boolean; - supervisor_comment?: string; -}; - -const randInt = (min: number, max: number) => - Math.floor(Math.random() * (max - min + 1)) + min; - -const isoDate = (y: number, m: number, d: number) => - new Date(Date.UTC(y, m - 1, d, 0, 0, 0)).toISOString(); - -export function makeExpense( - tsId: number, - bcId: number, - overrides: Partial = {} -): ExpensePayload { - return { - timesheet_id: tsId, - bank_code_id: bcId, - date: isoDate(2024, 6, randInt(1, 28)), - amount: randInt(5, 200), - description: `Expense ${randInt(100, 999)}`, - supervisor_comment: 'N/A', - ...overrides, - }; -} - -// volontairement invalide pour 400 via ValidationPipe -export function makeInvalidExpense(): Record { - return { amount: 'oops', timesheet_id: 'nope' }; -} diff --git a/test/factories/leave-request.factory.ts b/test/factories/leave-request.factory.ts deleted file mode 100644 index e69de29..0000000 diff --git a/test/factories/shift.factory.ts b/test/factories/shift.factory.ts deleted file mode 100644 index ef6e833..0000000 --- a/test/factories/shift.factory.ts +++ /dev/null @@ -1,43 +0,0 @@ -// test/factories/shift.factory.ts -export type ShiftPayload = { - timesheet_id: number; - bank_code_id: number; - date: string; // ISO date (jour) - start_time: string; // ISO datetime (heure) - end_time: string; // ISO datetime (heure) - description: string; // requis par le DTO -}; - -const randInt = (min: number, max: number) => - Math.floor(Math.random() * (max - min + 1)) + min; - -const isoDate = (y: number, m: number, d: number) => - new Date(Date.UTC(y, m - 1, d, 0, 0, 0)).toISOString(); - -const isoTime = (h: number, m = 0) => - new Date(Date.UTC(1970, 0, 1, h, m, 0)).toISOString(); - -export function makeShift( - tsId: number, - bcId: number, - overrides: Partial = {} -): ShiftPayload { - // 8h pile pour ne pas dépasser DAILY_LIMIT_HOURS (8 par défaut) - const startH = 8; - const endH = 16; - - return { - timesheet_id: tsId, - bank_code_id: bcId, - date: isoDate(2024, 5, randInt(1, 28)), - start_time: isoTime(startH), - end_time: isoTime(endH), - description: `Shift ${randInt(100, 999)}`, - ...overrides, - }; -} - -// payload invalide pour 400 via ValidationPipe -export function makeInvalidShift(): Record { - return { timesheet_id: 'nope' }; // types volontairement faux -} diff --git a/test/factories/timesheet.factory.ts b/test/factories/timesheet.factory.ts deleted file mode 100644 index dbcae3c..0000000 --- a/test/factories/timesheet.factory.ts +++ /dev/null @@ -1,39 +0,0 @@ -// test/factories/timesheet.factory.ts - -export type TimesheetPayload = { - employee_id: number; - is_approved?: boolean; -}; - -/** - * Construit un payload valide pour POST /timesheets. - * Par défaut, is_approved=false. - */ -export function makeTimesheet( - employeeId: number, - overrides: Partial = {} -): TimesheetPayload { - return { - employee_id: employeeId, - is_approved: false, - ...overrides, - }; -} - -/** - * Payload délibérément invalide pour déclencher un 400 via ValidationPipe. - */ -export function makeInvalidTimesheet(): Record { - return { employee_id: 'not-a-number' }; -} - -/** - * Helper pour récupérer un employee_id existant - * sans importer les types Prisma dans le factory. - */ -export async function pickAnyEmployeeId( - prisma: { employees: { findFirst: (args?: any) => Promise<{ id: number } | null> } } -): Promise { - const emp = await prisma.employees.findFirst({ select: { id: true } }); - return emp?.id ?? null; -} diff --git a/test/health.e2e-spec.ts b/test/health.e2e-spec.ts deleted file mode 100644 index f65933e..0000000 --- a/test/health.e2e-spec.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { INestApplication } from '@nestjs/common'; -import { Test, TestingModule } from '@nestjs/testing'; -import * as request from 'supertest'; -import { AppModule } from './../src/app.module'; -import { PrismaService } from 'src/prisma/prisma.service'; - -describe('HealthController (e2e)', () => { - let app: INestApplication; - - beforeAll(async () => { - const moduleFixture: TestingModule = await Test.createTestingModule({ - imports: [AppModule], - }).compile(); - - app = moduleFixture.createNestApplication(); - await app.init(); - }); - - it('/health (GET) → 200 & { status: "ok" }', () => { - return request(app.getHttpServer()) - .get('/health') - .expect(200) - .expect({ status: 'ok' }); - }); - - afterAll(async () => { - await app.close(); - const prisma = app.get(PrismaService); - await prisma.$disconnect(); - }); -}); diff --git a/test/jest-e2e.json b/test/jest-e2e.json deleted file mode 100644 index 2def0a7..0000000 --- a/test/jest-e2e.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "preset": "ts-jest", - "rootDir": "..", - "testEnvironment": "node", - "testRegex": ".e2e-spec.ts$", - "moduleFileExtensions": ["ts", "js", "json"], - "transform": { - "^.+\\.(t|j)s$": "ts-jest" - }, - "moduleNameMapper": { - "^src/(.*)$": "/src/$1" - }, - "setupFiles": ["dotenv/config"], - "setupFilesAfterEnv": ["/test/jest-setup.ts"], - "moduleDirectories": ["node_modules", ""], - "testTimeout": 30000, - "maxWorkers": 1, - "collectCoverage": false, - "coverageDirectory": "coverage-e2e", - "coverageReporters": ["text", "lcov"], - "coveragePathIgnorePatterns": [ - "/node_modules/", - "/dist/", - "src/modules/pay-periods/services/", - "src/modules/exports/services/csv-exports.service.ts", - "src/modules/notifications/services/", - "src/modules/leave-requests/services/" - ], - "coverageThreshold": { - "global": {} - } -} diff --git a/test/jest-setup.ts b/test/jest-setup.ts deleted file mode 100644 index d9df1ea..0000000 --- a/test/jest-setup.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { randomUUID, randomFillSync } from 'crypto'; - -if (!(globalThis as any).crypto) { - (globalThis as any).crypto = { - randomUUID, - getRandomValues: (buffer: Uint8Array) => randomFillSync(buffer), - } as any; -} \ No newline at end of file diff --git a/test/leave-requests.e2e-spec.ts b/test/leave-requests.e2e-spec.ts deleted file mode 100644 index ca8a64c..0000000 --- a/test/leave-requests.e2e-spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -import * as request from 'supertest'; -import { INestApplication } from '@nestjs/common'; -import { createApp } from './utils/testing-app'; -// import { resetDb } from './utils/reset-db'; -import { PrismaService } from 'src/prisma/prisma.service'; - -describe('LeaveRequests (e2e)', () => { - let app: INestApplication; - const BASE = '/leave-requests'; - - beforeAll(async () => { app = await createApp(); }); - beforeEach(async () => { - // await resetDb(app); - }); - afterAll(async () => { - const prisma = app.get(PrismaService); - await app.close(); - await prisma.$disconnect(); - }); - - it(`GET ${BASE} → 200`, async () => { - const res = await request(app.getHttpServer()).get(BASE); - expect(res.status).toBe(200); - }); -}); diff --git a/test/pay-periods-approval.e2e-spec.ts b/test/pay-periods-approval.e2e-spec.ts deleted file mode 100644 index e061c16..0000000 --- a/test/pay-periods-approval.e2e-spec.ts +++ /dev/null @@ -1,143 +0,0 @@ -// // test/pay-periods-approval.e2e-spec.ts -// const supertest = require('supertest'); -// import { INestApplication } from '@nestjs/common'; -// import { PrismaService } from 'src/prisma/prisma.service'; -// import { createApp } from './utils/testing-app'; -// import { makeEmployee } from './factories/employee.factory'; -// import { makeTimesheet } from './factories/timesheet.factory'; - -// describe('PayPeriods approval (e2e)', () => { -// const BASE = '/pay-periods'; -// let app: INestApplication; -// let prisma: PrismaService; - -// let periodYear: number; -// let periodNumber: number; - -// let employeeId: number; -// let timesheetId: number; -// let shiftId: number; -// let expenseId: number; - -// const isoDay = (d: Date) => -// new Date(Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate())).toISOString(); -// const isoTime = (h: number, m = 0) => -// new Date(Date.UTC(1970, 0, 1, h, m, 0)).toISOString(); - -// beforeAll(async () => { -// app = await createApp(); -// prisma = app.get(PrismaService); - -// // 1) Récupère un pay period existant -// const period = await prisma.payPeriods.findFirst({ orderBy: { period_number: 'asc' } }); -// if (!period) throw new Error('Aucun pay period en DB (seed requis).'); - -// periodYear = period.year; -// periodNumber = period.period_number; - -// // 2) Crée un employé + timesheet (non approuvé) -// const empRes = await supertest(app.getHttpServer()) -// .post('/employees') -// .send(makeEmployee()); -// if (empRes.status !== 201) { -// // eslint-disable-next-line no-console -// console.warn('Create employee error:', empRes.body || empRes.text); -// throw new Error('Impossible de créer un employé pour le test pay-periods.'); -// } -// employeeId = empRes.body.id; - -// const tsRes = await supertest(app.getHttpServer()) -// .post('/timesheets') -// .send(makeTimesheet(employeeId, { is_approved: false })); -// if (tsRes.status !== 201) { -// // eslint-disable-next-line no-console -// console.warn('Create timesheet error:', tsRes.body || tsRes.text); -// throw new Error('Impossible de créer un timesheet pour le test pay-periods.'); -// } -// timesheetId = tsRes.body.id; - -// // 3) Bank codes -// const bcShift = await prisma.bankCodes.findFirst({ -// where: { categorie: 'SHIFT' }, -// select: { id: true }, -// }); -// if (!bcShift) throw new Error('Aucun bank code SHIFT trouvé.'); -// const bcExpense = await prisma.bankCodes.findFirst({ -// where: { categorie: 'EXPENSE' }, -// select: { id: true }, -// }); -// if (!bcExpense) throw new Error('Aucun bank code EXPENSE trouvé.'); - -// // 4) Crée 1 shift + 1 expense DANS la période choisie -// const dateISO = isoDay(period.start_date); - -// const shiftRes = await supertest(app.getHttpServer()) -// .post('/shifts') -// .send({ -// timesheet_id: timesheetId, -// bank_code_id: bcShift.id, -// date: dateISO, -// start_time: isoTime(9), -// end_time: isoTime(17), -// description: 'PP approval shift', -// }); -// if (shiftRes.status !== 201) { -// // eslint-disable-next-line no-console -// console.warn('Create shift error:', shiftRes.body || shiftRes.text); -// throw new Error('Création shift échouée.'); -// } -// shiftId = shiftRes.body.id; - -// const expenseRes = await supertest(app.getHttpServer()) -// .post('/Expenses') // <- respecte ta casse de route -// .send({ -// timesheet_id: timesheetId, -// bank_code_id: bcExpense.id, -// date: dateISO, -// amount: 42, -// description: 'PP approval expense', -// is_approved: false, -// }); -// if (expenseRes.status !== 201) { -// // eslint-disable-next-line no-console -// console.warn('Create expense error:', expenseRes.body || expenseRes.text); -// throw new Error('Création expense échouée.'); -// } -// expenseId = expenseRes.body.id; -// }); - -// afterAll(async () => { -// await app.close(); -// await prisma.$disconnect(); -// }); - -// it(`PATCH ${BASE}/:year/:periodNumber/approval → 200 (cascade approval)`, async () => { -// const res = await supertest(app.getHttpServer()) -// .patch(`${BASE}/${periodYear}/${periodNumber}/approval`) -// .send(); // aucun body requis par ton contrôleur -// expect([200, 204]).toContain(res.status); -// if (res.body?.message) { -// expect(String(res.body.message)).toContain(`${periodYear}-${periodNumber}`); -// } - -// // Vérifie cascade: -// const tsCheck = await supertest(app.getHttpServer()).get(`/timesheets/${timesheetId}`); -// expect(tsCheck.status).toBe(200); -// expect(tsCheck.body?.is_approved).toBe(true); - -// const shiftCheck = await supertest(app.getHttpServer()).get(`/shifts/${shiftId}`); -// expect(shiftCheck.status).toBe(200); -// expect(shiftCheck.body?.is_approved).toBe(true); - -// const expCheck = await supertest(app.getHttpServer()).get(`/Expenses/${expenseId}`); -// expect(expCheck.status).toBe(200); -// expect(expCheck.body?.is_approved).toBe(true); -// }); - -// it(`PATCH ${BASE}/2099/999/approval → 404 (period not found)`, async () => { -// const bad = await supertest(app.getHttpServer()) -// .patch(`${BASE}/2099/999/approval`) -// .send(); -// expect(bad.status).toBe(404); -// }); -// }); diff --git a/test/pay-periods.e2e-spec.ts b/test/pay-periods.e2e-spec.ts deleted file mode 100644 index 0a78a5c..0000000 --- a/test/pay-periods.e2e-spec.ts +++ /dev/null @@ -1,26 +0,0 @@ -import * as request from 'supertest'; -import { INestApplication } from '@nestjs/common'; -import { createApp } from './utils/testing-app'; -// import { resetDb } from './utils/reset-db'; -import { PrismaService } from 'src/prisma/prisma.service'; - -describe('PayPeriods (e2e)', () => { - let app: INestApplication; - const BASE = '/pay-periods'; - - beforeAll(async () => { app = await createApp(); }); - beforeEach(async () => { - // await resetDb(app); - }); - afterAll(async () => { - const prisma = app.get(PrismaService); - await app.close(); - await prisma.$disconnect(); - }); - - it(`GET ${BASE} → 200`, async () => { - const res = await request(app.getHttpServer()).get(BASE); - expect(res.status).toBe(200); - //ajouter ici GET /pay-periods/date/:date etc. - }); -}); diff --git a/test/shifts-aproval.e2e-spec.ts b/test/shifts-aproval.e2e-spec.ts deleted file mode 100644 index 94b1b94..0000000 --- a/test/shifts-aproval.e2e-spec.ts +++ /dev/null @@ -1,101 +0,0 @@ -const request = require('supertest'); -import { INestApplication } from '@nestjs/common'; -import { PrismaService } from 'src/prisma/prisma.service'; -import { createApp } from './utils/testing-app'; -import { makeEmployee } from './factories/employee.factory'; -import { makeTimesheet } from './factories/timesheet.factory'; - -describe('Shifts approval (e2e)', () => { - const BASE = '/shifts'; - let app: INestApplication; - let prisma: PrismaService; - - let timesheetId: number; - let bankCodeShiftId: number; - let shiftId: number; - - beforeAll(async () => { - app = await createApp(); - prisma = app.get(PrismaService); - - // 1) bank_code SHIFT - const bc = await prisma.bankCodes.findFirst({ - where: { categorie: 'SHIFT' }, - select: { id: true }, - }); - if (!bc) throw new Error('Aucun bank code SHIFT trouvé'); - bankCodeShiftId = bc.id; - - // 2) timesheet existant ou création rapide - const ts = await prisma.timesheets.findFirst({ select: { id: true } }); - if (ts) { - timesheetId = ts.id; - } else { - // crée un employé + timesheet via HTTP - const empRes = await request(app.getHttpServer()) - .post('/employees') - .send(makeEmployee()); - if (empRes.status !== 201) { - // eslint-disable-next-line no-console - console.warn('Création employé échouée:', empRes.body || empRes.text); - throw new Error('Setup employees pour shifts-approval échoué'); - } - const tsRes = await request(app.getHttpServer()) - .post('/timesheets') - .send(makeTimesheet(empRes.body.id)); - if (tsRes.status !== 201) { - // eslint-disable-next-line no-console - console.warn('Création timesheet échouée:', tsRes.body || tsRes.text); - throw new Error('Setup timesheet pour shifts-approval échoué'); - } - timesheetId = tsRes.body.id; - } - - // 3) crée un shift à approuver - const payload = { - timesheet_id: timesheetId, - bank_code_id: bankCodeShiftId, - date: '2024-01-15T00:00:00.000Z', - start_time: '2024-01-15T08:00:00.000Z', - end_time: '2024-01-15T16:00:00.000Z', - description: 'Approval test shift', - }; - const create = await request(app.getHttpServer()).post(BASE).send(payload); - if (create.status !== 201) { - // eslint-disable-next-line no-console - console.log('Create shift error:', create.body || create.text); - } - expect(create.status).toBe(201); - shiftId = create.body.id; - }); - - afterAll(async () => { - await app.close(); - await prisma.$disconnect(); - }); - - it(`PATCH ${BASE}/:id/approval → 200 (true)`, async () => { - const res = await request(app.getHttpServer()) - .patch(`${BASE}/${shiftId}/approval`) - .send({ is_approved: true }); - expect(res.status).toBe(200); - expect(res.body?.is_approved).toBe(true); - }); - - it(`PATCH ${BASE}/:id/approval → 200 (false)`, async () => { - const res = await request(app.getHttpServer()) - .patch(`${BASE}/${shiftId}/approval`) - .send({ is_approved: false }); - expect(res.status).toBe(200); - expect(res.body?.is_approved).toBe(false); - }); - - it(`PATCH ${BASE}/:id/approval (invalid) → 400`, async () => { - const res = await request(app.getHttpServer()) - .patch(`${BASE}/${shiftId}/approval`) - // ParseBoolPipe doit rejeter une string non "true/false" - .send({ is_approved: 'notabool' }); - expect(res.status).toBeGreaterThanOrEqual(400); - expect(res.status).toBeLessThan(500); - }); -}); diff --git a/test/shifts.e2e-spec.ts b/test/shifts.e2e-spec.ts deleted file mode 100644 index 2b26c44..0000000 --- a/test/shifts.e2e-spec.ts +++ /dev/null @@ -1,121 +0,0 @@ -import * as request from 'supertest'; -import { INestApplication } from '@nestjs/common'; -import { createApp } from './utils/testing-app'; -import { PrismaService } from 'src/prisma/prisma.service'; -import { makeShift, makeInvalidShift } from './factories/shift.factory'; - -const BASE = '/shifts'; - -describe('Shifts (e2e) — autonome', () => { - let app: INestApplication; - let prisma: PrismaService; - let createdId: number | null = null; - - // FKs existants - let tsId: number; // any timesheet - let bcShiftId: number; // any bank code with categorie='SHIFT' - - beforeAll(async () => { - app = await createApp(); - prisma = app.get(PrismaService); - - // récupère un timesheet existant - const ts = await prisma.timesheets.findFirst({ select: { id: true } }); - if (!ts) throw new Error('No timesheet found — seed timesheets first.'); - tsId = ts.id; - - // récupère un bank code SHIFT (ta seed utilise 'SHIFT' en MAJ) - const bc = await prisma.bankCodes.findFirst({ - where: { categorie: 'SHIFT' }, - select: { id: true }, - }); - if (!bc) throw new Error('No SHIFT bank code found — seed bank codes first.'); - bcShiftId = bc.id; - }); - - afterAll(async () => { - if (createdId) { - try { - await prisma.shifts.delete({ where: { id: createdId } }); - } catch {} - } - await app.close(); - await prisma.$disconnect(); - }); - - it(`POST ${BASE} (valid) → 201 puis GET /:id → 200`, async () => { - const payload = makeShift(tsId, bcShiftId); - - const createRes = await request(app.getHttpServer()) - .post(BASE) - .send(payload); - - if (createRes.status !== 201) { - console.log('Create error:', createRes.body || createRes.text); - } - expect(createRes.status).toBe(201); - expect(createRes.body).toEqual( - expect.objectContaining({ - id: expect.any(Number), - timesheet_id: tsId, - bank_code_id: bcShiftId, - }) - ); - createdId = createRes.body.id; - - const getRes = await request(app.getHttpServer()).get(`${BASE}/${createdId}`); - expect(getRes.status).toBe(200); - expect(getRes.body).toEqual(expect.objectContaining({ id: createdId })); - }); - - it(`PATCH ${BASE}/:id → 200 (description mise à jour)`, async () => { - if (!createdId) { - const created = await request(app.getHttpServer()) - .post(BASE) - .send(makeShift(tsId, bcShiftId)); - expect(created.status).toBe(201); - createdId = created.body.id; - } - - const patchRes = await request(app.getHttpServer()) - .patch(`${BASE}/${createdId}`) - .send({ description: 'Updated shift description' }); - - expect([200, 204]).toContain(patchRes.status); - - const getRes = await request(app.getHttpServer()).get(`${BASE}/${createdId}`); - expect(getRes.status).toBe(200); - // on tolère l’absence, mais si elle est là, on vérifie la valeur. - if (getRes.body?.description !== undefined) { - expect(getRes.body.description).toBe('Updated shift description'); - } - }); - - it(`GET ${BASE}/999999 (not found) → 404/400`, async () => { - const res = await request(app.getHttpServer()).get(`${BASE}/999999`); - expect([404, 400]).toContain(res.status); - }); - - it(`POST ${BASE} (invalid payload) → 400`, async () => { - const res = await request(app.getHttpServer()) - .post(BASE) - .send(makeInvalidShift()); - expect(res.status).toBeGreaterThanOrEqual(400); - expect(res.status).toBeLessThan(500); - }); - - it(`DELETE ${BASE}/:id → 200/204`, async () => { - let id = createdId; - if (!id) { - const created = await request(app.getHttpServer()) - .post(BASE) - .send(makeShift(tsId, bcShiftId)); - expect(created.status).toBe(201); - id = created.body.id; - } - - const del = await request(app.getHttpServer()).delete(`${BASE}/${id}`); - expect([200, 204]).toContain(del.status); - if (createdId === id) createdId = null; - }); -}); diff --git a/test/timesheets-approval.e2e-spec.ts b/test/timesheets-approval.e2e-spec.ts deleted file mode 100644 index 10a6b3d..0000000 --- a/test/timesheets-approval.e2e-spec.ts +++ /dev/null @@ -1,68 +0,0 @@ -const request = require('supertest'); -import { INestApplication } from '@nestjs/common'; -import { PrismaService } from 'src/prisma/prisma.service'; -import { createApp } from './utils/testing-app'; -import { makeEmployee } from './factories/employee.factory'; -import { makeTimesheet } from './factories/timesheet.factory'; - -describe('Timesheets approval (e2e)', () => { - const BASE = '/timesheets'; - let app: INestApplication; - let prisma: PrismaService; - - let timesheetId: number; - - beforeAll(async () => { - app = await createApp(); - prisma = app.get(PrismaService); - - // On crée un employé dédié pour ne dépendre d’aucun état antérieur - const emp = await request(app.getHttpServer()) - .post('/employees') - .send(makeEmployee()); - if (emp.status !== 201) { - // eslint-disable-next-line no-console - console.warn('Création employé échouée:', emp.body || emp.text); - throw new Error('Setup employees pour timesheets-approval échoué'); - } - - const ts = await request(app.getHttpServer()) - .post(BASE) - .send(makeTimesheet(emp.body.id)); - if (ts.status !== 201) { - // eslint-disable-next-line no-console - console.warn('Création timesheet échouée:', ts.body || ts.text); - throw new Error('Setup timesheet échoué'); - } - timesheetId = ts.body.id; - }); - - afterAll(async () => { - await app.close(); - await prisma.$disconnect(); - }); - - it(`PATCH ${BASE}/:id/approval → 200 (true)`, async () => { - const res = await request(app.getHttpServer()) - .patch(`${BASE}/${timesheetId}/approval`) - .send({ is_approved: true }); - expect(res.status).toBe(200); - expect(res.body?.is_approved).toBe(true); - }); - - it(`PATCH ${BASE}/:id/approval → 200 (false)`, async () => { - const res = await request(app.getHttpServer()) - .patch(`${BASE}/${timesheetId}/approval`) - .send({ is_approved: false }); - expect(res.status).toBe(200); - expect(res.body?.is_approved).toBe(false); - }); - - it(`PATCH ${BASE}/:id/approval (invalid) → 400`, async () => { - const res = await request(app.getHttpServer()) - .patch(`${BASE}/${timesheetId}/approval`) - .send({ is_approved: 'plop' }); - expect(res.status).toBeGreaterThanOrEqual(400); - expect(res.status).toBeLessThan(500); - }); -}); diff --git a/test/timesheets.e2e-spec.ts b/test/timesheets.e2e-spec.ts deleted file mode 100644 index 14dbc3d..0000000 --- a/test/timesheets.e2e-spec.ts +++ /dev/null @@ -1,112 +0,0 @@ -// test/timesheets.e2e-spec.ts -const request = require('supertest'); -import { INestApplication } from '@nestjs/common'; -import { PrismaService } from 'src/prisma/prisma.service'; -import { - makeTimesheet, - makeInvalidTimesheet, -} from './factories/timesheet.factory'; -import { makeEmployee } from './factories/employee.factory'; -import { createApp } from './utils/testing-app'; - -describe('Timesheets (e2e) — autonome', () => { - const BASE = '/timesheets'; - let app: INestApplication; - let employeeIdForCreation: number; - - beforeAll(async () => { - app = await createApp(); - - // ✅ Crée un employé dédié pour cette suite - const empRes = await request(app.getHttpServer()) - .post('/employees') - .send(makeEmployee()); - if (empRes.status !== 201) { - // eslint-disable-next-line no-console - console.warn('Impossible de créer un employé pour les tests timesheets:', empRes.body || empRes.text); - throw new Error('Setup employé échoué'); - } - employeeIdForCreation = empRes.body.id; - }); - - afterAll(async () => { - const prisma = app.get(PrismaService); - await app.close(); - await prisma.$disconnect(); - }); - - it(`GET ${BASE} → 200`, async () => { - const res = await request(app.getHttpServer()).get(BASE); - expect(res.status).toBe(200); - expect(Array.isArray(res.body)).toBe(true); - }); - - it(`POST ${BASE} (valid) → 201 puis GET /:id → 200`, async () => { - const payload = makeTimesheet(employeeIdForCreation, { is_approved: true }); - - const createRes = await request(app.getHttpServer()).post(BASE).send(payload); - if (createRes.status !== 201) { - // eslint-disable-next-line no-console - console.log('Create error:', createRes.body || createRes.text); - } - expect(createRes.status).toBe(201); - expect(createRes.body).toEqual( - expect.objectContaining({ - id: expect.any(Number), - employee_id: payload.employee_id, - }), - ); - const id = createRes.body.id; - - const getRes = await request(app.getHttpServer()).get(`${BASE}/${id}`); - expect(getRes.status).toBe(200); - expect(getRes.body).toEqual( - expect.objectContaining({ - id, - employee_id: payload.employee_id, - }), - ); - }); - - it(`PATCH ${BASE}/:id → 200 (is_approved toggled)`, async () => { - const created = await request(app.getHttpServer()) - .post(BASE) - .send(makeTimesheet(employeeIdForCreation)); - expect(created.status).toBe(201); - const id = created.body.id; - - const updated = await request(app.getHttpServer()) - .patch(`${BASE}/${id}`) - .send({ is_approved: true }); - expect(updated.status).toBe(200); - expect(updated.body).toEqual( - expect.objectContaining({ - id, - is_approved: true, - }), - ); - }); - - it(`POST ${BASE} (invalid payload) → 400`, async () => { - const bad = await request(app.getHttpServer()) - .post(BASE) - .send(makeInvalidTimesheet()); - - expect(bad.status).toBeGreaterThanOrEqual(400); - expect(bad.status).toBeLessThan(500); - }); - - it(`DELETE ${BASE}/:id → 200/204`, async () => { - const created = await request(app.getHttpServer()) - .post(BASE) - .send(makeTimesheet(employeeIdForCreation)); - expect(created.status).toBe(201); - const id = created.body.id; - - const delRes = await request(app.getHttpServer()).delete(`${BASE}/${id}`); - expect([200, 204]).toContain(delRes.status); - - const getRes = await request(app.getHttpServer()).get(`${BASE}/${id}`); - expect(getRes.status).toBe(404); - }); -}); diff --git a/test/utils/reset-db.ts b/test/utils/reset-db.ts deleted file mode 100644 index 4dd25f3..0000000 --- a/test/utils/reset-db.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { INestApplication } from '@nestjs/common'; -import { PrismaService } from 'src/prisma/prisma.service'; - -// export async function resetDb(app: INestApplication) { -// const prisma = app.get(PrismaService); -// const KEEP_USERS = process.env.E2E_KEEP_USERS === '1'; -// const excludes = ['_prisma_migrations', ...(KEEP_USERS ? ['users'] : [])]; -// const notIn = excludes.map(n => `'${n}'`).join(', '); -// const rows = await prisma.$queryRawUnsafe>(` -// SELECT table_name AS tablename -// FROM information_schema.tables -// WHERE table_schema = 'public' -// AND table_type = 'BASE TABLE' -// AND table_name NOT IN (${notIn}) -// `); - -// if (!rows.length) return; - -// const list = rows.map(r => `"public"."${r.tablename}"`).join(', '); -// await prisma.$executeRawUnsafe(`TRUNCATE TABLE ${list} RESTART IDENTITY CASCADE;`); -// } diff --git a/test/utils/testing-app.ts b/test/utils/testing-app.ts deleted file mode 100644 index 318fd93..0000000 --- a/test/utils/testing-app.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { INestApplication, ValidationPipe } from '@nestjs/common'; -import { Test } from '@nestjs/testing'; -import { AppModule } from 'src/app.module'; -// si tu overrides des guards, garde-les comme avant - -export async function createApp(): Promise { - const mod = await Test.createTestingModule({ imports: [AppModule] }).compile(); - const app = mod.createNestApplication(); - - app.useGlobalPipes(new ValidationPipe({ - whitelist: true, - transform: true, - transformOptions: { enableImplicitConversion: true }, - forbidNonWhitelisted: true, - validateCustomDecorators: true, - })); - - await app.init(); - return app; -}