clean(modules): modules file cleaning
This commit is contained in:
parent
e4447a138c
commit
8fdbdafd91
18
package.json
18
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",
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
@ -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 = <T,>(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();
|
||||
});
|
||||
|
|
@ -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());
|
||||
|
|
@ -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());
|
||||
|
|
@ -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());
|
||||
|
|
@ -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());
|
||||
|
|
@ -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<6F> (SKIP_LEAVE_REQUESTS=true)');
|
||||
// process.exit(0);
|
||||
// }
|
||||
|
||||
// const prisma = new PrismaClient();
|
||||
|
||||
// function dateOn(y: number, m: number, d: number) {
|
||||
// // stocke une date (@db.Date) <20> 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<41>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 <20> futur <20>
|
||||
|
||||
// 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());
|
||||
|
|
@ -1,87 +0,0 @@
|
|||
// import { PrismaClient, LeaveApprovalStatus, LeaveTypes } from '@prisma/client';
|
||||
|
||||
// if (process.env.SKIP_LEAVE_REQUESTS === 'true') {
|
||||
// console.log('?? Seed leave-requests ignor<6F> (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<6F> trouv<75>. Ex<45>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<75> 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());
|
||||
|
|
@ -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());
|
||||
|
|
@ -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());
|
||||
|
|
@ -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());
|
||||
|
|
@ -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());
|
||||
|
|
@ -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());
|
||||
|
|
@ -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());
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -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<App>;
|
||||
|
||||
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();
|
||||
});
|
||||
});
|
||||
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
|
@ -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> = {}): 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;
|
||||
// });
|
||||
// });
|
||||
|
|
@ -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;
|
||||
});
|
||||
});
|
||||
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
|
@ -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;
|
||||
});
|
||||
});
|
||||
|
|
@ -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> = {}): 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<string, unknown> {
|
||||
return {}; // manque tous les champs requis
|
||||
}
|
||||
|
|
@ -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> = {}): 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<string, unknown> {
|
||||
return { first_name: '', external_payroll_id: 'nope' };
|
||||
}
|
||||
|
|
@ -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> = {}
|
||||
): 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<string, unknown> {
|
||||
return { amount: 'oops', timesheet_id: 'nope' };
|
||||
}
|
||||
|
|
@ -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> = {}
|
||||
): 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<string, unknown> {
|
||||
return { timesheet_id: 'nope' }; // types volontairement faux
|
||||
}
|
||||
|
|
@ -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> = {}
|
||||
): 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<string, unknown> {
|
||||
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<number | null> {
|
||||
const emp = await prisma.employees.findFirst({ select: { id: true } });
|
||||
return emp?.id ?? 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();
|
||||
});
|
||||
});
|
||||
|
|
@ -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/(.*)$": "<rootDir>/src/$1"
|
||||
},
|
||||
"setupFiles": ["dotenv/config"],
|
||||
"setupFilesAfterEnv": ["<rootDir>/test/jest-setup.ts"],
|
||||
"moduleDirectories": ["node_modules", "<rootDir>"],
|
||||
"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": {}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
|
@ -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);
|
||||
// });
|
||||
// });
|
||||
|
|
@ -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.
|
||||
});
|
||||
});
|
||||
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
|
@ -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;
|
||||
});
|
||||
});
|
||||
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
|
@ -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<Array<{ tablename: string }>>(`
|
||||
// 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;`);
|
||||
// }
|
||||
|
|
@ -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<INestApplication> {
|
||||
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;
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user