test(uploads): extraire extractMetadata() + tests unitaires

Refactoring minimal : le bloc de parsing de métadonnées (titre, version,
description) est extrait en méthode privée extractMetadata() dans
UploadsController. Le comportement de validate() est identique.

12 tests unitaires couvrent tous les cas : version standard (1.2.3, 1.0, 2,
0.0.0), extraction nom/description, et cas limites (version non-numérique,
absente, vide, info absent).

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
This commit is contained in:
z3n
2026-06-24 13:18:59 +00:00
parent b6a52ce554
commit 1a8bd0b7b4
2 changed files with 121 additions and 16 deletions

View File

@@ -0,0 +1,93 @@
import { describe, it, expect } from 'vitest';
import { UploadsController } from './uploads.controller';
/** Accès à la méthode privée sans instancier le contrôleur complet */
function extract(yamlContent: string) {
return (UploadsController.prototype as any).extractMetadata.call({}, yamlContent);
}
describe('UploadsController.extractMetadata()', () => {
describe('version OpenAPI standard', () => {
it('"1.2.3" → major=1, minor=2, patch=3', () => {
const result = extract('info:\n title: Test\n version: "1.2.3"');
expect(result.versionMajor).toBe(1);
expect(result.versionMinor).toBe(2);
expect(result.versionPatch).toBe(3);
});
it('"1.0" → major=1, minor=0, patch=null', () => {
const result = extract('info:\n title: Test\n version: "1.0"');
expect(result.versionMajor).toBe(1);
expect(result.versionMinor).toBe(0);
expect(result.versionPatch).toBeNull();
});
it('"2" → major=2, minor=null, patch=null', () => {
const result = extract('info:\n title: Test\n version: "2"');
expect(result.versionMajor).toBe(2);
expect(result.versionMinor).toBeNull();
expect(result.versionPatch).toBeNull();
});
it('"0.0.0" → major=0, minor=0, patch=0', () => {
const result = extract('info:\n title: Test\n version: "0.0.0"');
expect(result.versionMajor).toBe(0);
expect(result.versionMinor).toBe(0);
expect(result.versionPatch).toBe(0);
});
});
describe('extraction du nom et description', () => {
it('title présent → name extrait', () => {
const result = extract('info:\n title: Mon API\n version: "1.0.0"');
expect(result.name).toBe('Mon API');
});
it('title absent → name=null', () => {
const result = extract('info:\n version: "1.0.0"');
expect(result.name).toBeNull();
});
it('description présente → description extraite', () => {
const result = extract('info:\n title: Test\n version: "1.0.0"\n description: Une description');
expect(result.description).toBe('Une description');
});
it('description absente → description=null', () => {
const result = extract('info:\n title: Test\n version: "1.0.0"');
expect(result.description).toBeNull();
});
});
describe('cas limite de version', () => {
it('version non numérique ("alpha") → major=null, minor=null, patch=null', () => {
const result = extract('info:\n title: Test\n version: alpha');
expect(result.versionMajor).toBeNull();
expect(result.versionMinor).toBeNull();
expect(result.versionPatch).toBeNull();
});
it('version absente → tout null', () => {
const result = extract('info:\n title: Test');
expect(result.versionMajor).toBeNull();
expect(result.versionMinor).toBeNull();
expect(result.versionPatch).toBeNull();
});
it('version vide string "" → tout null', () => {
const result = extract('info:\n title: Test\n version: ""');
expect(result.versionMajor).toBeNull();
expect(result.versionMinor).toBeNull();
expect(result.versionPatch).toBeNull();
});
it('info absent → tout null', () => {
const result = extract('asyncapi: "3.0.0"');
expect(result.name).toBeNull();
expect(result.description).toBeNull();
expect(result.versionMajor).toBeNull();
expect(result.versionMinor).toBeNull();
expect(result.versionPatch).toBeNull();
});
});
});

View File

@@ -156,22 +156,7 @@ export class UploadsController {
/* Extraction des métadonnées depuis le fichier principal */ /* Extraction des métadonnées depuis le fichier principal */
const mainContent = fileContents.find((f) => f.name === mainFileName)!.content; const mainContent = fileContents.find((f) => f.name === mainFileName)!.content;
const parsed = yaml.load(mainContent) as Record<string, unknown>; const { name, description, versionMajor, versionMinor, versionPatch } = this.extractMetadata(mainContent);
const info = parsed['info'] as Record<string, unknown> | undefined;
const name = typeof info?.['title'] === 'string' ? info['title'] : null;
const description = typeof info?.['description'] === 'string' ? info['description'] : null;
/* Décomposer la version en major.minor.patch */
let versionMajor: number | null = null;
let versionMinor: number | null = null;
let versionPatch: number | null = null;
const versionStr = typeof info?.['version'] === 'string' ? info['version'] : null;
if (versionStr) {
const parts = versionStr.split('.').map((p) => parseInt(p, 10));
if (parts.length >= 1 && !isNaN(parts[0])) versionMajor = parts[0];
if (parts.length >= 2 && !isNaN(parts[1])) versionMinor = parts[1];
if (parts.length >= 3 && !isNaN(parts[2])) versionPatch = parts[2];
}
return { valid: true, type: detectedType, mainFile: mainFileName, name, versionMajor, versionMinor, versionPatch, description, errors: [] }; return { valid: true, type: detectedType, mainFile: mainFileName, name, versionMajor, versionMinor, versionPatch, description, errors: [] };
} }
@@ -274,6 +259,33 @@ export class UploadsController {
return entry; return entry;
} }
/** Extrait les métadonnées (titre, description, version) depuis le contenu YAML */
private extractMetadata(yamlContent: string): {
name: string | null;
description: string | null;
versionMajor: number | null;
versionMinor: number | null;
versionPatch: number | null;
} {
const parsed = yaml.load(yamlContent) as Record<string, unknown>;
const info = parsed['info'] as Record<string, unknown> | undefined;
const name = typeof info?.['title'] === 'string' ? info['title'] : null;
const description = typeof info?.['description'] === 'string' ? info['description'] : null;
let versionMajor: number | null = null;
let versionMinor: number | null = null;
let versionPatch: number | null = null;
const versionStr = typeof info?.['version'] === 'string' ? info['version'] : null;
if (versionStr) {
const parts = versionStr.split('.').map((p) => parseInt(p, 10));
if (parts.length >= 1 && !isNaN(parts[0])) versionMajor = parts[0];
if (parts.length >= 2 && !isNaN(parts[1])) versionMinor = parts[1];
if (parts.length >= 3 && !isNaN(parts[2])) versionPatch = parts[2];
}
return { name, description, versionMajor, versionMinor, versionPatch };
}
/** Valide la syntaxe YAML et lève une BadRequestException si invalide */ /** Valide la syntaxe YAML et lève une BadRequestException si invalide */
private validateYamlSyntax(content: string, filename: string): void { private validateYamlSyntax(content: string, filename: string): void {
try { try {