brandingRepository = new InMemorySchoolBrandingRepository(); $this->logoStorage = new InMemoryLogoStorage(); $this->clock = new class implements Clock { public function now(): DateTimeImmutable { return new DateTimeImmutable('2026-02-20 10:00:00'); } }; } #[Test] public function itDeletesLogoFromBranding(): void { $this->seedBrandingWithLogo(); $handler = $this->createHandler(); $command = new DeleteLogoCommand( tenantId: self::TENANT_ID, schoolId: self::SCHOOL_ID, ); $branding = $handler($command); self::assertNull($branding->logoUrl); self::assertNull($branding->logoUpdatedAt); } #[Test] public function itDeletesFileFromStorage(): void { $this->seedBrandingWithLogo(); self::assertSame(1, $this->logoStorage->count()); $handler = $this->createHandler(); $command = new DeleteLogoCommand( tenantId: self::TENANT_ID, schoolId: self::SCHOOL_ID, ); $handler($command); self::assertSame(0, $this->logoStorage->count()); } #[Test] public function itRecordsDomainEventOnDeletion(): void { $this->seedBrandingWithLogo(); $handler = $this->createHandler(); $command = new DeleteLogoCommand( tenantId: self::TENANT_ID, schoolId: self::SCHOOL_ID, ); $branding = $handler($command); $events = $branding->pullDomainEvents(); self::assertCount(1, $events); self::assertInstanceOf(BrandingModifie::class, $events[0]); } #[Test] public function itPersistsBrandingAfterDeletion(): void { $this->seedBrandingWithLogo(); $handler = $this->createHandler(); $command = new DeleteLogoCommand( tenantId: self::TENANT_ID, schoolId: self::SCHOOL_ID, ); $handler($command); $persisted = $this->brandingRepository->findBySchoolId( SchoolId::fromString(self::SCHOOL_ID), TenantId::fromString(self::TENANT_ID), ); self::assertNotNull($persisted); self::assertNull($persisted->logoUrl); } #[Test] public function itDoesNothingWhenNoLogoExists(): void { $this->seedBrandingWithoutLogo(); $handler = $this->createHandler(); $command = new DeleteLogoCommand( tenantId: self::TENANT_ID, schoolId: self::SCHOOL_ID, ); $branding = $handler($command); $events = $branding->pullDomainEvents(); self::assertCount(0, $events); } #[Test] public function itThrowsWhenBrandingDoesNotExist(): void { $handler = $this->createHandler(); $command = new DeleteLogoCommand( tenantId: self::TENANT_ID, schoolId: self::SCHOOL_ID, ); $this->expectException(SchoolBrandingNotFoundException::class); $handler($command); } private function createHandler(): DeleteLogoHandler { return new DeleteLogoHandler( $this->brandingRepository, new LogoUploader($this->logoStorage, new InMemoryImageProcessor()), $this->clock, ); } private function seedBrandingWithLogo(): void { // Store a file first so we can verify deletion $this->logoStorage->store('fake-content', 'logos/tenant/test.png', 'image/png'); $branding = SchoolBranding::creer( schoolId: SchoolId::fromString(self::SCHOOL_ID), tenantId: TenantId::fromString(self::TENANT_ID), createdAt: new DateTimeImmutable('2026-02-01 10:00:00'), ); $branding->changerLogo( new LogoUrl('https://storage.example.com/logos/tenant/test.png'), new DateTimeImmutable('2026-02-01 10:00:00'), ); $branding->pullDomainEvents(); $this->brandingRepository->save($branding); } private function seedBrandingWithoutLogo(): void { $branding = SchoolBranding::creer( schoolId: SchoolId::fromString(self::SCHOOL_ID), tenantId: TenantId::fromString(self::TENANT_ID), createdAt: new DateTimeImmutable('2026-02-01 10:00:00'), ); $this->brandingRepository->save($branding); } }