From 6d0acaae56aa5fd332a4b441fdf6fb46fd10b940 Mon Sep 17 00:00:00 2001 From: Yisroel Baum Date: Thu, 28 May 2026 20:05:19 +0300 Subject: [PATCH] test element updates --- .../tests/Feature/ElementsEndpointTest.php | 118 +++++++++++ .../Element/UseCases/UpdateElementTest.php | 191 ++++++++++++++++++ 2 files changed, 309 insertions(+) create mode 100644 backend/tests/Unit/Element/UseCases/UpdateElementTest.php diff --git a/backend/tests/Feature/ElementsEndpointTest.php b/backend/tests/Feature/ElementsEndpointTest.php index 33725c8..251fddf 100644 --- a/backend/tests/Feature/ElementsEndpointTest.php +++ b/backend/tests/Feature/ElementsEndpointTest.php @@ -2,10 +2,17 @@ namespace Tests\Feature; +use App\Auth\CreateSessionDto; +use App\Auth\SessionRepository; use App\Element\CreateElementDto; use App\Element\ElementRepository; use App\Set\CreateSetDto; use App\Set\SetRepository; +use App\Shared\ValueObject\EmailAddress; +use App\User\CreateUserDto; +use App\User\UserRepository; +use DateTimeImmutable; +use DateTimeZone; use Illuminate\Foundation\Testing\RefreshDatabase; use Tests\TestCase; @@ -92,4 +99,115 @@ class ElementsEndpointTest extends TestCase 'error' => 'Element not found', ]); } + + public function testUpdateRequiresAuthentication(): void + { + $setRepository = app(SetRepository::class); + $elementRepository = app(ElementRepository::class); + $set = $setRepository->create(new CreateSetDto( + name: 'Baderech HaAvodah', + description: 'A structured path for growth', + iconImageUrl: '/assets/baderech-haavodah-icon.png', + )); + $element = $elementRepository->create(new CreateElementDto( + set: $set, + title: 'Baderech HaAvodah', + description: 'A structured path for growth', + iconImageUrl: '/assets/baderech-haavodah-icon.png', + richText: '

A structured path for growth

', + pdfPath: '/assets/pdfs/baderech.pdf', + youtubeUrl: null, + parentElement: null, + )); + + $response = $this->patchJson("/api/elements/{$element->getId()}", [ + 'title' => 'Updated title', + 'description' => 'Updated description', + 'iconImageUrl' => '/assets/updated-icon.png', + 'richText' => '

Updated rich text

', + 'pdfPath' => '/assets/pdfs/updated.pdf', + 'youtubeUrl' => 'https://www.youtube.com/watch?v=dQw4w9WgXcQ', + ]); + + $response->assertUnauthorized(); + $response->assertExactJson([ + 'error' => 'unauthenticated', + ]); + } + + public function testAuthenticatedUpdateReturnsElementPayload(): void + { + $sampleYoutubeUrl = 'https://www.youtube.com/watch?v=' + . 'dQw4w9WgXcQ'; + $setRepository = app(SetRepository::class); + $elementRepository = app(ElementRepository::class); + $set = $setRepository->create(new CreateSetDto( + name: 'Baderech HaAvodah', + description: 'A structured path for growth', + iconImageUrl: '/assets/baderech-haavodah-icon.png', + )); + $element = $elementRepository->create(new CreateElementDto( + set: $set, + title: 'Baderech HaAvodah', + description: 'A structured path for growth', + iconImageUrl: '/assets/baderech-haavodah-icon.png', + richText: '

A structured path for growth

', + pdfPath: '/assets/pdfs/baderech.pdf', + youtubeUrl: null, + parentElement: null, + )); + $this->createSession('valid-token'); + + $response = $this->withCookie('auth_token', 'valid-token') + ->patchJson("/api/elements/{$element->getId()}", [ + 'title' => 'Updated title', + 'description' => 'Updated description', + 'iconImageUrl' => '/assets/updated-icon.png', + 'richText' => '

Updated rich text

', + 'pdfPath' => '/assets/pdfs/updated.pdf', + 'youtubeUrl' => $sampleYoutubeUrl, + ]); + + $response->assertOk(); + $response->assertExactJson([ + 'element' => [ + 'id' => $element->getId(), + 'title' => 'Updated title', + 'description' => 'Updated description', + 'iconImageUrl' => '/assets/updated-icon.png', + 'richText' => '

Updated rich text

', + 'pdfPath' => '/assets/pdfs/updated.pdf', + 'youtubeUrl' => $sampleYoutubeUrl, + ], + ]); + $updatedElement = $elementRepository->find($element->getId()); + $this->assertSame('Updated title', $updatedElement->getTitle()); + $this->assertSame( + '

Updated rich text

', + $updatedElement->getRichText(), + ); + } + + private function createSession(string $token): void + { + $userRepository = app(UserRepository::class); + $sessionRepository = app(SessionRepository::class); + $user = $userRepository->create(new CreateUserDto( + email: new EmailAddress('admin@example.com'), + passwordHash: 'password-hash', + )); + $now = new DateTimeImmutable( + '2026-05-01T00:00:00', + new DateTimeZone('UTC'), + ); + $sessionRepository->create(new CreateSessionDto( + token: $token, + user: $user, + createdAt: $now, + expiresAt: new DateTimeImmutable( + '2099-01-01T00:00:00', + new DateTimeZone('UTC'), + ), + )); + } } diff --git a/backend/tests/Unit/Element/UseCases/UpdateElementTest.php b/backend/tests/Unit/Element/UseCases/UpdateElementTest.php new file mode 100644 index 0000000..0bb1195 --- /dev/null +++ b/backend/tests/Unit/Element/UseCases/UpdateElementTest.php @@ -0,0 +1,191 @@ +elementRepo = new FakeElementRepository(); + $this->updateElement = new UpdateElement($this->elementRepo); + } + + public function testUpdatesContentFields(): void + { + $set = $this->createSet(1, 'Baderech'); + $element = $this->createElement( + $set, + 'Original title', + 'Original description', + '/assets/original-icon.png', + '

Original rich text

', + '/assets/pdfs/original.pdf', + 'https://www.youtube.com/watch?v=yHx-r4p6hHU&t=1s', + null, + ); + + $updatedElement = $this->updateElement->execute( + new UpdateElementRequest( + id: $element->getId(), + title: 'Updated title', + description: 'Updated description', + iconImageUrl: '/assets/updated-icon.png', + richText: '

Updated rich text

', + pdfPath: '/assets/pdfs/updated.pdf', + youtubeUrl: 'https://www.youtube.com/watch?v=dQw4w9WgXcQ', + ) + ); + + $this->assertSame($element->getId(), $updatedElement->getId()); + $this->assertSame('Updated title', $updatedElement->getTitle()); + $this->assertSame( + 'Updated description', + $updatedElement->getDescription(), + ); + $this->assertSame( + '/assets/updated-icon.png', + $updatedElement->getIconImageUrl(), + ); + $this->assertSame( + '

Updated rich text

', + $updatedElement->getRichText(), + ); + $this->assertSame( + '/assets/pdfs/updated.pdf', + $updatedElement->getPdfPath(), + ); + $this->assertSame( + 'https://www.youtube.com/watch?v=dQw4w9WgXcQ', + $updatedElement->getYoutubeUrl(), + ); + + $persistedElement = $this->elementRepo->find($element->getId()); + $this->assertInstanceOf(Element::class, $persistedElement); + $this->assertSame('Updated title', $persistedElement->getTitle()); + } + + public function testConvertsEmptyNullableFieldsToNull(): void + { + $set = $this->createSet(1, 'Baderech'); + $element = $this->createElement( + $set, + 'Original title', + 'Original description', + '/assets/original-icon.png', + '

Original rich text

', + '/assets/pdfs/original.pdf', + 'https://www.youtube.com/watch?v=yHx-r4p6hHU&t=1s', + null, + ); + + $updatedElement = $this->updateElement->execute( + new UpdateElementRequest( + id: $element->getId(), + title: 'Updated title', + description: 'Updated description', + iconImageUrl: '', + richText: '

Updated rich text

', + pdfPath: '', + youtubeUrl: '', + ) + ); + + $this->assertNull($updatedElement->getIconImageUrl()); + $this->assertNull($updatedElement->getPdfPath()); + $this->assertNull($updatedElement->getYoutubeUrl()); + } + + public function testThrowsWhenIdMissing(): void + { + $this->expectException(BadRequestException::class); + $this->expectExceptionMessage('id is required'); + + $this->updateElement->execute(new UpdateElementRequest( + id: null, + title: 'Updated title', + description: 'Updated description', + iconImageUrl: null, + richText: '

Updated rich text

', + pdfPath: null, + youtubeUrl: null, + )); + } + + public function testThrowsWhenTitleIsBlank(): void + { + $this->expectException(BadRequestException::class); + $this->expectExceptionMessage('title is required'); + + $this->updateElement->execute(new UpdateElementRequest( + id: 1, + title: '', + description: 'Updated description', + iconImageUrl: null, + richText: '

Updated rich text

', + pdfPath: null, + youtubeUrl: null, + )); + } + + public function testThrowsWhenElementDoesNotExist(): void + { + $this->expectException(NotFoundException::class); + $this->expectExceptionMessage('Element not found'); + + $this->updateElement->execute(new UpdateElementRequest( + id: 999, + title: 'Updated title', + description: 'Updated description', + iconImageUrl: null, + richText: '

Updated rich text

', + pdfPath: null, + youtubeUrl: null, + )); + } + + private function createSet(int $id, string $name): DomainSet + { + return new DomainSet( + id: $id, + name: $name, + description: "$name description", + iconImageUrl: '/assets/baderech-icon.png', + ); + } + + private function createElement( + DomainSet $set, + string $title, + string $description, + ?string $iconImageUrl, + string $richText, + ?string $pdfPath, + ?string $youtubeUrl, + ?Element $parentElement, + ): Element { + return $this->elementRepo->create(new CreateElementDto( + set: $set, + title: $title, + description: $description, + iconImageUrl: $iconImageUrl, + richText: $richText, + pdfPath: $pdfPath, + youtubeUrl: $youtubeUrl, + parentElement: $parentElement, + )); + } +}