Files
Classeo/backend/tests/Functional/Administration/Api/GuardianEndpointsTest.php
Mathias STRASSER 44ebe5e511 feat: Liaison parents-enfants avec gestion des tuteurs
Les parents doivent pouvoir suivre la scolarité de leurs enfants (notes,
emploi du temps, devoirs). Cela nécessite un lien formalisé entre le
compte parent et le compte élève, géré par les administrateurs.

Le lien est établi soit manuellement via l'interface d'administration,
soit automatiquement lors de l'activation du compte parent lorsque
l'invitation inclut un élève cible. Ce lien conditionne l'accès aux
données scolaires de l'enfant (autorisations vérifiées par un voter
dédié).
2026-02-12 08:38:19 +01:00

223 lines
6.9 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Tests\Functional\Administration\Api;
use ApiPlatform\Symfony\Bundle\Test\ApiTestCase;
use PHPUnit\Framework\Attributes\Test;
/**
* Tests for guardian (parent-student linking) API endpoints.
*
* @see Story 2.7 - Liaison Parents-Enfants
*/
final class GuardianEndpointsTest extends ApiTestCase
{
/**
* Opt-in for API Platform 5.0 behavior where kernel boot is explicit.
*
* @see https://github.com/api-platform/core/issues/6971
*/
protected static ?bool $alwaysBootKernel = true;
private const string STUDENT_ID = '00000000-0000-0000-0000-000000000001';
private const string GUARDIAN_ID = '00000000-0000-0000-0000-000000000002';
// =========================================================================
// GET /students/{studentId}/guardians — Security
// =========================================================================
/**
* Without a valid tenant subdomain, the endpoint returns 404.
* This is correct security behavior: don't reveal endpoint existence.
*/
#[Test]
public function getGuardiansReturns404WithoutTenant(): void
{
$client = static::createClient();
$client->request('GET', '/api/students/' . self::STUDENT_ID . '/guardians', [
'headers' => [
'Host' => 'localhost',
'Accept' => 'application/json',
],
]);
self::assertResponseStatusCodeSame(404);
}
/**
* With a valid tenant but no JWT token, the endpoint returns 401.
* Proves the endpoint exists and requires authentication.
*/
#[Test]
public function getGuardiansReturns401WithoutAuthentication(): void
{
$client = static::createClient();
$client->request('GET', 'http://ecole-alpha.classeo.local/api/students/' . self::STUDENT_ID . '/guardians', [
'headers' => [
'Accept' => 'application/json',
],
]);
self::assertResponseStatusCodeSame(401);
}
// =========================================================================
// POST /students/{studentId}/guardians — Security
// =========================================================================
/**
* Without a valid tenant subdomain, the endpoint returns 404.
*/
#[Test]
public function linkGuardianReturns404WithoutTenant(): void
{
$client = static::createClient();
$client->request('POST', '/api/students/' . self::STUDENT_ID . '/guardians', [
'headers' => [
'Host' => 'localhost',
'Accept' => 'application/json',
'Content-Type' => 'application/json',
],
'json' => [
'guardianId' => self::GUARDIAN_ID,
'relationshipType' => 'père',
],
]);
self::assertResponseStatusCodeSame(404);
}
/**
* With a valid tenant but no JWT token, the endpoint returns 401.
*/
#[Test]
public function linkGuardianReturns401WithoutAuthentication(): void
{
$client = static::createClient();
$client->request('POST', 'http://ecole-alpha.classeo.local/api/students/' . self::STUDENT_ID . '/guardians', [
'headers' => [
'Accept' => 'application/json',
'Content-Type' => 'application/json',
],
'json' => [
'guardianId' => self::GUARDIAN_ID,
'relationshipType' => 'père',
],
]);
self::assertResponseStatusCodeSame(401);
}
// =========================================================================
// POST /students/{studentId}/guardians — Validation
// =========================================================================
/**
* Without tenant, validation never fires — returns 404 before reaching processor.
*/
#[Test]
public function linkGuardianRejectsInvalidPayloadWithoutTenant(): void
{
$client = static::createClient();
$client->request('POST', '/api/students/' . self::STUDENT_ID . '/guardians', [
'headers' => [
'Host' => 'localhost',
'Accept' => 'application/json',
'Content-Type' => 'application/json',
],
'json' => [
'guardianId' => '',
'relationshipType' => '',
],
]);
// Without tenant → 404 (not 422)
self::assertResponseStatusCodeSame(404);
}
// =========================================================================
// DELETE /students/{studentId}/guardians/{guardianId} — Security
// =========================================================================
/**
* Without a valid tenant subdomain, the endpoint returns 404.
*/
#[Test]
public function unlinkGuardianReturns404WithoutTenant(): void
{
$client = static::createClient();
$client->request('DELETE', '/api/students/' . self::STUDENT_ID . '/guardians/' . self::GUARDIAN_ID, [
'headers' => [
'Host' => 'localhost',
'Accept' => 'application/json',
],
]);
self::assertResponseStatusCodeSame(404);
}
/**
* With a valid tenant but no JWT token, the endpoint returns 401.
*/
#[Test]
public function unlinkGuardianReturns401WithoutAuthentication(): void
{
$client = static::createClient();
$client->request('DELETE', 'http://ecole-alpha.classeo.local/api/students/' . self::STUDENT_ID . '/guardians/' . self::GUARDIAN_ID, [
'headers' => [
'Accept' => 'application/json',
],
]);
self::assertResponseStatusCodeSame(401);
}
// =========================================================================
// GET /me/children — Security
// =========================================================================
/**
* Without a valid tenant subdomain, the endpoint returns 404.
*/
#[Test]
public function getMyChildrenReturns404WithoutTenant(): void
{
$client = static::createClient();
$client->request('GET', '/api/me/children', [
'headers' => [
'Host' => 'localhost',
'Accept' => 'application/json',
],
]);
self::assertResponseStatusCodeSame(404);
}
/**
* With a valid tenant but no JWT token, the endpoint returns 401.
*/
#[Test]
public function getMyChildrenReturns401WithoutAuthentication(): void
{
$client = static::createClient();
$client->request('GET', 'http://ecole-alpha.classeo.local/api/me/children', [
'headers' => [
'Accept' => 'application/json',
],
]);
self::assertResponseStatusCodeSame(401);
}
}