From 787d8f32eec8613889f812729641db7d80b93600 Mon Sep 17 00:00:00 2001 From: z3n Date: Sun, 28 Jun 2026 21:21:36 +0000 Subject: [PATCH] back(test): specs validation env + DTO upload (48 -> 59 tests) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - env.validation.spec : config valide/coercion/défauts + rejets (var manquante, port hors plage, NODE_ENV inconnu) - create-api-upload.dto.spec : name requis, emails @ValidateIf (vides OK, invalides rejetés), énum convention, MaxLength - vitest-setup charge reflect-metadata (décorateurs class-validator en test) Co-Authored-By: Claude Opus 4.8 --- back/src/config/env.validation.spec.ts | 33 +++++++++++++ .../uploads/dto/create-api-upload.dto.spec.ts | 49 +++++++++++++++++++ back/vitest-setup.ts | 3 ++ back/vitest.config.ts | 1 + 4 files changed, 86 insertions(+) create mode 100644 back/src/config/env.validation.spec.ts create mode 100644 back/src/modules/uploads/dto/create-api-upload.dto.spec.ts create mode 100644 back/vitest-setup.ts diff --git a/back/src/config/env.validation.spec.ts b/back/src/config/env.validation.spec.ts new file mode 100644 index 0000000..ead1ec7 --- /dev/null +++ b/back/src/config/env.validation.spec.ts @@ -0,0 +1,33 @@ +import { describe, it, expect } from 'vitest'; +import { validateEnv } from './env.validation'; + +const base = { + DATABASE_HOST: 'localhost', + DATABASE_PORT: '5432', + DATABASE_USER: 'datacat', + DATABASE_PASSWORD: 'datacat', + DATABASE_NAME: 'datacat', +}; + +describe('validateEnv', () => { + it('accepte une config valide, coerce le port et applique les défauts', () => { + const cfg = validateEnv({ ...base }); + expect(cfg.DATABASE_PORT).toBe(5432); + expect(typeof cfg.DATABASE_PORT).toBe('number'); + expect(cfg.NODE_ENV).toBe('development'); + expect(cfg.PORT).toBe(3000); + }); + + it('rejette une variable requise manquante (message explicite)', () => { + const { DATABASE_HOST: _omit, ...rest } = base; + expect(() => validateEnv(rest)).toThrow(/DATABASE_HOST/); + }); + + it('rejette un port hors plage', () => { + expect(() => validateEnv({ ...base, DATABASE_PORT: '70000' })).toThrow(); + }); + + it('rejette un NODE_ENV inconnu', () => { + expect(() => validateEnv({ ...base, NODE_ENV: 'staging' })).toThrow(); + }); +}); diff --git a/back/src/modules/uploads/dto/create-api-upload.dto.spec.ts b/back/src/modules/uploads/dto/create-api-upload.dto.spec.ts new file mode 100644 index 0000000..ff7ff49 --- /dev/null +++ b/back/src/modules/uploads/dto/create-api-upload.dto.spec.ts @@ -0,0 +1,49 @@ +import { describe, it, expect } from 'vitest'; +import { plainToInstance } from 'class-transformer'; +import { validateSync } from 'class-validator'; +import { CreateApiUploadDto } from './create-api-upload.dto'; + +function validate(payload: Record) { + const dto = plainToInstance(CreateApiUploadDto, payload, { enableImplicitConversion: true }); + return validateSync(dto, { whitelist: true }); +} + +describe('CreateApiUploadDto', () => { + it('accepte un payload minimal (name seul requis)', () => { + expect(validate({ name: 'Mon API' })).toHaveLength(0); + }); + + it('rejette un name manquant', () => { + expect(validate({}).some((e) => e.property === 'name')).toBe(true); + }); + + it('accepte des emails de contact vides (@ValidateIf)', () => { + expect( + validate({ name: 'X', contactFunctionalEmail: '', contactTechnicalEmail: '' }), + ).toHaveLength(0); + }); + + it('rejette un email renseigné mais invalide', () => { + expect( + validate({ name: 'X', contactFunctionalEmail: 'pas-un-email' }).some( + (e) => e.property === 'contactFunctionalEmail', + ), + ).toBe(true); + }); + + it('accepte un email valide', () => { + expect(validate({ name: 'X', contactFunctionalEmail: 'a@b.fr' })).toHaveLength(0); + }); + + it('rejette une convention hors énumération', () => { + expect( + validate({ name: 'X', convention: 'FOO' }).some((e) => e.property === 'convention'), + ).toBe(true); + }); + + it('rejette un name trop long (>255)', () => { + expect( + validate({ name: 'a'.repeat(256) }).some((e) => e.property === 'name'), + ).toBe(true); + }); +}); diff --git a/back/vitest-setup.ts b/back/vitest-setup.ts new file mode 100644 index 0000000..c0c1347 --- /dev/null +++ b/back/vitest-setup.ts @@ -0,0 +1,3 @@ +// Requis pour que les décorateurs class-validator / class-transformer +// (Reflect.getMetadata) fonctionnent dans les tests, comme dans l'app (main.ts). +import 'reflect-metadata'; diff --git a/back/vitest.config.ts b/back/vitest.config.ts index 0e8fb7b..e0dc0ad 100644 --- a/back/vitest.config.ts +++ b/back/vitest.config.ts @@ -4,6 +4,7 @@ export default defineConfig({ test: { globals: true, environment: 'node', + setupFiles: ['./vitest-setup.ts'], include: ['src/**/*.spec.ts'], coverage: { provider: 'v8',