feat: Gestion des périodes scolaires
L'administration d'un établissement nécessite de découper l'année scolaire en trimestres ou semestres avant de pouvoir saisir les notes et générer les bulletins. Ce module permet de configurer les périodes par année scolaire (current/previous/next résolus en UUID v5 déterministes), de modifier les dates individuelles avec validation anti-chevauchement, et de consulter la période en cours avec le décompte des jours restants. Les dates par défaut de février s'adaptent aux années bissextiles. Le repository utilise UPSERT transactionnel pour garantir l'intégrité lors du changement de mode (trimestres ↔ semestres). Les domain events de Subject sont étendus pour couvrir toutes les mutations (code, couleur, description) en plus du renommage.
This commit is contained in:
@@ -0,0 +1,162 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Tests\Functional\Administration\Api;
|
||||
|
||||
use ApiPlatform\Symfony\Bundle\Test\ApiTestCase;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
|
||||
/**
|
||||
* Tests for academic periods API endpoints.
|
||||
*
|
||||
* @see Story 2.3 - Gestion des périodes scolaires
|
||||
*/
|
||||
final class PeriodsEndpointsTest extends ApiTestCase
|
||||
{
|
||||
protected static ?bool $alwaysBootKernel = true;
|
||||
|
||||
// =========================================================================
|
||||
// Security - Without tenant
|
||||
// =========================================================================
|
||||
|
||||
#[Test]
|
||||
public function getPeriodsReturns404WithoutTenant(): void
|
||||
{
|
||||
$client = static::createClient();
|
||||
|
||||
$client->request('GET', '/api/academic-years/current/periods', [
|
||||
'headers' => [
|
||||
'Host' => 'localhost',
|
||||
'Accept' => 'application/json',
|
||||
],
|
||||
]);
|
||||
|
||||
self::assertResponseStatusCodeSame(404);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function configurePeriodsReturns404WithoutTenant(): void
|
||||
{
|
||||
$client = static::createClient();
|
||||
|
||||
$client->request('PUT', '/api/academic-years/current/periods', [
|
||||
'headers' => [
|
||||
'Host' => 'localhost',
|
||||
'Accept' => 'application/json',
|
||||
'Content-Type' => 'application/json',
|
||||
],
|
||||
'json' => ['periodType' => 'trimester', 'startYear' => 2025],
|
||||
]);
|
||||
|
||||
self::assertResponseStatusCodeSame(404);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function updatePeriodReturns404WithoutTenant(): void
|
||||
{
|
||||
$client = static::createClient();
|
||||
|
||||
$client->request('PATCH', '/api/academic-years/current/periods/1', [
|
||||
'headers' => [
|
||||
'Host' => 'localhost',
|
||||
'Accept' => 'application/json',
|
||||
'Content-Type' => 'application/merge-patch+json',
|
||||
],
|
||||
'json' => ['startDate' => '2025-09-02', 'endDate' => '2025-11-30'],
|
||||
]);
|
||||
|
||||
self::assertResponseStatusCodeSame(404);
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// Security - Without authentication (with tenant)
|
||||
// =========================================================================
|
||||
|
||||
#[Test]
|
||||
public function getPeriodsReturns401WithoutAuthentication(): void
|
||||
{
|
||||
$client = static::createClient();
|
||||
|
||||
$client->request('GET', 'http://ecole-alpha.classeo.local/api/academic-years/current/periods', [
|
||||
'headers' => [
|
||||
'Accept' => 'application/json',
|
||||
],
|
||||
]);
|
||||
|
||||
self::assertResponseStatusCodeSame(401);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function configurePeriodsReturns401WithoutAuthentication(): void
|
||||
{
|
||||
$client = static::createClient();
|
||||
|
||||
$client->request('PUT', 'http://ecole-alpha.classeo.local/api/academic-years/current/periods', [
|
||||
'headers' => [
|
||||
'Accept' => 'application/json',
|
||||
'Content-Type' => 'application/json',
|
||||
],
|
||||
'json' => ['periodType' => 'trimester', 'startYear' => 2025],
|
||||
]);
|
||||
|
||||
self::assertResponseStatusCodeSame(401);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function updatePeriodReturns401WithoutAuthentication(): void
|
||||
{
|
||||
$client = static::createClient();
|
||||
|
||||
$client->request('PATCH', 'http://ecole-alpha.classeo.local/api/academic-years/current/periods/1', [
|
||||
'headers' => [
|
||||
'Accept' => 'application/json',
|
||||
'Content-Type' => 'application/merge-patch+json',
|
||||
],
|
||||
'json' => ['startDate' => '2025-09-02', 'endDate' => '2025-11-30'],
|
||||
]);
|
||||
|
||||
self::assertResponseStatusCodeSame(401);
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// Special identifiers - 'current', 'next', 'previous'
|
||||
// =========================================================================
|
||||
|
||||
#[Test]
|
||||
public function getPeriodsAcceptsCurrentIdentifier(): void
|
||||
{
|
||||
$client = static::createClient();
|
||||
|
||||
$client->request('GET', 'http://ecole-alpha.classeo.local/api/academic-years/current/periods', [
|
||||
'headers' => ['Accept' => 'application/json'],
|
||||
]);
|
||||
|
||||
// 401 (no auth) not 404 (invalid id) — proves 'current' is accepted
|
||||
self::assertResponseStatusCodeSame(401);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function getPeriodsAcceptsNextIdentifier(): void
|
||||
{
|
||||
$client = static::createClient();
|
||||
|
||||
$client->request('GET', 'http://ecole-alpha.classeo.local/api/academic-years/next/periods', [
|
||||
'headers' => ['Accept' => 'application/json'],
|
||||
]);
|
||||
|
||||
self::assertResponseStatusCodeSame(401);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function getPeriodsAcceptsPreviousIdentifier(): void
|
||||
{
|
||||
$client = static::createClient();
|
||||
|
||||
$client->request('GET', 'http://ecole-alpha.classeo.local/api/academic-years/previous/periods', [
|
||||
'headers' => ['Accept' => 'application/json'],
|
||||
]);
|
||||
|
||||
self::assertResponseStatusCodeSame(401);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user