back(test): specs validation env + DTO upload (48 -> 59 tests)

- 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 <noreply@anthropic.com>
This commit is contained in:
z3n
2026-06-28 21:21:36 +00:00
parent d2d140640e
commit 787d8f32ee
4 changed files with 86 additions and 0 deletions

View File

@@ -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();
});
});

View File

@@ -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<string, unknown>) {
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);
});
});

3
back/vitest-setup.ts Normal file
View File

@@ -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';

View File

@@ -4,6 +4,7 @@ export default defineConfig({
test: {
globals: true,
environment: 'node',
setupFiles: ['./vitest-setup.ts'],
include: ['src/**/*.spec.ts'],
coverage: {
provider: 'v8',