Files
Starseed/tests/Module/Commercial/Api/ClientMigrationTest.php
T
Matthieu 74f0f981d8
Pull Request — Quality gate / Backend (PHP CS + PHPUnit) (pull_request) Successful in 1m57s
Pull Request — Quality gate / Frontend (lint + Vitest + build) (pull_request) Successful in 1m13s
refactor(commercial) : suppression du contact principal inline du Client (M1)
Le contact principal (firstName, lastName, phonePrimary, phoneSecondary,
email) n'est plus porte par l'entite Client : les contacts vivent uniquement
dans ClientContact (onglet Contact). RG-1.01 et RG-1.02 supprimees du Client
(equivalent RG-1.05 / RG-1.14 sur ClientContact).

- Migration (namespace racine DoctrineMigrations, ordre par timestamp) :
  backfill des clients sans contact vers client_contact (position 0) puis
  DROP des 5 colonnes inline. down() best-effort documente.
- Entite Client : retrait des 5 props + getters/setters + groupes.
- ClientProcessor : MAIN_FIELDS / changedBusinessFields / normalize alleges,
  validateMainContact (RG-1.01) supprimee.
- Recherche repertoire : companyName seul (D1).
- Export XLSX : colonnes de contact retirees (D2).
- Fixtures + catalogue de commentaires SQL alignes.
- Tests fonctionnels et unitaires mis a jour.
2026-06-03 15:31:01 +02:00

99 lines
3.9 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Tests\Module\Commercial\Api;
/**
* Tests de structure / migration M1 (ERP-60).
*
* Verifie la decision Q4 (29/05/2026) au niveau du schema Postgres :
* - l'unique index partiel fonctionnel uq_client_company_name_active existe
* (un seul, sur LOWER(company_name), partiel sur les actifs non archives /
* non supprimes) — seule unicite metier conservee (RG-1.16) ;
* - les anciens index uq_client_siren_active (RG-1.15) et uq_client_email_active
* (RG-1.17) ont ete supprimes / ne sont jamais crees.
*
* Verifie aussi la refonte contact (Version20260603120000) : les 5 colonnes de
* contact principal inline ont ete supprimees de la table `client`.
*
* @internal
*/
final class ClientMigrationTest extends AbstractCommercialApiTestCase
{
/**
* Refonte contact : first_name / last_name / phone_primary / phone_secondary
* / email ne doivent plus exister sur la table `client` (deplaces vers
* client_contact). NB : le backfill de la migration ne s'exerce que sur une
* base portant des donnees pre-refonte ; sur le schema de test (table client
* vierge au moment de la migration) il est un no-op, donc non assertable ici
* au runtime — seul l'etat de schema final est verifie.
*/
public function testInlineContactColumnsAreDropped(): void
{
self::bootKernel();
/** @var list<array{column_name: string}> $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<array{indexname: string, indexdef: string}>
*/
private function clientIndexes(): array
{
self::bootKernel();
/** @var list<array{indexname: string, indexdef: string}> $rows */
return $this->getEm()->getConnection()->fetchAllAssociative(
"SELECT indexname, indexdef FROM pg_indexes WHERE schemaname = 'public' AND tablename = 'client'",
);
}
}