Files

116 lines
8.7 KiB
PHP

<?php
// Tests du rapprochement, des doublons, de l'appariement du lot et de l'email de sauvegarde : php tests/test_suivi.php
declare(strict_types=1);
const VIVIER_RACINE = __DIR__ . '/..';
const SENIORITES = ['junior' => 'Junior', 'intermediaire' => 'Intermédiaire', 'confirme' => 'Confirmé', 'expert' => 'Expert'];
const STATUTS = ['recu' => 'Reçu'];
const ETATS_BESOIN = ['ouvert' => 'Ouvert', 'pourvu' => 'Pourvu', 'ferme' => 'Fermé'];
require __DIR__ . '/../src/i18n.php';
require __DIR__ . '/../src/Recherche.php';
require __DIR__ . '/../src/Import.php';
require __DIR__ . '/../src/besoins.php';
require __DIR__ . '/../src/Lot.php';
require __DIR__ . '/../src/Sauvegarde.php';
$ok = 0; $ko = 0;
function verifier(string $nom, bool $cond): void {
global $ok, $ko;
if ($cond) { $ok++; } else { $ko++; echo "ECHEC : $nom\n"; }
}
// --- Listes saisies
verifier('liste : virgules, points-virgules, doublons', liste_saisie(' Java, Spring Boot ; java,, PostgreSQL ') === ['Java', 'Spring Boot', 'PostgreSQL']);
verifier('liste vide', liste_saisie(null) === [] && liste_saisie(' , ') === []);
// --- Rapprochement
$profil = static fn (array $technos, string $seniorite = 'confirme', array $plus = []): array => $plus + [
'technologies' => $technos,
'termes' => array_fill_keys(array_merge(['developpeur', 'java'], array_keys($technos)), true),
'seniorite' => $seniorite, 'langues' => ['francais', 'anglais'], 'disponible_le' => null, 'tjm' => null,
];
$besoin = ['intitule' => 'Développeur Java', 'technologies' => 'Java, Spring Boot, PostgreSQL', 'seniorite_min' => null, 'langues' => null, 'date_debut' => null, 'tjm_max' => null];
$complet = noter_rapprochement($besoin, $profil(['java' => 9.0, 'spring boot' => 6.0, 'postgresql' => 5.0]));
verifier('toutes les technologies, 5 ans et plus : 100', $complet['score'] === 100 && $complet['manquantes'] === [] && count($complet['trouvees']) === 3);
$partiel = noter_rapprochement($besoin, $profil(['java' => 9.0]));
verifier('une technologie sur trois : note plus basse, manquantes listées', $partiel['score'] < $complet['score'] && $partiel['manquantes'] === ['Spring Boot', 'PostgreSQL']);
verifier('une sur trois à 5 ans : 55/3 + 45 = 63', $partiel['score'] === 63);
$debutant = noter_rapprochement($besoin, $profil(['java' => 1.0, 'spring boot' => 1.0, 'postgresql' => 1.0]));
verifier('mêmes technologies, moins d\'années : note plus basse', $debutant['score'] < $complet['score'] && $debutant['score'] > $partiel['score']);
$sansAnnees = noter_rapprochement($besoin, $profil(['java' => null, 'spring boot' => null, 'postgresql' => null]));
verifier('années inconnues : 60 % des points de technologie', $sansAnnees['score'] === 78);
$horsSujet = noter_rapprochement($besoin, ['technologies' => ['cobol' => 20.0], 'termes' => ['cobol' => true], 'seniorite' => 'expert', 'langues' => [], 'disponible_le' => null, 'tjm' => null]);
verifier('aucune technologie ni mot de l\'intitulé : profil écarté', $horsSujet['pertinent'] === false);
// Technologie citée dans une description (index) mais absente de la liste des technologies
$parIndex = noter_rapprochement(['intitule' => 'Consultant', 'technologies' => 'Spring Boot'] + $besoin,
['technologies' => [], 'termes' => ['spring' => true, 'boot' => true], 'seniorite' => 'junior', 'langues' => [], 'disponible_le' => null, 'tjm' => null]);
verifier('technologie retrouvée par l\'index', $parIndex['pertinent'] && count($parIndex['trouvees']) === 1 && $parIndex['trouvees'][0]['annees'] === null);
$b2 = ['seniorite_min' => 'expert'] + $besoin;
$t = ['java' => 9.0, 'spring boot' => 6.0, 'postgresql' => 5.0];
verifier('séniorité un niveau en dessous : 4 points sur 10 et alerte', noter_rapprochement($b2, $profil($t, 'confirme'))['score'] === 94 && count(noter_rapprochement($b2, $profil($t, 'confirme'))['alertes']) === 1);
verifier('séniorité deux niveaux en dessous : 0 point', noter_rapprochement($b2, $profil($t, 'intermediaire'))['score'] === 90);
verifier('séniorité atteinte : pas d\'alerte', noter_rapprochement($b2, $profil($t, 'expert'))['alertes'] === []);
$b3 = ['langues' => 'Français, Allemand'] + $besoin;
$n = noter_rapprochement($b3, $profil($t));
verifier('une langue sur deux : 5 points de moins, langue nommée', $n['score'] === 95 && str_contains($n['alertes'][0], 'Allemand'));
$b4 = ['date_debut' => '2026-11-01', 'tjm_max' => 600] + $besoin;
verifier('disponibilité et tarif inconnus : moitié des points', noter_rapprochement($b4, $profil($t))['score'] === 95);
verifier('disponible à temps, tarif dans le budget : 100', noter_rapprochement($b4, $profil($t, 'confirme', ['disponible_le' => '2026-10-15', 'tjm' => 550]))['score'] === 100);
$tard = noter_rapprochement($b4, $profil($t, 'confirme', ['disponible_le' => '2026-12-01', 'tjm' => 700]));
verifier('disponible trop tard, tarif trop haut : 90 et deux alertes', $tard['score'] === 90 && count($tard['alertes']) === 2);
verifier('tarif 10 % au-dessus : 2 points', noter_rapprochement($b4, $profil($t, 'confirme', ['disponible_le' => '2026-10-15', 'tjm' => 650]))['score'] === 97);
$sansTechno = ['technologies' => null] + $besoin;
verifier('besoin sans technologie : l\'intitulé porte la note', noter_rapprochement($sansTechno, $profil([]))['score'] === 100);
verifier('barème : total 100', array_sum(BAREME_RAPPROCHEMENT) === 100);
// --- Doublons : clé de téléphone
verifier('téléphone : indicatif et espaces ignorés', Import::cleTelephone('+352 621 37 37 95') === Import::cleTelephone('00352-621.373.795') && Import::cleTelephone('+33 6 12 34 56 78') === Import::cleTelephone('06 12 34 56 78'));
verifier('téléphone trop court ou absent : pas de comparaison', Import::cleTelephone('12 34') === '' && Import::cleTelephone(null) === '');
// --- Lot : appariement des fichiers déposés ensemble
$dossier = sys_get_temp_dir() . '/vivier-test-' . bin2hex(random_bytes(4));
mkdir($dossier);
$fichier = static function (string $nom, string $contenu = 'x') use ($dossier): array {
file_put_contents("$dossier/$nom", $contenu);
return ['tmp' => "$dossier/$nom", 'nom' => $nom, 'ext' => strtolower(pathinfo($nom, PATHINFO_EXTENSION)), 'mime' => 'x', 'taille' => 1];
};
$json = static fn (string $cv, string $nom): string => (string) json_encode(['identite' => ['prenom' => 'A', 'nom' => $nom], 'source' => ['fichier_origine' => $cv]]);
$r = Lot::apparier([
$fichier('profil-durand.json', $json('CV Durand.pdf', 'Durand')), $fichier('cv durand.PDF'), $fichier('rapport-durand.md'),
$fichier('martin.json', $json('CV_Martin.docx', 'Martin')), $fichier('CV_Martin.docx'),
$fichier('seul.json', $json('absent.pdf', 'Seul')), $fichier('orphelin.pdf'), $fichier('casse.json', '{pas du json'),
]);
verifier('lot : deux paires formées', count($r['paires']) === 2);
verifier('lot : CV associé sans tenir compte de la casse', $r['paires'][0]['cv']['nom'] === 'cv durand.PDF' && $r['paires'][1]['cv']['nom'] === 'CV_Martin.docx');
verifier('lot : rapport associé par le nom du candidat', ($r['paires'][0]['rapport']['nom'] ?? '') === 'rapport-durand.md' && $r['paires'][1]['rapport'] === null);
verifier('lot : CV manquant, fichier orphelin et JSON illisible signalés', count($r['erreurs']) === 3);
array_map('unlink', glob("$dossier/*")); rmdir($dossier);
// --- Sauvegarde : composition de l'email
[$sujet, $corps, $entetes] = Sauvegarde::composer('vivier@itclub.lu', 'Vivier : sauvegarde du 11/10/2026', "Bonjour,\r\ntexte accentué.", null, null);
verifier('email simple : sujet encodé, texte en UTF-8', str_starts_with($sujet, '=?UTF-8?B?') && base64_decode(substr($sujet, 10, -2)) === 'Vivier : sauvegarde du 11/10/2026'
&& str_contains($entetes, 'From: Vivier <vivier@itclub.lu>') && str_contains($entetes, 'text/plain; charset=UTF-8') && str_contains($corps, 'accentué'));
$piece = sys_get_temp_dir() . '/vivier-test.zip';
file_put_contents($piece, random_bytes(5000));
[$sujet, $corps, $entetes] = Sauvegarde::composer('vivier@itclub.lu', 'Essai', 'Texte', $piece, 'vivier-base-2026-10-11-0600.zip');
preg_match('/boundary="([^"]+)"/', $entetes, $m);
$parties = explode('--' . $m[1], $corps);
verifier('email avec pièce : deux parties et une fin', count($parties) === 4 && trim($parties[3]) === '--');
verifier('pièce jointe : nom, type, contenu intact', str_contains($parties[2], 'filename="vivier-base-2026-10-11-0600.zip"') && str_contains($parties[2], 'application/zip')
&& base64_decode(preg_replace('/\s+/', '', explode("\r\n\r\n", $parties[2], 2)[1])) === file_get_contents($piece));
verifier('pièce jointe : lignes de 76 caractères au plus', max(array_map('strlen', explode("\r\n", $parties[2]))) <= 76);
unlink($piece);
verifier('taille lisible', Sauvegarde::taille(512) === '1 Ko' && Sauvegarde::taille(2621440) === '2,5 Mo');
echo "$ok réussis, $ko échoués\n";
exit($ko > 0 ? 1 : 0);