elementRepository = new FakeElementRepository(); $this->fileUploader = new FakeFilesystem(); $this->deleteChildElement = new DeleteChildElement( $this->elementRepository, $this->fileUploader, ); } public function testDeletesChildSubtreeAndManagedFiles(): void { $parentElement = $this->createElement( parentElement: null, title: 'Parent', iconImageUrl: null, shortPdfPath: null, longPdfPath: null, ); $childElement = $this->createElement( parentElement: $parentElement, title: 'Child', iconImageUrl: 'element-icons/child.png', shortPdfPath: 'element-pdfs/short/child.pdf', longPdfPath: 'element-pdfs/long/child.pdf', ); $grandchildElement = $this->createElement( parentElement: $childElement, title: 'Grandchild', iconImageUrl: 'element-icons/grandchild.png', shortPdfPath: 'element-pdfs/short/grandchild.pdf', longPdfPath: 'element-pdfs/long/grandchild.pdf', ); $this->deleteChildElement->execute(new DeleteChildElementRequest( parentElementId: $parentElement->getId(), childElementId: $childElement->getId(), )); $this->assertNull( $this->elementRepository->find($childElement->getId()), ); $this->assertNull( $this->elementRepository->find($grandchildElement->getId()), ); $this->assertSame([ 'element-icons/grandchild.png', 'element-pdfs/short/grandchild.pdf', 'element-pdfs/long/grandchild.pdf', 'element-icons/child.png', 'element-pdfs/short/child.pdf', 'element-pdfs/long/child.pdf', ], $this->fileUploader->deletedPaths); } public function testThrowsWhenParentElementIsMissing(): void { $this->expectException(NotFoundException::class); $this->expectExceptionMessage('Parent element not found'); $this->deleteChildElement->execute(new DeleteChildElementRequest( parentElementId: 999, childElementId: 1, )); } public function testThrowsWhenChildElementIsMissing(): void { $parentElement = $this->createElement( parentElement: null, title: 'Parent', iconImageUrl: null, shortPdfPath: null, longPdfPath: null, ); $this->expectException(NotFoundException::class); $this->expectExceptionMessage('Child element not found'); $this->deleteChildElement->execute(new DeleteChildElementRequest( parentElementId: $parentElement->getId(), childElementId: 999, )); } public function testThrowsWhenChildIsNotDirectChild(): void { $parentElement = $this->createElement( parentElement: null, title: 'Parent', iconImageUrl: null, shortPdfPath: null, longPdfPath: null, ); $childElement = $this->createElement( parentElement: $parentElement, title: 'Child', iconImageUrl: null, shortPdfPath: null, longPdfPath: null, ); $grandchildElement = $this->createElement( parentElement: $childElement, title: 'Grandchild', iconImageUrl: null, shortPdfPath: null, longPdfPath: null, ); $this->expectException(BadRequestException::class); $this->expectExceptionMessage( 'Child element does not belong to parent' ); $this->deleteChildElement->execute(new DeleteChildElementRequest( parentElementId: $parentElement->getId(), childElementId: $grandchildElement->getId(), )); } private function createElement( ?Element $parentElement, string $title, ?string $iconImageUrl, ?string $shortPdfPath, ?string $longPdfPath, ): Element { return $this->elementRepository->create(new CreateElementDto( set: $this->createSet(), title: $title, description: "$title description", iconImageUrl: $iconImageUrl, richText: '', shortPdfPath: $shortPdfPath, longPdfPath: $longPdfPath, youtubeUrl: null, parentElement: $parentElement, )); } private function createSet(): DomainSet { return new DomainSet( id: 1, name: 'Baderech', description: 'Baderech description', iconImageUrl: '/assets/baderech-icon.png', ); } }