diff --git a/backend/tests/Feature/SetsEndpointTest.php b/backend/tests/Feature/SetsEndpointTest.php index 8f4af23..54a730a 100644 --- a/backend/tests/Feature/SetsEndpointTest.php +++ b/backend/tests/Feature/SetsEndpointTest.php @@ -140,6 +140,88 @@ class SetsEndpointTest extends TestCase ); } + public function testUpdateSetRequiresAuthentication(): void + { + $setRepository = app(SetRepository::class); + $set = $setRepository->create(new CreateSetDto( + name: 'Original Set', + description: 'Original set description', + iconImageUrl: '/assets/original-icon.png', + )); + + $response = $this->postJson( + "/api/sets/{$set->getId()}/update", + [ + 'name' => 'Updated Set', + 'description' => 'Updated set description', + ], + ); + + $response->assertUnauthorized(); + $response->assertExactJson([ + 'error' => 'unauthenticated', + ]); + } + + public function testAuthenticatedUpdateSetUpdatesMediaPayloadOnly(): void + { + $setRepository = app(SetRepository::class); + $elementRepository = app(ElementRepository::class); + $this->createSession('valid-token'); + $set = $setRepository->create(new CreateSetDto( + name: 'Original Set', + description: 'Original set description', + iconImageUrl: '/assets/original-icon.png', + )); + $rootElement = $elementRepository->create(new CreateElementDto( + set: $set, + title: 'Original Root', + description: 'Original root description', + iconImageUrl: '/assets/original-icon.png', + richText: '', + shortPdfPath: null, + longPdfPath: null, + youtubeUrl: null, + parentElement: null, + )); + + $response = $this->withCredentials() + ->withUnencryptedCookie('auth_token', 'valid-token') + ->postJson("/api/sets/{$set->getId()}/update", [ + 'name' => 'Updated Set', + 'description' => 'Updated set description', + ]); + + $response->assertOk(); + $response->assertExactJson([ + 'set' => [ + 'id' => $set->getId(), + 'name' => 'Updated Set', + 'description' => 'Updated set description', + 'iconImageUrl' => '/assets/original-icon.png', + 'rootElementId' => $rootElement->getId(), + ], + ]); + + $updatedSet = $setRepository->find($set->getId()); + $this->assertNotNull($updatedSet); + $this->assertSame('Updated Set', $updatedSet->getName()); + $this->assertSame( + 'Updated set description', + $updatedSet->getDescription(), + ); + + $unchangedRootElement = $elementRepository->find( + $rootElement->getId(), + ); + $this->assertNotNull($unchangedRootElement); + $this->assertSame('Original Root', $unchangedRootElement->getTitle()); + $this->assertSame( + 'Original root description', + $unchangedRootElement->getDescription(), + ); + } + public function testCreateSetRequiresIconImage(): void { $this->createSession('valid-token'); diff --git a/backend/tests/Unit/Set/UseCases/UpdateSetTest.php b/backend/tests/Unit/Set/UseCases/UpdateSetTest.php new file mode 100644 index 0000000..a1bba41 --- /dev/null +++ b/backend/tests/Unit/Set/UseCases/UpdateSetTest.php @@ -0,0 +1,181 @@ +setRepository = new FakeSetRepository(); + $this->elementRepository = new FakeElementRepository(); + $this->filesystem = new FakeFilesystem(); + $this->updateSet = new UpdateSet( + $this->setRepository, + $this->filesystem, + ); + } + + public function testUpdatesSetWithoutChangingRootElement(): void + { + $set = $this->setRepository->create(new CreateSetDto( + name: 'Original Set', + description: 'Original set description', + iconImageUrl: 'set-icons/original.png', + )); + $rootElement = $this->elementRepository->create(new CreateElementDto( + set: $set, + title: 'Original Root', + description: 'Original root description', + iconImageUrl: 'set-icons/original.png', + richText: '', + shortPdfPath: null, + longPdfPath: null, + youtubeUrl: null, + parentElement: null, + )); + + $updatedSet = $this->updateSet->execute(new UpdateSetRequest( + id: $set->getId(), + name: 'Updated Set', + description: 'Updated set description', + iconImageContents: null, + iconImageOriginalName: null, + iconImageMimeType: null, + iconImageSizeBytes: null, + )); + + $this->assertSame('Updated Set', $updatedSet->getName()); + $this->assertSame( + 'Updated set description', + $updatedSet->getDescription(), + ); + $this->assertSame( + 'set-icons/original.png', + $updatedSet->getIconImageUrl(), + ); + + $unchangedRootElement = $this->elementRepository->find( + $rootElement->getId(), + ); + $this->assertNotNull($unchangedRootElement); + $this->assertSame('Original Root', $unchangedRootElement->getTitle()); + $this->assertSame( + 'Original root description', + $unchangedRootElement->getDescription(), + ); + $this->assertSame( + 'set-icons/original.png', + $unchangedRootElement->getIconImageUrl(), + ); + } + + public function testReplacesSetIconAndDeletesOldManagedIcon(): void + { + $set = $this->setRepository->create(new CreateSetDto( + name: 'Original Set', + description: 'Original set description', + iconImageUrl: 'set-icons/original.png', + )); + + $updatedSet = $this->updateSet->execute(new UpdateSetRequest( + id: $set->getId(), + name: 'Updated Set', + description: 'Updated set description', + iconImageContents: 'image contents', + iconImageOriginalName: 'updated.png', + iconImageMimeType: 'image/png', + iconImageSizeBytes: 13, + )); + + $this->assertSame( + 'set-icons/fake-updated.png', + $updatedSet->getIconImageUrl(), + ); + $this->assertCount(1, $this->filesystem->uploads); + $this->assertSame('set-icons', $this->filesystem->uploads[0]['folder']); + $this->assertSame([ + 'set-icons/original.png', + ], $this->filesystem->deletedPaths); + } + + public function testRequiresName(): void + { + $set = $this->createSet(); + + $this->expectException(BadRequestException::class); + $this->expectExceptionMessage('name is required'); + + $this->updateSet->execute(new UpdateSetRequest( + id: $set->getId(), + name: '', + description: 'Updated set description', + iconImageContents: null, + iconImageOriginalName: null, + iconImageMimeType: null, + iconImageSizeBytes: null, + )); + } + + public function testRequiresDescription(): void + { + $set = $this->createSet(); + + $this->expectException(BadRequestException::class); + $this->expectExceptionMessage('description is required'); + + $this->updateSet->execute(new UpdateSetRequest( + id: $set->getId(), + name: 'Updated Set', + description: '', + iconImageContents: null, + iconImageOriginalName: null, + iconImageMimeType: null, + iconImageSizeBytes: null, + )); + } + + public function testThrowsWhenSetIsMissing(): void + { + $this->expectException(NotFoundException::class); + $this->expectExceptionMessage('Set not found'); + + $this->updateSet->execute(new UpdateSetRequest( + id: 999, + name: 'Updated Set', + description: 'Updated set description', + iconImageContents: null, + iconImageOriginalName: null, + iconImageMimeType: null, + iconImageSizeBytes: null, + )); + } + + private function createSet(): DomainSet + { + return $this->setRepository->create(new CreateSetDto( + name: 'Original Set', + description: 'Original set description', + iconImageUrl: 'set-icons/original.png', + )); + } +}