$columns */ $columns = $this->getEm()->getConnection()->fetchAllAssociative( "SELECT column_name FROM information_schema.columns " ."WHERE table_schema = 'public' AND table_name = 'client'", ); $names = array_map(static fn (array $r): string => $r['column_name'], $columns); foreach (['first_name', 'last_name', 'phone_primary', 'phone_secondary', 'email'] as $dropped) { self::assertNotContains( $dropped, $names, sprintf('La colonne client.%s aurait du etre supprimee (refonte contact).', $dropped), ); } } public function testCompanyNameActivePartialIndexExistsExactlyOnce(): void { $rows = $this->clientIndexes(); $companyNameIndexes = array_filter( $rows, static fn (array $r): bool => 'uq_client_company_name_active' === $r['indexname'], ); self::assertCount( 1, $companyNameIndexes, 'Il doit exister exactement UN index uq_client_company_name_active.', ); // Confirme la nature fonctionnelle (LOWER) + partielle (WHERE) de l'index. // Postgres serialise l'expression sous la forme `lower((company_name)::text)`, // d'ou des verifications de sous-chaines distinctes. $def = strtolower((string) array_values($companyNameIndexes)[0]['indexdef']); self::assertStringContainsString('unique', $def); self::assertStringContainsString('lower', $def); self::assertStringContainsString('company_name', $def); self::assertStringContainsString('where', $def, 'L\'index doit etre partiel (clause WHERE sur les actifs).'); } public function testNoSirenOrEmailUniqueIndex(): void { $names = array_map(static fn (array $r): string => $r['indexname'], $this->clientIndexes()); // RG-1.15 / RG-1.17 supprimees (Q4) : aucun index unique siren / email. self::assertNotContains('uq_client_siren_active', $names); self::assertNotContains('uq_client_email_active', $names); } /** * @return list */ private function clientIndexes(): array { self::bootKernel(); /** @var list $rows */ return $this->getEm()->getConnection()->fetchAllAssociative( "SELECT indexname, indexdef FROM pg_indexes WHERE schemaname = 'public' AND tablename = 'client'", ); } }