From 0f3e57c6e636502167dd84d10b1c1912e9ab3832 Mon Sep 17 00:00:00 2001 From: Mathias STRASSER Date: Tue, 10 Mar 2026 22:58:06 +0100 Subject: [PATCH] fix(demo): invalidate paginated caches after seeding Invalidate tenant read-model caches after generating demo data so seeded users, classes and assignments appear immediately in the UI. --- .../Service/DemoDataGenerator.php | 17 ++++++ .../Service/DemoDataGeneratorTest.php | 57 +++++++++++++++++++ 2 files changed, 74 insertions(+) diff --git a/backend/src/Administration/Infrastructure/Service/DemoDataGenerator.php b/backend/src/Administration/Infrastructure/Service/DemoDataGenerator.php index 225f02b..9885900 100644 --- a/backend/src/Administration/Infrastructure/Service/DemoDataGenerator.php +++ b/backend/src/Administration/Infrastructure/Service/DemoDataGenerator.php @@ -6,6 +6,7 @@ namespace App\Administration\Infrastructure\Service; use App\Administration\Application\Port\OfficialCalendarProvider; use App\Administration\Application\Port\PasswordHasher; +use App\Administration\Application\Service\Cache\PaginatedQueryCache; use App\Administration\Domain\Model\AcademicYear\DefaultPeriods; use App\Administration\Domain\Model\AcademicYear\PeriodType; use App\Administration\Domain\Model\ClassAssignment\ClassAssignment; @@ -69,6 +70,7 @@ final readonly class DemoDataGenerator private \App\Administration\Domain\Model\SchoolCalendar\SchoolCalendarRepository $schoolCalendarRepository, private GradingConfigurationRepository $gradingConfigurationRepository, private PasswordHasher $passwordHasher, + private PaginatedQueryCache $paginatedQueryCache, private Clock $clock, private TenantContext $tenantContext, private CurrentAcademicYearResolver $currentAcademicYearResolver, @@ -114,6 +116,7 @@ final readonly class DemoDataGenerator $this->seedTeacherAssignments($tenantId, $academicYearId, $users, $classes, $subjects, $now, $result); $this->seedStudentGuardianLinks($tenantId, $users, $now, $result); $this->seedScheduleSlots($tenantId, $classes, $subjects, $users, $academicYearStartYear, $now, $result); + $this->invalidateReadModelCaches((string) $tenantId); return $result; } finally { @@ -667,6 +670,20 @@ final readonly class DemoDataGenerator return $startYear; } + private function invalidateReadModelCaches(string $tenantId): void + { + foreach ([ + 'users', + 'classes', + 'subjects', + 'assignments', + 'parent_invitations', + 'students_image_rights', + ] as $entityType) { + $this->paginatedQueryCache->invalidate($entityType, $tenantId); + } + } + private function emailFor(string $emailSlug, string $subdomain): string { return sprintf('%s.%s@classeo.test', strtolower($emailSlug), strtolower($subdomain)); diff --git a/backend/tests/Unit/Administration/Infrastructure/Service/DemoDataGeneratorTest.php b/backend/tests/Unit/Administration/Infrastructure/Service/DemoDataGeneratorTest.php index 9c6515b..eb924ec 100644 --- a/backend/tests/Unit/Administration/Infrastructure/Service/DemoDataGeneratorTest.php +++ b/backend/tests/Unit/Administration/Infrastructure/Service/DemoDataGeneratorTest.php @@ -4,8 +4,10 @@ declare(strict_types=1); namespace App\Tests\Unit\Administration\Infrastructure\Service; +use App\Administration\Application\Dto\PaginatedResult; use App\Administration\Application\Port\OfficialCalendarProvider; use App\Administration\Application\Port\PasswordHasher; +use App\Administration\Application\Service\Cache\PaginatedQueryCache; use App\Administration\Domain\Model\AcademicYear\PeriodType; use App\Administration\Domain\Model\SchoolCalendar\CalendarEntry; use App\Administration\Domain\Model\SchoolCalendar\CalendarEntryId; @@ -34,6 +36,8 @@ use DateTimeImmutable; use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\TestCase; use RuntimeException; +use Symfony\Component\Cache\Adapter\ArrayAdapter; +use Symfony\Component\Cache\Adapter\TagAwareAdapter; final class DemoDataGeneratorTest extends TestCase { @@ -50,6 +54,7 @@ final class DemoDataGeneratorTest extends TestCase private InMemoryPeriodConfigurationRepository $periodConfigurationRepository; private InMemorySchoolCalendarRepository $schoolCalendarRepository; private GradingConfigurationRepository $gradingConfigurationRepository; + private PaginatedQueryCache $paginatedQueryCache; private DemoDataGenerator $generator; private TenantConfig $tenantConfig; @@ -65,6 +70,7 @@ final class DemoDataGeneratorTest extends TestCase $this->periodConfigurationRepository = new InMemoryPeriodConfigurationRepository(); $this->schoolCalendarRepository = new InMemorySchoolCalendarRepository(); $this->gradingConfigurationRepository = new InMemoryGradingConfigurationRepository(); + $this->paginatedQueryCache = new PaginatedQueryCache(new TagAwareAdapter(new ArrayAdapter())); $clock = new class implements Clock { public function now(): DateTimeImmutable @@ -136,6 +142,7 @@ final class DemoDataGeneratorTest extends TestCase schoolCalendarRepository: $this->schoolCalendarRepository, gradingConfigurationRepository: $this->gradingConfigurationRepository, passwordHasher: $passwordHasher, + paginatedQueryCache: $this->paginatedQueryCache, clock: $clock, tenantContext: $tenantContext, currentAcademicYearResolver: $currentAcademicYearResolver, @@ -255,6 +262,56 @@ final class DemoDataGeneratorTest extends TestCase self::assertCount(30, $result->accounts); } + #[Test] + public function itInvalidatesPaginatedReadModelCacheAfterGeneration(): void + { + $loadCount = 0; + + $this->paginatedQueryCache->getOrLoad( + 'users', + self::TENANT_ID, + ['page' => 1, 'limit' => 30, 'role' => null, 'statut' => null, 'search' => null], + static function () use (&$loadCount): PaginatedResult { + ++$loadCount; + + return new PaginatedResult( + items: [], + total: 0, + page: 1, + limit: 30, + ); + }, + ); + + self::assertSame(1, $loadCount); + + $this->generator->generate( + tenantConfig: $this->tenantConfig, + password: 'DemoPassword123!', + schoolName: 'Établissement Démo', + zone: SchoolZone::B, + periodType: PeriodType::TRIMESTER, + ); + + $this->paginatedQueryCache->getOrLoad( + 'users', + self::TENANT_ID, + ['page' => 1, 'limit' => 30, 'role' => null, 'statut' => null, 'search' => null], + static function () use (&$loadCount): PaginatedResult { + ++$loadCount; + + return new PaginatedResult( + items: [], + total: 30, + page: 1, + limit: 30, + ); + }, + ); + + self::assertSame(2, $loadCount); + } + private function resolveCurrentAcademicYearId(): \App\Administration\Domain\Model\SchoolClass\AcademicYearId { $tenantContext = new TenantContext();