feat: Provisionner automatiquement un nouvel établissement
Lorsqu'un super-admin crée un établissement via l'interface, le système doit automatiquement créer la base tenant, exécuter les migrations, créer le premier utilisateur admin et envoyer l'invitation — le tout de manière asynchrone pour ne pas bloquer la réponse HTTP. Ce mécanisme rend chaque établissement opérationnel dès sa création sans intervention manuelle sur l'infrastructure.
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Tests\Unit\Scolarite\Application\Query\GetClassStatisticsDetail;
|
||||
|
||||
use App\Scolarite\Application\Port\PeriodFinder;
|
||||
use App\Scolarite\Application\Port\PeriodInfo;
|
||||
use App\Scolarite\Application\Query\GetClassStatisticsDetail\GetClassStatisticsDetailHandler;
|
||||
use App\Scolarite\Application\Query\GetClassStatisticsDetail\GetClassStatisticsDetailQuery;
|
||||
use App\Scolarite\Domain\Service\AverageCalculator;
|
||||
use App\Scolarite\Domain\Service\TeacherStatisticsCalculator;
|
||||
use App\Scolarite\Infrastructure\ReadModel\InMemoryTeacherStatisticsReader;
|
||||
use App\Shared\Domain\Tenant\TenantId;
|
||||
use DateTimeImmutable;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
final class GetClassStatisticsDetailHandlerTest extends TestCase
|
||||
{
|
||||
private const string TEACHER_ID = '550e8400-e29b-41d4-a716-446655440010';
|
||||
private const string TENANT_ID = '550e8400-e29b-41d4-a716-446655440001';
|
||||
private const string CLASS_ID = '550e8400-e29b-41d4-a716-446655440020';
|
||||
private const string SUBJECT_ID = '550e8400-e29b-41d4-a716-446655440030';
|
||||
|
||||
private InMemoryTeacherStatisticsReader $reader;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->reader = new InMemoryTeacherStatisticsReader();
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function itReturnsEmptyWhenNoPeriodFound(): void
|
||||
{
|
||||
$handler = $this->createHandler(periodInfo: null);
|
||||
|
||||
$result = $handler($this->query());
|
||||
|
||||
self::assertNull($result->average);
|
||||
self::assertSame(0.0, $result->successRate);
|
||||
self::assertSame([], $result->students);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function itComputesClassStatisticsFromGrades(): void
|
||||
{
|
||||
$this->reader->feedClassGrades([8.0, 10.0, 12.0, 14.0, 16.0]);
|
||||
$this->reader->feedMonthlyAverages([
|
||||
['month' => '2026-01', 'average' => 11.0],
|
||||
['month' => '2026-02', 'average' => 12.5],
|
||||
]);
|
||||
$this->reader->feedStudentAverages([
|
||||
['studentId' => 's1', 'studentName' => 'Alice Dupont', 'average' => 14.0],
|
||||
['studentId' => 's2', 'studentName' => 'Bob Martin', 'average' => 7.0],
|
||||
]);
|
||||
|
||||
$handler = $this->createHandler(periodInfo: $this->currentPeriod());
|
||||
$result = $handler($this->query());
|
||||
|
||||
self::assertSame(12.0, $result->average);
|
||||
self::assertSame(80.0, $result->successRate); // 4/5 >= 10
|
||||
self::assertSame([0, 0, 0, 1, 2, 1, 1, 0], $result->distribution);
|
||||
self::assertCount(2, $result->evolution);
|
||||
self::assertCount(2, $result->students);
|
||||
self::assertFalse($result->students[0]->inDifficulty); // Alice 14.0 >= 8.0
|
||||
self::assertTrue($result->students[1]->inDifficulty); // Bob 7.0 < 8.0
|
||||
}
|
||||
|
||||
private function query(): GetClassStatisticsDetailQuery
|
||||
{
|
||||
return new GetClassStatisticsDetailQuery(
|
||||
teacherId: self::TEACHER_ID,
|
||||
classId: self::CLASS_ID,
|
||||
subjectId: self::SUBJECT_ID,
|
||||
tenantId: self::TENANT_ID,
|
||||
);
|
||||
}
|
||||
|
||||
private function createHandler(?PeriodInfo $periodInfo): GetClassStatisticsDetailHandler
|
||||
{
|
||||
$periodFinder = new class($periodInfo) implements PeriodFinder {
|
||||
public function __construct(private readonly ?PeriodInfo $info)
|
||||
{
|
||||
}
|
||||
|
||||
public function findForDate(DateTimeImmutable $date, TenantId $tenantId): ?PeriodInfo
|
||||
{
|
||||
return $this->info;
|
||||
}
|
||||
};
|
||||
|
||||
return new GetClassStatisticsDetailHandler(
|
||||
$this->reader,
|
||||
$periodFinder,
|
||||
new TeacherStatisticsCalculator(),
|
||||
new AverageCalculator(),
|
||||
);
|
||||
}
|
||||
|
||||
private function currentPeriod(): PeriodInfo
|
||||
{
|
||||
return new PeriodInfo(
|
||||
periodId: 'period-1',
|
||||
startDate: new DateTimeImmutable('2026-01-05'),
|
||||
endDate: new DateTimeImmutable('2026-03-31'),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user