getMessage()}\n"; $failed++; } }; $assert = static function (bool $condition, string $message = 'Assertion échouée'): void { if (!$condition) { throw new RuntimeException($message); } }; try { Migrations::run($root . '/migrations'); I18n::setLocale('fr'); $db = DB::pdo(); Auth::start(); $test('Toutes les migrations sont appliquées', function () use ($db, $root, $assert): void { $expected = count(glob($root . '/migrations/*.sql') ?: []); $actual = (int) $db->query('SELECT COUNT(*) FROM _migrations')->fetchColumn(); $assert($actual === $expected, "$actual/$expected migrations"); }); $test("Les voies d’administration des médicaments sont initialisées", function () use ($db, $assert): void { $routes = $db ->query('SELECT accro FROM ref_med_administration ORDER BY id') ->fetchAll(PDO::FETCH_COLUMN); $assert(count($routes) === 14, 'Référentiel des voies incomplet'); $assert(in_array('PO', $routes, true) && in_array('SC', $routes, true) && in_array('IV', $routes, true)); $historyColumns = array_column( $db->query('PRAGMA table_info(animal_history)')->fetchAll(PDO::FETCH_ASSOC), 'name', ); foreach (['type', 'label', 'details'] as $column) { $assert(in_array($column, $historyColumns, true), 'Colonne historique absente : ' . $column); } $assert( (int) $db->query("SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='icad_cache'")->fetchColumn() === 1, 'Cache i-CAD absent', ); }); $test('Une installation vierge initialise les médias privés', function () use ($db, $assert): void { $columns = array_column($db->query('PRAGMA table_info(animal_photos)')->fetchAll(PDO::FETCH_ASSOC), 'name'); $assert(in_array('is_public', $columns, true), 'Colonne is_public absente'); $assert( (int) $db ->query("SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='medical_photos'") ->fetchColumn() === 1, 'Table medical_photos absente', ); PrivateMediaService::migrateLegacy(); }); $test('La base SQLite temporaire est intègre', function () use ($db, $assert): void { $assert($db->query('PRAGMA integrity_check')->fetchColumn() === 'ok'); }); $test('La configuration initiale des salles est complète', function () use ($assert): void { $rooms = ShelterRoomService::byCode(false); foreach (['silver', 'twix', 'care', 'quarantine'] as $code) { $assert(isset($rooms[$code]), 'Salle absente : ' . $code); } $assert($rooms['silver']['name'] === 'Salle collective 1'); $assert($rooms['twix']['name'] === 'Salle collective 2'); $assert(ShelterRoomService::periods($rooms['silver']) === ['morning', 'evening']); $assert(ShelterRoomService::accepts($rooms['silver'], ['species' => 'chien', 'sex' => 'F', 'fiv' => 0])); $assert( count($rooms['care']['boxes']) === 5 && count($rooms['quarantine']['boxes']) === 5, 'Les cinq box initiaux ne sont pas disponibles', ); }); $test('Les tournées acceptent quatre créneaux configurables', function () use ($db, $assert): void { foreach (ShelterRoomService::PERIODS as $period) { $db->prepare('INSERT INTO care_rounds(round_date,period) VALUES(?,?)')->execute(['2099-01-01', $period]); } $assert((int) $db->query("SELECT COUNT(*) FROM care_rounds WHERE round_date='2099-01-01'")->fetchColumn() === 4); $db->exec("DELETE FROM care_rounds WHERE round_date='2099-01-01'"); }); $test('Les critères d’accueil d’une salle sont appliqués', function () use ($assert): void { $room = ['allowed_species' => 'chat', 'allowed_sex' => 'M', 'required_fiv' => 'positive']; $assert(ShelterRoomService::accepts($room, ['species' => 'chat', 'sex' => 'M', 'fiv' => 1])); $assert(!ShelterRoomService::accepts($room, ['species' => 'chien', 'sex' => 'M', 'fiv' => 1])); $assert(!ShelterRoomService::accepts($room, ['species' => 'chat', 'sex' => 'F', 'fiv' => 1])); $assert(!ShelterRoomService::accepts($room, ['species' => 'chat', 'sex' => 'M', 'fiv' => 0])); }); $test('Les paramètres sont enregistrés et relus', function () use ($assert): void { AppSettings::save(['association_name' => 'Refuge Test', 'app_language' => 'en'], null); $assert(AppSettings::get('association_name') === 'Refuge Test'); $assert(AppSettings::get('app_language') === 'en'); I18n::setLocale('fr'); }); $test('Les frais d’adoption sont configurables par espèce', function () use ($db, $assert): void { $fees = PricingService::adoptionFees(); $assert(isset($fees['chat']), 'Tarif du chat absent'); $db->exec("UPDATE adoption_fee_rules SET fee_before_sterilization=175,fee_after_sterilization=225,is_free=0 WHERE species_code='chat'"); $assert(PricingService::adoptionFee('chat', false) === 175); $assert(PricingService::adoptionFee('chat', true) === 225); $db->exec("UPDATE adoption_fee_rules SET is_free=1 WHERE species_code='chat'"); $assert(PricingService::adoptionFee('chat', true) === null, 'Le tarif libre n’est pas appliqué'); }); $test('Les identifiants RNA et SIRET sont contrôlés', function () use ($assert): void { $assert(Ids::validRna('W123456789')); $assert(!Ids::validRna('W1234')); $assert(Ids::validSiret('73282932000074')); $assert(!Ids::validSiret('73282932000075')); }); $test('Les quatre bilans biologiques existent pour chats et chiens', function () use ($db, $assert): void { $rows = $db ->query( 'SELECT analyzer_code,species_code,COUNT(*) total FROM lab_reference_ranges GROUP BY analyzer_code,species_code', ) ->fetchAll(PDO::FETCH_ASSOC); $sets = []; foreach ($rows as $row) { $sets[$row['analyzer_code']][$row['species_code']] = (int) $row['total']; } foreach (['vetscan-hm5', 'renal', 'hepatic', 'thyroid'] as $code) { foreach (['chat', 'chien'] as $species) { $assert(($sets[$code][$species] ?? 0) > 0, "Référentiel $code/$species absent"); } } $assert( (string) $db ->query("SELECT analyzer_name FROM lab_reference_ranges WHERE analyzer_code='vetscan-hm5' LIMIT 1") ->fetchColumn() === 'Bilan complet', ); }); $test('Une espèce peut recevoir un référentiel biologique modifiable', function () use ($db, $assert): void { $copied = LabReferenceService::copySpecies('chat', 'lapin', null); $assert($copied > 0, 'Aucune référence copiée'); $count = (int) $db->query("SELECT COUNT(*) FROM lab_reference_ranges WHERE species_code='lapin'")->fetchColumn(); $assert($count === $copied, 'Références incomplètes'); }); $test('La mise en forme du site public échappe le HTML', function () use ($assert): void { $html = PublicContentFormatter::render("## Titre\n- **Texte**\n"); $assert(str_contains($html, '