2ecfe226af
- Migration retrofit Version20260528120000 : pose COMMENT ON TABLE/COLUMN sur les 11 tables metier existantes (53 colonnes) via le catalogue partage ColumnCommentsCatalog (Shared/Infrastructure/Database). - Commande app:apply-column-comments : rejoue le catalogue apres doctrine:schema:update --force (sinon l'ORM drop les commentaires absents du mapping PHP). Branchee dans makefile test-db-setup et workflow CI Gitea. - Test architecture tests/Architecture/ColumnsHaveSqlCommentTest : echoue si une colonne public n'a pas de col_description (hors doctrine_migration_versions et fake_site_aware_entity). Whitelist metier vide. - Convention documentee dans CLAUDE.md (regle ABSOLUE n°12) et .claude/rules/backend.md (section Migrations Doctrine) avec exemples et helper standardise pour les colonnes Timestampable/Blamable.
115 lines
4.2 KiB
PHP
115 lines
4.2 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Tests\Architecture;
|
|
|
|
use Doctrine\DBAL\ArrayParameterType;
|
|
use Doctrine\DBAL\Connection;
|
|
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
|
|
|
/**
|
|
* Garde-fou architecture : toute colonne d'une table metier doit porter une
|
|
* description SQL (`COMMENT ON COLUMN`).
|
|
*
|
|
* Postgres stocke la description dans `pg_description`, recuperable via
|
|
* `col_description(table_oid, column_position)`. Une colonne sans description
|
|
* remonte `NULL`. Le test parcourt `information_schema.columns` filtre sur le
|
|
* schema `public` et echoue si une seule colonne metier n'a pas de description.
|
|
*
|
|
* Tables ignorees :
|
|
* - `doctrine_migration_versions` : table system Doctrine, schema fige par la
|
|
* librairie.
|
|
* - Whitelist `EXCLUDED_TABLES` : doit rester vide ou justifiee — toute entree
|
|
* doit avoir un ticket Lesstime ouvert pour le retrofit.
|
|
*
|
|
* @internal
|
|
*/
|
|
final class ColumnsHaveSqlCommentTest extends KernelTestCase
|
|
{
|
|
/**
|
|
* Tables system, gerees par Doctrine — leur schema n'est pas notre.
|
|
*/
|
|
private const EXCLUDED_BUILTINS = [
|
|
'doctrine_migration_versions',
|
|
];
|
|
|
|
/**
|
|
* Entites mappees uniquement en `when@test` (fixtures techniques pour les
|
|
* tests d'integration, jamais en prod). Pas de migration, donc pas de
|
|
* lieu naturel pour poser un COMMENT ON COLUMN.
|
|
*
|
|
* @var list<string>
|
|
*/
|
|
private const EXCLUDED_TEST_FIXTURES = [
|
|
// tests/Fixtures/SiteAware/FakeSiteAwareEntity.php — fixture du module
|
|
// Sites pour couvrir le SiteScopedQueryExtension. Cree via schema:update
|
|
// sur la DB de test uniquement.
|
|
'fake_site_aware_entity',
|
|
];
|
|
|
|
/**
|
|
* Whitelist metier — DOIT rester vide ou justifiee.
|
|
*
|
|
* Chaque entree doit comporter (1) un commentaire expliquant pourquoi la
|
|
* table n'est pas encore documentee et (2) la reference d'un ticket
|
|
* Lesstime ouvert pour le retrofit.
|
|
*
|
|
* @var list<string>
|
|
*/
|
|
private const EXCLUDED_TABLES = [];
|
|
|
|
public function testAllPublicColumnsHaveASqlComment(): void
|
|
{
|
|
/** @var Connection $conn */
|
|
$conn = self::getContainer()->get('doctrine.dbal.default_connection');
|
|
|
|
$excluded = [...self::EXCLUDED_BUILTINS, ...self::EXCLUDED_TEST_FIXTURES, ...self::EXCLUDED_TABLES];
|
|
|
|
$rows = $conn->fetchAllAssociative(
|
|
<<<'SQL'
|
|
SELECT c.table_name, c.column_name
|
|
FROM information_schema.columns c
|
|
WHERE c.table_schema = 'public'
|
|
AND c.table_name NOT IN (:excluded)
|
|
AND col_description(
|
|
(c.table_schema || '.' || c.table_name)::regclass,
|
|
c.ordinal_position
|
|
) IS NULL
|
|
ORDER BY c.table_name, c.ordinal_position
|
|
SQL,
|
|
['excluded' => $excluded],
|
|
['excluded' => ArrayParameterType::STRING],
|
|
);
|
|
|
|
if ([] !== $rows) {
|
|
$missing = array_map(
|
|
static fn (array $row): string => sprintf('%s.%s', $row['table_name'], $row['column_name']),
|
|
$rows,
|
|
);
|
|
|
|
self::fail(sprintf(
|
|
"%d colonne(s) sans COMMENT ON COLUMN — ajouter une description SQL dans la migration qui les cree (cf. .claude/rules/backend.md § Migrations Doctrine) :\n - %s",
|
|
count($missing),
|
|
implode("\n - ", $missing),
|
|
));
|
|
}
|
|
|
|
// Garde : si la requete ne renvoie rien et qu'aucune table publique
|
|
// n'existe (sauf doctrine_migration_versions), le test deviendrait un
|
|
// faux positif vert. On verifie qu'il y a bien des tables a auditer.
|
|
$tableCount = (int) $conn->fetchOne(
|
|
<<<'SQL'
|
|
SELECT COUNT(*)
|
|
FROM information_schema.tables
|
|
WHERE table_schema = 'public'
|
|
AND table_name NOT IN (:excluded)
|
|
SQL,
|
|
['excluded' => $excluded],
|
|
['excluded' => ArrayParameterType::STRING],
|
|
);
|
|
|
|
self::assertGreaterThan(0, $tableCount, 'Aucune table publique a auditer : schema vide ou whitelist trop large.');
|
|
}
|
|
}
|