test child element ordering

This commit is contained in:
Yisroel Baum 2026-06-22 09:42:11 +03:00
parent 14de828b4b
commit 7323925319
Signed by: yisroelbaum
GPG key ID: 0FA60884F75520A9
3 changed files with 505 additions and 0 deletions

View file

@ -297,6 +297,234 @@ class ElementsEndpointTest extends TestCase
);
}
public function testReorderChildElementsRequiresAuthentication(): void
{
$setRepository = app(SetRepository::class);
$elementRepository = app(ElementRepository::class);
$set = $this->createSet($setRepository);
$parentElement = $this->createElement(
$elementRepository,
$set,
'Baderech HaAvodah',
'A structured path for growth',
null,
'<p>A structured path for growth</p>',
null,
null,
null,
null,
);
$firstChildElement = $this->createElement(
$elementRepository,
$set,
'First child',
'First child description',
null,
'',
null,
null,
null,
$parentElement,
);
$response = $this->putJson(
"/api/elements/{$parentElement->getId()}/children/order",
[
'childElementIds' => [$firstChildElement->getId()],
],
);
$response->assertUnauthorized();
$response->assertExactJson([
'error' => 'unauthenticated',
]);
}
public function testAuthenticatedReorderChildElementsPersistsOrder(): void
{
$setRepository = app(SetRepository::class);
$elementRepository = app(ElementRepository::class);
$set = $this->createSet($setRepository);
$parentElement = $this->createElement(
$elementRepository,
$set,
'Baderech HaAvodah',
'A structured path for growth',
null,
'<p>A structured path for growth</p>',
null,
null,
null,
null,
);
$firstChildElement = $this->createElement(
$elementRepository,
$set,
'First child',
'First child description',
null,
'',
null,
null,
null,
$parentElement,
);
$secondChildElement = $this->createElement(
$elementRepository,
$set,
'Second child',
'Second child description',
null,
'',
null,
null,
null,
$parentElement,
);
$thirdChildElement = $this->createElement(
$elementRepository,
$set,
'Third child',
'Third child description',
null,
'',
null,
null,
null,
$parentElement,
);
$this->createSession('valid-token');
$response = $this->withCredentials()
->withUnencryptedCookie('auth_token', 'valid-token')
->putJson(
"/api/elements/{$parentElement->getId()}/children/order",
[
'childElementIds' => [
$thirdChildElement->getId(),
$firstChildElement->getId(),
$secondChildElement->getId(),
],
],
);
$response->assertOk();
$response->assertExactJson([
'childElements' => [
[
'id' => $thirdChildElement->getId(),
'title' => 'Third child',
'description' => 'Third child description',
],
[
'id' => $firstChildElement->getId(),
'title' => 'First child',
'description' => 'First child description',
],
[
'id' => $secondChildElement->getId(),
'title' => 'Second child',
'description' => 'Second child description',
],
],
]);
$this->getJson("/api/elements/{$parentElement->getId()}")
->assertJsonPath('childElements.0.id', $thirdChildElement->getId())
->assertJsonPath('childElements.1.id', $firstChildElement->getId())
->assertJsonPath(
'childElements.2.id',
$secondChildElement->getId(),
);
$this->getJson("/api/elements/{$thirdChildElement->getId()}")
->assertJsonPath(
'siblingElements.0.id',
$thirdChildElement->getId(),
)
->assertJsonPath(
'siblingElements.1.id',
$firstChildElement->getId(),
)
->assertJsonPath(
'siblingElements.2.id',
$secondChildElement->getId(),
);
}
public function testAuthenticatedReorderRejectsNestedChildElement(): void
{
$setRepository = app(SetRepository::class);
$elementRepository = app(ElementRepository::class);
$set = $this->createSet($setRepository);
$parentElement = $this->createElement(
$elementRepository,
$set,
'Baderech HaAvodah',
'A structured path for growth',
null,
'<p>A structured path for growth</p>',
null,
null,
null,
null,
);
$firstChildElement = $this->createElement(
$elementRepository,
$set,
'First child',
'First child description',
null,
'',
null,
null,
null,
$parentElement,
);
$secondChildElement = $this->createElement(
$elementRepository,
$set,
'Second child',
'Second child description',
null,
'',
null,
null,
null,
$parentElement,
);
$nestedChildElement = $this->createElement(
$elementRepository,
$set,
'Nested child',
'Nested child description',
null,
'',
null,
null,
null,
$firstChildElement,
);
$this->createSession('valid-token');
$response = $this->withCredentials()
->withUnencryptedCookie('auth_token', 'valid-token')
->putJson(
"/api/elements/{$parentElement->getId()}/children/order",
[
'childElementIds' => [
$firstChildElement->getId(),
$nestedChildElement->getId(),
$secondChildElement->getId(),
],
],
);
$response->assertBadRequest();
$response->assertExactJson([
'error' => 'Child order contains invalid child',
]);
}
public function testDeleteChildRequiresAuthentication(): void
{
$setRepository = app(SetRepository::class);

View file

@ -0,0 +1,237 @@
<?php
namespace Tests\Unit\Element\UseCases;
use App\Element\CreateElementDto;
use App\Element\Element;
use App\Element\UseCases\ReorderChildElements\ReorderChildElements;
use App\Element\UseCases\ReorderChildElements\ReorderChildElementsRequest;
use App\Exceptions\BadRequestException;
use App\Exceptions\NotFoundException;
use App\Set\Set as DomainSet;
use Tests\Fakes\FakeElementRepository;
use Tests\TestCase;
class ReorderChildElementsTest extends TestCase
{
private FakeElementRepository $elementRepository;
private ReorderChildElements $reorderChildElements;
protected function setUp(): void
{
$this->elementRepository = new FakeElementRepository();
$this->reorderChildElements = new ReorderChildElements(
$this->elementRepository,
);
}
public function testReordersDirectChildElements(): void
{
$set = $this->createSet(1, 'Baderech');
$parentElement = $this->createElement($set, 'Parent', null);
$firstChildElement = $this->createElement(
$set,
'First child',
$parentElement,
);
$secondChildElement = $this->createElement(
$set,
'Second child',
$parentElement,
);
$thirdChildElement = $this->createElement(
$set,
'Third child',
$parentElement,
);
$this->createElement($set, 'Nested child', $firstChildElement);
$childElements = $this->reorderChildElements->execute(
new ReorderChildElementsRequest(
parentElementId: $parentElement->getId(),
childElementIds: [
$thirdChildElement->getId(),
$firstChildElement->getId(),
$secondChildElement->getId(),
],
)
);
$this->assertSame(
[
$thirdChildElement->getId(),
$firstChildElement->getId(),
$secondChildElement->getId(),
],
$this->elementIds($childElements),
);
$this->assertSame(
[
$thirdChildElement->getId(),
$firstChildElement->getId(),
$secondChildElement->getId(),
],
$this->elementIds(
$this->elementRepository->findByParentElement($parentElement),
),
);
}
public function testThrowsWhenParentElementIsMissing(): void
{
$this->expectException(NotFoundException::class);
$this->expectExceptionMessage('Parent element not found');
$this->reorderChildElements->execute(
new ReorderChildElementsRequest(
parentElementId: 999,
childElementIds: [1, 2],
)
);
}
public function testThrowsWhenChildIdsAreMissing(): void
{
$this->expectException(BadRequestException::class);
$this->expectExceptionMessage('childElementIds is required');
$this->reorderChildElements->execute(
new ReorderChildElementsRequest(
parentElementId: 1,
childElementIds: null,
)
);
}
public function testThrowsWhenChildOrderHasDuplicates(): void
{
$set = $this->createSet(1, 'Baderech');
$parentElement = $this->createElement($set, 'Parent', null);
$firstChildElement = $this->createElement(
$set,
'First child',
$parentElement,
);
$secondChildElement = $this->createElement(
$set,
'Second child',
$parentElement,
);
$this->expectException(BadRequestException::class);
$this->expectExceptionMessage('Child order contains duplicate ids');
$this->reorderChildElements->execute(
new ReorderChildElementsRequest(
parentElementId: $parentElement->getId(),
childElementIds: [
$firstChildElement->getId(),
$firstChildElement->getId(),
$secondChildElement->getId(),
],
)
);
}
public function testThrowsWhenChildOrderHasInvalidElement(): void
{
$set = $this->createSet(1, 'Baderech');
$parentElement = $this->createElement($set, 'Parent', null);
$firstChildElement = $this->createElement(
$set,
'First child',
$parentElement,
);
$secondChildElement = $this->createElement(
$set,
'Second child',
$parentElement,
);
$nestedChildElement = $this->createElement(
$set,
'Nested child',
$firstChildElement,
);
$this->expectException(BadRequestException::class);
$this->expectExceptionMessage('Child order contains invalid child');
$this->reorderChildElements->execute(
new ReorderChildElementsRequest(
parentElementId: $parentElement->getId(),
childElementIds: [
$firstChildElement->getId(),
$nestedChildElement->getId(),
$secondChildElement->getId(),
],
)
);
}
public function testThrowsWhenChildOrderOmitsDirectChild(): void
{
$set = $this->createSet(1, 'Baderech');
$parentElement = $this->createElement($set, 'Parent', null);
$firstChildElement = $this->createElement(
$set,
'First child',
$parentElement,
);
$this->createElement($set, 'Second child', $parentElement);
$this->expectException(BadRequestException::class);
$this->expectExceptionMessage(
'Child order must include every direct child',
);
$this->reorderChildElements->execute(
new ReorderChildElementsRequest(
parentElementId: $parentElement->getId(),
childElementIds: [$firstChildElement->getId()],
)
);
}
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,
?Element $parentElement,
): Element {
return $this->elementRepository->create(new CreateElementDto(
set: $set,
title: $title,
description: "$title description",
iconImageUrl: null,
richText: '',
shortPdfPath: null,
longPdfPath: null,
youtubeUrl: null,
parentElement: $parentElement,
));
}
/**
* @param Element[] $elements
* @return int[]
*/
private function elementIds(array $elements): array
{
$elementIds = [];
foreach ($elements as $element) {
$elementIds[] = $element->getId();
}
return $elementIds;
}
}