feat(tests): setup e2e-spec files for route testing. bank_codes, customers, employees
This commit is contained in:
parent
4496c1e419
commit
fd3b9334e3
|
|
@ -2209,13 +2209,11 @@
|
|||
"description": "Employee`s company code"
|
||||
},
|
||||
"first_work_day": {
|
||||
"format": "date-time",
|
||||
"type": "string",
|
||||
"example": "23/09/3018",
|
||||
"description": "Employee`s first working day"
|
||||
},
|
||||
"last_work_day": {
|
||||
"format": "date-time",
|
||||
"type": "string",
|
||||
"example": "25/03/3019",
|
||||
"description": "Employee`s last working day"
|
||||
|
|
@ -2465,19 +2463,16 @@
|
|||
"description": "ID number of a shift code (link with bank-codes)"
|
||||
},
|
||||
"date": {
|
||||
"format": "date-time",
|
||||
"type": "string",
|
||||
"example": "3018-10-20T00:00:00.000Z",
|
||||
"description": "Date where the shift takes place"
|
||||
},
|
||||
"start_time": {
|
||||
"format": "date-time",
|
||||
"type": "string",
|
||||
"example": "3018-10-20T08:00:00.000Z",
|
||||
"description": "Start time of the said shift"
|
||||
},
|
||||
"end_time": {
|
||||
"format": "date-time",
|
||||
"type": "string",
|
||||
"example": "3018-10-20T17:00:00.000Z",
|
||||
"description": "End time of the said shift"
|
||||
|
|
@ -2511,19 +2506,16 @@
|
|||
"description": "ID number of a shift code (link with bank-codes)"
|
||||
},
|
||||
"date": {
|
||||
"format": "date-time",
|
||||
"type": "string",
|
||||
"example": "3018-10-20T00:00:00.000Z",
|
||||
"description": "Date where the shift takes place"
|
||||
},
|
||||
"start_time": {
|
||||
"format": "date-time",
|
||||
"type": "string",
|
||||
"example": "3018-10-20T08:00:00.000Z",
|
||||
"description": "Start time of the said shift"
|
||||
},
|
||||
"end_time": {
|
||||
"format": "date-time",
|
||||
"type": "string",
|
||||
"example": "3018-10-20T17:00:00.000Z",
|
||||
"description": "End time of the said shift"
|
||||
|
|
@ -2640,7 +2632,8 @@
|
|||
"id": {
|
||||
"type": "number",
|
||||
"example": 1,
|
||||
"description": "Unique ID of a bank-code (auto-generated)"
|
||||
"description": "Unique ID of a bank-code (auto-generated)",
|
||||
"readOnly": true
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
|
|
@ -2677,7 +2670,8 @@
|
|||
"id": {
|
||||
"type": "number",
|
||||
"example": 1,
|
||||
"description": "Unique ID of a bank-code (auto-generated)"
|
||||
"description": "Unique ID of a bank-code (auto-generated)",
|
||||
"readOnly": true
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
|
|
|
|||
|
|
@ -5,23 +5,17 @@ const prisma = new PrismaClient();
|
|||
async function main() {
|
||||
const presets = [
|
||||
// type, categorie, modifier, bank_code
|
||||
['SHIFT', 'REGULAR', 1.0, 'REG'],
|
||||
['SHIFT', 'EVENING', 1.15, 'EVE'],
|
||||
['SHIFT', 'NIGHT', 1.25, 'NGT'],
|
||||
['SHIFT', 'WEEKEND', 1.5, 'WKD'],
|
||||
['SHIFT', 'HOLIDAY', 2.0, 'HLD'],
|
||||
['REGULAR' ,'SHIFT', 1.0 , 'G1'],
|
||||
['EVENING' ,'SHIFT', 1.25, 'G43'],
|
||||
['Emergency','SHIFT', 2 , 'G48'],
|
||||
['HOLIDAY' ,'SHIFT', 2.0 , 'G700'],
|
||||
|
||||
['EXPENSE', 'MEAL', 1.0, 'EXP_MEAL'],
|
||||
['EXPENSE', 'MILEAGE', 1.0, 'EXP_MILE'],
|
||||
['EXPENSE', 'HOTEL', 1.0, 'EXP_HOTEL'],
|
||||
['EXPENSE', 'SUPPLIES', 1.0, 'EXP_SUP'],
|
||||
['EXPENSES','EXPENSE', 1.0 , 'G517'],
|
||||
['MILEAGE' ,'EXPENSE', 0.72, 'G57'],
|
||||
['PER_DIEM','EXPENSE', 1.0 , 'G502'],
|
||||
|
||||
['LEAVE', 'SICK', 1.0, 'LV_SICK'],
|
||||
['LEAVE', 'VACATION', 1.0, 'LV_VAC'],
|
||||
['LEAVE', 'UNPAID', 0.0, 'LV_UNP'],
|
||||
['LEAVE', 'BEREAVEMENT', 1.0, 'LV_BER'],
|
||||
['LEAVE', 'PARENTAL', 1.0, 'LV_PAR'],
|
||||
['LEAVE', 'LEGAL', 1.0, 'LV_LEG'],
|
||||
['SICK' ,'LEAVE', 1.0, 'G105'],
|
||||
['VACATION' ,'LEAVE', 1.0, 'G305'],
|
||||
];
|
||||
|
||||
await prisma.bankCodes.createMany({
|
||||
|
|
@ -34,7 +28,7 @@ async function main() {
|
|||
skipDuplicates: true,
|
||||
});
|
||||
|
||||
console.log('✓ BankCodes: 15 rows');
|
||||
console.log('✓ BankCodes: 9 rows');
|
||||
}
|
||||
|
||||
main().finally(() => prisma.$disconnect());
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ async function main() {
|
|||
|
||||
const employees = await prisma.employees.findMany({ select: { id: true } });
|
||||
const bankCodes = await prisma.bankCodes.findMany({
|
||||
where: { type: 'LEAVE' },
|
||||
where: { categorie: 'LEAVE' },
|
||||
select: { id: true },
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -11,12 +11,11 @@ function daysAgo(n:number) {
|
|||
|
||||
async function main() {
|
||||
const employees = await prisma.employees.findMany({ select: { id: true } });
|
||||
const bankCodes = await prisma.bankCodes.findMany({ select: { id: true }, where: { type: 'LEAVE' } });
|
||||
const bankCodes = await prisma.bankCodes.findMany({ select: { id: true }, where: { categorie: 'LEAVE' } });
|
||||
|
||||
const types = Object.values(LeaveTypes);
|
||||
const statuses = Object.values(LeaveApprovalStatus);
|
||||
|
||||
// ✅ typer le tableau pour éviter never[]
|
||||
const created: LeaveRequests[] = [];
|
||||
|
||||
for (let i = 0; i < 10; i++) {
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ function daysAgo(n:number) {
|
|||
}
|
||||
|
||||
async function main() {
|
||||
const bankCodes = await prisma.bankCodes.findMany({ where: { type: 'SHIFT' }, select: { id: true } });
|
||||
const bankCodes = await prisma.bankCodes.findMany({ where: { categorie: 'SHIFT' }, select: { id: true } });
|
||||
if (!bankCodes.length) throw new Error('Need SHIFT bank codes');
|
||||
|
||||
const employees = await prisma.employees.findMany({ select: { id: true } });
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ function daysAgo(n:number) {
|
|||
}
|
||||
|
||||
async function main() {
|
||||
const bankCodes = await prisma.bankCodes.findMany({ where: { type: 'SHIFT' }, select: { id: true } });
|
||||
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) {
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ function daysAgo(n:number) {
|
|||
}
|
||||
|
||||
async function main() {
|
||||
const expenseCodes = await prisma.bankCodes.findMany({ where: { type: 'EXPENSE' }, select: { id: true } });
|
||||
const expenseCodes = await prisma.bankCodes.findMany({ where: { categorie: 'EXPENSE' }, select: { id: true } });
|
||||
if (!expenseCodes.length) throw new Error('Need EXPENSE bank codes');
|
||||
|
||||
const timesheets = await prisma.timesheets.findMany({ select: { id: true } });
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ function daysAgo(n:number) {
|
|||
}
|
||||
|
||||
async function main() {
|
||||
const expenseCodes = await prisma.bankCodes.findMany({ where: { type: 'EXPENSE' }, select: { id: true } });
|
||||
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[]
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ shifts = 10 × (#employees)
|
|||
|
||||
shifts_archive = 30 × (#employees)
|
||||
|
||||
bank_codes = 15
|
||||
bank_codes = 9
|
||||
|
||||
expenses = 5
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,14 @@
|
|||
import { ApiProperty } from "@nestjs/swagger";
|
||||
import { IsNotEmpty, IsNumber, IsString } from "class-validator";
|
||||
import { Type } from "class-transformer";
|
||||
import { Allow, IsNotEmpty, IsNumber, IsString } from "class-validator";
|
||||
|
||||
export class CreateBankCodeDto {
|
||||
@ApiProperty({
|
||||
example: 1,
|
||||
description: 'Unique ID of a bank-code (auto-generated)',
|
||||
readOnly: true,
|
||||
})
|
||||
@Allow()
|
||||
id: number;
|
||||
|
||||
@ApiProperty({
|
||||
|
|
@ -28,6 +31,7 @@ export class CreateBankCodeDto {
|
|||
example: '0, 0.72, 1, 1.5, 2',
|
||||
description: 'modifier number to apply to salary',
|
||||
})
|
||||
@Type(()=> Number)
|
||||
@IsNumber()
|
||||
@IsNotEmpty()
|
||||
modifier: number;
|
||||
|
|
|
|||
|
|
@ -9,7 +9,14 @@ export class BankCodesService {
|
|||
constructor(private readonly prisma: PrismaService) {}
|
||||
|
||||
async create(dto: CreateBankCodeDto): Promise<BankCodes>{
|
||||
return this.prisma.bankCodes.create({ data: dto })
|
||||
return this.prisma.bankCodes.create({
|
||||
data: {
|
||||
type: dto.type,
|
||||
categorie: dto.categorie,
|
||||
modifier: dto.modifier,
|
||||
bank_code: dto.bank_code,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
findAll() {
|
||||
|
|
@ -19,15 +26,21 @@ export class BankCodesService {
|
|||
async findOne(id: number) {
|
||||
const bankCode = await this.prisma.bankCodes.findUnique({ where: {id} });
|
||||
|
||||
if(!bankCode) {
|
||||
throw new NotFoundException(`Bank Code #${id} not found`);
|
||||
}
|
||||
if(!bankCode) throw new NotFoundException(`Bank Code #${id} not found`);
|
||||
|
||||
return bankCode;
|
||||
}
|
||||
|
||||
async update(id:number, dto: UpdateBankCodeDto) {
|
||||
await this.prisma.bankCodes.update({ where: { id }, data: dto });
|
||||
return await this.prisma.bankCodes.update({
|
||||
where: { id },
|
||||
data: {
|
||||
type: dto.type,
|
||||
categorie: dto.categorie,
|
||||
modifier: dto.modifier as any,
|
||||
bank_code: dto.bank_code,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async remove(id: number) {
|
||||
|
|
|
|||
|
|
@ -1,12 +1,14 @@
|
|||
import { ApiProperty } from "@nestjs/swagger";
|
||||
import { Type } from "class-transformer";
|
||||
import {
|
||||
Allow,
|
||||
IsEmail,
|
||||
IsInt,
|
||||
IsNotEmpty,
|
||||
IsOptional,
|
||||
IsPositive,
|
||||
IsString,
|
||||
IsUUID,
|
||||
} from "class-validator";
|
||||
|
||||
export class CreateCustomerDto {
|
||||
|
|
@ -14,13 +16,16 @@ export class CreateCustomerDto {
|
|||
example: 1,
|
||||
description: 'Unique ID of a customer(primary-key, auto-incremented)',
|
||||
})
|
||||
id: number;
|
||||
@Allow()
|
||||
id?: number;
|
||||
|
||||
@ApiProperty({
|
||||
example: '0e6e2e1f-b157-4c7c-ae3f-999b3e4f914d',
|
||||
description: 'UUID of the user linked to that customer',
|
||||
})
|
||||
user_id: string;
|
||||
@IsUUID()
|
||||
@IsOptional()
|
||||
user_id?: string;
|
||||
|
||||
@ApiProperty({
|
||||
example: 'Gandalf',
|
||||
|
|
@ -69,6 +74,7 @@ export class CreateCustomerDto {
|
|||
description: 'Customer`s invoice number',
|
||||
required: false,
|
||||
})
|
||||
@Type(() => Number)
|
||||
@IsInt()
|
||||
@IsNotEmpty()
|
||||
invoice_id: number;
|
||||
|
|
|
|||
|
|
@ -58,7 +58,6 @@ export class EmployeesController {
|
|||
@ApiResponse({ status: 200, description: 'Employee updated or restored', type: CreateEmployeeDto })
|
||||
@ApiResponse({ status: 202, description: 'Employee archived successfully', type: CreateEmployeeDto })
|
||||
@ApiResponse({ status: 404, description: 'Employee not found in active or archive' })
|
||||
@Patch(':id')
|
||||
async updateOrArchiveOrRestore(@Param('id') id: string, @Body() dto: UpdateEmployeeDto,) {
|
||||
// if last_work_day is set => archive the employee
|
||||
// else if employee is archived and first_work_day or last_work_day = null => restore
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import {
|
||||
Allow,
|
||||
IsDate,
|
||||
IsDateString,
|
||||
IsEmail,
|
||||
|
|
@ -7,6 +8,7 @@ import {
|
|||
IsOptional,
|
||||
IsPositive,
|
||||
IsString,
|
||||
IsUUID,
|
||||
} from 'class-validator';
|
||||
import { Type } from 'class-transformer';
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
|
|
@ -16,13 +18,16 @@ export class CreateEmployeeDto {
|
|||
example: 1,
|
||||
description: 'Unique ID of an employee(primary-key, auto-incremented)',
|
||||
})
|
||||
@Allow()
|
||||
id: number;
|
||||
|
||||
@ApiProperty({
|
||||
example: '0e6e2e1f-b157-4c7c-ae3f-999b3e4f914d',
|
||||
description: 'UUID of the user linked to that employee',
|
||||
})
|
||||
user_id: string;
|
||||
@IsUUID()
|
||||
@IsOptional()
|
||||
user_id?: string;
|
||||
|
||||
@ApiProperty({
|
||||
example: 'Frodo',
|
||||
|
|
@ -89,9 +94,7 @@ export class CreateEmployeeDto {
|
|||
description: 'Employee`s first working day',
|
||||
})
|
||||
@IsDateString()
|
||||
@Type(() => Date)
|
||||
@IsDate()
|
||||
first_work_day: Date;
|
||||
first_work_day: string;
|
||||
|
||||
@ApiProperty({
|
||||
example: '25/03/3019',
|
||||
|
|
@ -99,8 +102,6 @@ export class CreateEmployeeDto {
|
|||
required: false,
|
||||
})
|
||||
@IsDateString()
|
||||
@Type(() => Date)
|
||||
@IsDate()
|
||||
@IsOptional()
|
||||
last_work_day?: Date;
|
||||
last_work_day?: string;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,22 @@
|
|||
import { ApiProperty, PartialType } from '@nestjs/swagger';
|
||||
import { CreateEmployeeDto } from './create-employee.dto';
|
||||
import { IsDateString, IsOptional, Max } from 'class-validator';
|
||||
|
||||
export class UpdateEmployeeDto extends PartialType(CreateEmployeeDto) {
|
||||
@ApiProperty({ required: false, type: Date, description: 'New hire date or undefined' })
|
||||
first_work_day?: Date;
|
||||
@IsDateString()
|
||||
@IsOptional()
|
||||
first_work_day?: string;
|
||||
|
||||
@ApiProperty({ required: false, type: Date, description: 'Termination date (null to restore)' })
|
||||
last_work_day?: Date;
|
||||
@IsDateString()
|
||||
@IsOptional()
|
||||
last_work_day?: string;
|
||||
|
||||
@ApiProperty({ required: false, type: Number, description: 'Supervisor ID' })
|
||||
@IsOptional()
|
||||
supervisor_id?: number;
|
||||
|
||||
@Max(2147483647)
|
||||
phone_number: number;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,16 @@ import { CreateEmployeeDto } from '../dtos/create-employee.dto';
|
|||
import { UpdateEmployeeDto } from '../dtos/update-employee.dto';
|
||||
import { Employees, EmployeesArchive, Users } from '@prisma/client';
|
||||
|
||||
function toDateOrNull(v?: string | null): Date | null {
|
||||
if (!v) return null;
|
||||
const d = new Date(v);
|
||||
return isNaN(d.getTime()) ? null : d;
|
||||
}
|
||||
function toDateOrUndefined(v?: string | null): Date | undefined {
|
||||
const d = toDateOrNull(v ?? undefined);
|
||||
return d === null ? undefined : d;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class EmployeesService {
|
||||
constructor(private readonly prisma: PrismaService) {}
|
||||
|
|
@ -111,36 +121,91 @@ async update(
|
|||
|
||||
//archivation functions ******************************************************
|
||||
|
||||
async patchEmployee(id: number, dto: UpdateEmployeeDto): Promise<any> {
|
||||
//fetching existing employee
|
||||
async patchEmployee(id: number, dto: UpdateEmployeeDto): Promise<Employees | EmployeesArchive | null> {
|
||||
// 1) Tenter sur employés actifs
|
||||
const existing = await this.prisma.employees.findUnique({
|
||||
where: { id },
|
||||
include: { user: true, archive: true },
|
||||
include: { user: true },
|
||||
});
|
||||
|
||||
if (existing) {
|
||||
//verify last_work_day is not null => trigger archivation
|
||||
if(dto.last_work_day != undefined && existing.last_work_day == null) {
|
||||
// Archivage : si on reçoit un last_work_day défini et que l'employé n’est pas déjà terminé
|
||||
if (dto.last_work_day !== undefined && existing.last_work_day == null && dto.last_work_day !== null) {
|
||||
return this.archiveOnTermination(existing, dto);
|
||||
}
|
||||
//if null => regular update
|
||||
return this.prisma.employees.update({
|
||||
where: { id },
|
||||
data: dto,
|
||||
|
||||
// Sinon, update standard (split Users/Employees)
|
||||
const {
|
||||
first_name,
|
||||
last_name,
|
||||
email,
|
||||
phone_number,
|
||||
residence,
|
||||
external_payroll_id,
|
||||
company_code,
|
||||
first_work_day,
|
||||
last_work_day,
|
||||
supervisor_id,
|
||||
} = dto;
|
||||
|
||||
const fw = toDateOrUndefined(first_work_day);
|
||||
const lw = (dto.hasOwnProperty('last_work_day'))
|
||||
? toDateOrNull(last_work_day ?? null)
|
||||
: undefined;
|
||||
|
||||
await this.prisma.$transaction(async (tx) => {
|
||||
const willUpdateUser =
|
||||
first_name !== undefined ||
|
||||
last_name !== undefined ||
|
||||
email !== undefined ||
|
||||
phone_number !== undefined ||
|
||||
residence !== undefined;
|
||||
|
||||
if (willUpdateUser) {
|
||||
await tx.users.update({
|
||||
where: { id: existing.user_id },
|
||||
data: {
|
||||
...(first_name !== undefined ? { first_name } : {}),
|
||||
...(last_name !== undefined ? { last_name } : {}),
|
||||
...(email !== undefined ? { email } : {}),
|
||||
...(phone_number !== undefined ? { phone_number } : {}),
|
||||
...(residence !== undefined ? { residence } : {}),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
await tx.employees.update({
|
||||
where: { id },
|
||||
data: {
|
||||
...(external_payroll_id !== undefined ? { external_payroll_id } : {}),
|
||||
...(company_code !== undefined ? { company_code } : {}),
|
||||
...(fw !== undefined ? { first_work_day: fw } : {}),
|
||||
...(lw !== undefined
|
||||
? { last_work_day: lw }
|
||||
: {}),
|
||||
...(supervisor_id !== undefined ? { supervisor_id } : {}),
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
return this.prisma.employees.findUnique({ where: { id } });
|
||||
}
|
||||
//if not found => fetch archives side for restoration
|
||||
|
||||
// 2) Pas trouvé en actifs → regarder en archive (pour restauration)
|
||||
const archived = await this.prisma.employeesArchive.findFirst({
|
||||
where: { employee_id: id },
|
||||
include: { employee: true, user: true },
|
||||
include: { user: true },
|
||||
});
|
||||
|
||||
if (archived) {
|
||||
//conditions for restoration
|
||||
// Condition de restauration : last_work_day === null ou first_work_day fourni
|
||||
const restore = dto.last_work_day === null || dto.first_work_day != null;
|
||||
if(restore) {
|
||||
if (restore) {
|
||||
return this.restoreEmployee(archived, dto);
|
||||
}
|
||||
}
|
||||
//if neither activated nor archivated => 404
|
||||
|
||||
// 3) Ni actif, ni archivé → 404 dans le controller
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -30,27 +30,21 @@ export class CreateShiftDto {
|
|||
description: 'Date where the shift takes place',
|
||||
})
|
||||
@IsDateString()
|
||||
@Type(() => Date)
|
||||
@IsDate()
|
||||
date: Date;
|
||||
date: string;
|
||||
|
||||
@ApiProperty({
|
||||
example: '3018-10-20T08:00:00.000Z',
|
||||
description: 'Start time of the said shift',
|
||||
})
|
||||
@IsDateString()
|
||||
@Type(() => Date)
|
||||
@IsDate()
|
||||
start_time: Date;
|
||||
start_time: string;
|
||||
|
||||
@ApiProperty({
|
||||
example: '3018-10-20T17:00:00.000Z',
|
||||
description: 'End time of the said shift',
|
||||
})
|
||||
@IsDateString()
|
||||
@Type(() => Date)
|
||||
@IsDate()
|
||||
end_time: Date;
|
||||
end_time: string;
|
||||
|
||||
@IsString()
|
||||
description: string;
|
||||
|
|
|
|||
|
|
@ -16,6 +16,10 @@ export class SearchShiftsDto {
|
|||
@IsString()
|
||||
description_contains?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsDateString()
|
||||
start_date?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsDateString()
|
||||
end_date?: string;
|
||||
|
|
|
|||
0
targo-backend@0.0.1
Normal file
0
targo-backend@0.0.1
Normal file
|
|
@ -3,6 +3,7 @@ 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>;
|
||||
|
|
@ -22,4 +23,10 @@ describe('AppController (e2e)', () => {
|
|||
.expect(200)
|
||||
.expect('Hello World!');
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await app.close();
|
||||
const prisma = app.get(PrismaService);
|
||||
await prisma.$disconnect();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
125
test/bank-codes.e2e-spec.ts
Normal file
125
test/bank-codes.e2e-spec.ts
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
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);
|
||||
});
|
||||
});
|
||||
125
test/customers.e2e-spec.ts
Normal file
125
test/customers.e2e-spec.ts
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
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;
|
||||
});
|
||||
});
|
||||
105
test/employees.e2e-spec.ts
Normal file
105
test/employees.e2e-spec.ts
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
// 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;
|
||||
});
|
||||
});
|
||||
25
test/expenses.e2e-spec.ts
Normal file
25
test/expenses.e2e-spec.ts
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
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('Expenses (e2e)', () => {
|
||||
let app: INestApplication;
|
||||
const BASE = '/expenses';
|
||||
|
||||
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);
|
||||
});
|
||||
});
|
||||
26
test/factories/bank-code.factory.ts
Normal file
26
test/factories/bank-code.factory.ts
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
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
|
||||
}
|
||||
0
test/factories/customer.factory.ts
Normal file
0
test/factories/customer.factory.ts
Normal file
46
test/factories/employee.factory.ts
Normal file
46
test/factories/employee.factory.ts
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
// 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' };
|
||||
}
|
||||
0
test/factories/expense.factory.ts
Normal file
0
test/factories/expense.factory.ts
Normal file
0
test/factories/leave-request.factory.ts
Normal file
0
test/factories/leave-request.factory.ts
Normal file
0
test/factories/shift.factory.ts
Normal file
0
test/factories/shift.factory.ts
Normal file
0
test/factories/timesheet.factory.ts
Normal file
0
test/factories/timesheet.factory.ts
Normal file
|
|
@ -2,6 +2,7 @@ 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;
|
||||
|
|
@ -16,7 +17,6 @@ describe('HealthController (e2e)', () => {
|
|||
});
|
||||
|
||||
it('/health (GET) → 200 & { status: "ok" }', () => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
||||
return request(app.getHttpServer())
|
||||
.get('/health')
|
||||
.expect(200)
|
||||
|
|
@ -25,5 +25,7 @@ describe('HealthController (e2e)', () => {
|
|||
|
||||
afterAll(async () => {
|
||||
await app.close();
|
||||
const prisma = app.get(PrismaService);
|
||||
await prisma.$disconnect();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,9 +1,18 @@
|
|||
{
|
||||
"moduleFileExtensions": ["js", "json", "ts"],
|
||||
"rootDir": ".",
|
||||
"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
|
||||
}
|
||||
|
|
|
|||
8
test/jest-setup.ts
Normal file
8
test/jest-setup.ts
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
import { randomUUID, randomFillSync } from 'crypto';
|
||||
|
||||
if (!(globalThis as any).crypto) {
|
||||
(globalThis as any).crypto = {
|
||||
randomUUID,
|
||||
getRandomValues: (buffer: Uint8Array) => randomFillSync(buffer),
|
||||
} as any;
|
||||
}
|
||||
25
test/leave-requests.e2e-spec.ts
Normal file
25
test/leave-requests.e2e-spec.ts
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
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);
|
||||
});
|
||||
});
|
||||
26
test/pay-periods.e2e-spec.ts
Normal file
26
test/pay-periods.e2e-spec.ts
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
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.
|
||||
});
|
||||
});
|
||||
25
test/shifts.e2e-spec.ts
Normal file
25
test/shifts.e2e-spec.ts
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
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('Shifts (e2e)', () => {
|
||||
let app: INestApplication;
|
||||
const BASE = '/shifts';
|
||||
|
||||
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);
|
||||
});
|
||||
});
|
||||
25
test/timesheets.e2e-spec.ts
Normal file
25
test/timesheets.e2e-spec.ts
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
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('Timesheets (e2e)', () => {
|
||||
let app: INestApplication;
|
||||
const BASE = '/timesheets';
|
||||
|
||||
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);
|
||||
});
|
||||
});
|
||||
21
test/utils/reset-db.ts
Normal file
21
test/utils/reset-db.ts
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
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;`);
|
||||
// }
|
||||
20
test/utils/testing-app.ts
Normal file
20
test/utils/testing-app.ts
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
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