L'admin doit pouvoir voir en un coup d'œil quelles matières sont actives (notes saisies) pour décider lesquelles peuvent être supprimées sans perte de données. Auparavant, la suppression d'une matière était silencieuse : elle cascade-deletait évaluations et notes sans avertir. La liste des matières affiche désormais les compteurs d'enseignants, classes, évaluations et notes. La suppression déclenche une confirmation explicite quand la matière contient des notes, avec récapitulatif des volumes impactés, pour rendre l'action irréversible consciente. Côté tests, un endpoint de seeding HTTP remplace les appels docker exec dans les E2E (gain ~30-60s → 5-10s par test), et un trait partagé factorise le SQL de seeding entre les deux suites fonctionnelles.
175 lines
5.9 KiB
PHP
175 lines
5.9 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Tests\Functional\Helpers;
|
|
|
|
use Doctrine\DBAL\Connection;
|
|
use Ramsey\Uuid\Uuid;
|
|
|
|
use function sprintf;
|
|
|
|
/**
|
|
* Mutualise les helpers SQL de seeding pour les tests fonctionnels qui couvrent
|
|
* les statistiques de matière (compteurs d'évaluations, de notes, d'enseignants
|
|
* et de classes). Deux suites partagent exactement ces inserts :
|
|
*
|
|
* - `DoctrineSubjectGradeStatsReaderTest` (périmètre Scolarite — reader dédié)
|
|
* - `DbalPaginatedSubjectsReaderTest` (périmètre Administration — reader paginé)
|
|
*
|
|
* Chaque classe utilisatrice garde ses propres constantes d'UUID (plages
|
|
* disjointes) et expose sa `Connection` via `connection()`. Le trait n'a pas
|
|
* d'état : il ne fait que factoriser le SQL brut.
|
|
*/
|
|
trait SubjectStatsSeedingTrait
|
|
{
|
|
abstract protected function connection(): Connection;
|
|
|
|
protected function insertSubject(string $tenantId, string $schoolId, string $name, string $code): string
|
|
{
|
|
$id = Uuid::uuid4()->toString();
|
|
$this->connection()->executeStatement(
|
|
"INSERT INTO subjects (id, tenant_id, school_id, name, code, status, created_at, updated_at)
|
|
VALUES (:id, :tenant, :school, :name, :code, 'active', NOW(), NOW())",
|
|
[
|
|
'id' => $id,
|
|
'tenant' => $tenantId,
|
|
'school' => $schoolId,
|
|
'name' => $name,
|
|
'code' => $code,
|
|
],
|
|
);
|
|
|
|
return $id;
|
|
}
|
|
|
|
protected function insertClass(string $tenantId, string $schoolId): string
|
|
{
|
|
$id = Uuid::uuid4()->toString();
|
|
$this->connection()->executeStatement(
|
|
"INSERT INTO school_classes (id, tenant_id, school_id, academic_year_id, name, level, status, created_at, updated_at)
|
|
VALUES (:id, :tenant, :school, :year, '6e A', '6e', 'active', NOW(), NOW())",
|
|
[
|
|
'id' => $id,
|
|
'tenant' => $tenantId,
|
|
'school' => $schoolId,
|
|
'year' => $schoolId,
|
|
],
|
|
);
|
|
|
|
return $id;
|
|
}
|
|
|
|
protected function insertUser(string $tenantId, string $email): string
|
|
{
|
|
$id = Uuid::uuid4()->toString();
|
|
$this->connection()->executeStatement(
|
|
"INSERT INTO users (id, tenant_id, email, first_name, last_name, roles, statut, school_name, image_rights_status, created_at, updated_at)
|
|
VALUES (:id, :tenant, :email, 'Test', 'User', '[\"ROLE_USER\"]', 'active', 'Test School', 'not_requested', NOW(), NOW())",
|
|
[
|
|
'id' => $id,
|
|
'tenant' => $tenantId,
|
|
'email' => $email,
|
|
],
|
|
);
|
|
|
|
return $id;
|
|
}
|
|
|
|
protected function insertTeacherAssignment(
|
|
string $tenantId,
|
|
string $teacherId,
|
|
string $classId,
|
|
string $subjectId,
|
|
string $academicYearId,
|
|
): void {
|
|
$this->connection()->executeStatement(
|
|
"INSERT INTO teacher_assignments (id, tenant_id, teacher_id, school_class_id, subject_id, academic_year_id, start_date, status, created_at, updated_at)
|
|
VALUES (:id, :tenant, :teacher, :class, :subject, :year, NOW(), 'active', NOW(), NOW())",
|
|
[
|
|
'id' => Uuid::uuid4()->toString(),
|
|
'tenant' => $tenantId,
|
|
'teacher' => $teacherId,
|
|
'class' => $classId,
|
|
'subject' => $subjectId,
|
|
'year' => $academicYearId,
|
|
],
|
|
);
|
|
}
|
|
|
|
protected function insertEvaluation(
|
|
string $tenantId,
|
|
string $classId,
|
|
string $subjectId,
|
|
string $teacherId,
|
|
string $title,
|
|
): string {
|
|
$id = Uuid::uuid4()->toString();
|
|
$this->connection()->executeStatement(
|
|
'INSERT INTO evaluations (id, tenant_id, class_id, subject_id, teacher_id, title, evaluation_date)
|
|
VALUES (:id, :tenant, :class, :subject, :teacher, :title, CURRENT_DATE)',
|
|
[
|
|
'id' => $id,
|
|
'tenant' => $tenantId,
|
|
'class' => $classId,
|
|
'subject' => $subjectId,
|
|
'teacher' => $teacherId,
|
|
'title' => $title,
|
|
],
|
|
);
|
|
|
|
return $id;
|
|
}
|
|
|
|
protected function insertGrade(string $tenantId, string $evaluationId, string $studentId, float $value): void
|
|
{
|
|
$this->connection()->executeStatement(
|
|
'INSERT INTO grades (id, tenant_id, evaluation_id, student_id, value, created_by)
|
|
VALUES (:id, :tenant, :eval, :student, :value, :student)',
|
|
[
|
|
'id' => Uuid::uuid4()->toString(),
|
|
'tenant' => $tenantId,
|
|
'eval' => $evaluationId,
|
|
'student' => $studentId,
|
|
'value' => $value,
|
|
],
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Purge les tables de seeding pour les tenants donnés.
|
|
*
|
|
* L'ordre respecte les clés étrangères (grades → evaluations → teacher_assignments → subjects → school_classes → users).
|
|
*
|
|
* @param list<string> $tenantIds
|
|
*/
|
|
protected function cleanupSubjectStatsData(array $tenantIds): void
|
|
{
|
|
if ($tenantIds === []) {
|
|
return;
|
|
}
|
|
|
|
$placeholders = implode(',', array_map(static fn (int $i) => ':tenant_' . $i, array_keys($tenantIds)));
|
|
$params = [];
|
|
foreach ($tenantIds as $i => $tenantId) {
|
|
$params['tenant_' . $i] = $tenantId;
|
|
}
|
|
|
|
foreach (
|
|
[
|
|
'grades',
|
|
'evaluations',
|
|
'teacher_assignments',
|
|
'subjects',
|
|
'school_classes',
|
|
'users',
|
|
] as $table
|
|
) {
|
|
$this->connection()->executeStatement(
|
|
sprintf('DELETE FROM %s WHERE tenant_id IN (%s)', $table, $placeholders),
|
|
$params,
|
|
);
|
|
}
|
|
}
|
|
}
|