Goal-Calibration/tests/e2e/Controllers/NodeControllerTest.php
Yisroel Baum e56cb56ce7
test node controller ownership checks
add failing tests asserting 403 when a non-owner tries to
read or write nodes on another user's text, plus admin
bypass. existing tests now attach a session user to mirror
the new controller signature.
2026-05-02 21:45:15 +03:00

296 lines
8.5 KiB
PHP

<?php
namespace Tests\e2e\Controllers;
use App\Node\CreateNodeDto;
use App\Node\NodeController;
use App\Node\UseCases\CreateNode;
use App\Text\CreateTextDto;
use App\User\UseCases\CreateUserDto;
use App\User\User;
use App\ValueObjects\EmailAddress;
use PHPUnit\Framework\TestCase;
use Psr\Http\Message\ServerRequestInterface;
use Slim\Psr7\Factory\ServerRequestFactory;
use Slim\Psr7\Factory\StreamFactory;
use Slim\Psr7\Response;
use Tests\Fakes\FakeNodeRepository;
use Tests\Fakes\FakeTextRepository;
use Tests\Fakes\FakeUserRepository;
class NodeControllerTest extends TestCase
{
private FakeTextRepository $textRepo;
private FakeNodeRepository $nodeRepo;
private NodeController $controller;
private User $user;
private User $otherUser;
private User $admin;
public function setUp(): void
{
$userRepo = new FakeUserRepository();
$this->user = $userRepo->create(new CreateUserDto(
email: new EmailAddress('a@b.com'),
passwordHash: '',
isAdmin: false,
));
$this->otherUser = $userRepo->create(new CreateUserDto(
email: new EmailAddress('other@b.com'),
passwordHash: '',
isAdmin: false,
));
$this->admin = $userRepo->create(new CreateUserDto(
email: new EmailAddress('admin@b.com'),
passwordHash: '',
isAdmin: true,
));
$this->textRepo = new FakeTextRepository();
$this->textRepo->create(new CreateTextDto(
name: 'test text',
user: $this->user,
));
$this->nodeRepo = new FakeNodeRepository();
$this->controller = new NodeController(
$this->nodeRepo,
$this->textRepo,
);
}
private function makeRequest(
array $body,
?User $user,
): ServerRequestInterface {
$stream = new StreamFactory()->createStream(json_encode($body));
$request = new ServerRequestFactory()
->createServerRequest('POST', 'http://localhost/api/nodes')
->withHeader('Content-Type', 'application/json')
->withBody($stream);
if ($user !== null) {
$request = $request->withAttribute('user', $user);
}
return $request;
}
private function makeGetRequest(?User $user): ServerRequestInterface
{
$request = new ServerRequestFactory()
->createServerRequest('GET', 'http://localhost/api/nodes/0');
if ($user !== null) {
$request = $request->withAttribute('user', $user);
}
return $request;
}
public function test_get_nodes_of_text_returns_flat_array(): void
{
$text = $this->textRepo->find(0);
$this->nodeRepo->create(new CreateNodeDto(
text: $text,
title: 'Root Node',
parentNode: null,
));
$response = $this->controller->getNodesOfText(
$this->makeGetRequest($this->user),
new Response(),
0,
);
$this->assertEquals(200, $response->getStatusCode());
$this->assertEquals(
json_encode([
['id' => 0, 'title' => 'Root Node', 'parentNodeId' => null],
]),
$response->getBody(),
);
}
public function test_get_nodes_of_text_returns_empty_array_when_no_nodes(): void
{
$response = $this->controller->getNodesOfText(
$this->makeGetRequest($this->user),
new Response(),
0,
);
$this->assertEquals(200, $response->getStatusCode());
$this->assertEquals(json_encode([]), $response->getBody());
}
public function test_get_nodes_of_text_returns_404_for_unknown_text(): void
{
$response = $this->controller->getNodesOfText(
$this->makeGetRequest($this->user),
new Response(),
99,
);
$this->assertEquals(404, $response->getStatusCode());
}
public function test_get_nodes_of_text_returns_403_when_not_owner(): void
{
$response = $this->controller->getNodesOfText(
$this->makeGetRequest($this->otherUser),
new Response(),
0,
);
$this->assertEquals(403, $response->getStatusCode());
}
public function test_get_nodes_of_text_allows_admin(): void
{
$text = $this->textRepo->find(0);
$this->nodeRepo->create(new CreateNodeDto(
text: $text,
title: 'Root Node',
parentNode: null,
));
$response = $this->controller->getNodesOfText(
$this->makeGetRequest($this->admin),
new Response(),
0,
);
$this->assertEquals(200, $response->getStatusCode());
}
public function test_get_nodes_includes_parent_node_id(): void
{
$text = $this->textRepo->find(0);
$rootNode = $this->nodeRepo->create(new CreateNodeDto(
text: $text,
title: 'Root Node',
parentNode: null,
));
$this->nodeRepo->create(new CreateNodeDto(
text: $text,
title: 'Child Node',
parentNode: $rootNode,
));
$response = $this->controller->getNodesOfText(
$this->makeGetRequest($this->user),
new Response(),
0,
);
$body = json_decode($response->getBody(), true);
$this->assertEquals(0, $body[1]['parentNodeId']);
}
public function test_create_node_returns_created_node(): void
{
$text = $this->textRepo->find(0);
$rootNode = $this->nodeRepo->create(new CreateNodeDto(
text: $text,
title: 'Root Node',
parentNode: null,
));
$request = $this->makeRequest(
[
'textId' => 0,
'title' => 'Child Node',
'parentNodeId' => $rootNode->getId(),
],
$this->user,
);
$response = $this->controller->createNode(
$request,
new Response(),
new CreateNode($this->nodeRepo, $this->textRepo),
);
$this->assertEquals(201, $response->getStatusCode());
$body = json_decode($response->getBody(), true);
$this->assertEquals('Child Node', $body['title']);
$this->assertEquals($rootNode->getId(), $body['parentNodeId']);
$this->assertArrayHasKey('id', $body);
}
public function test_create_node_returns_400_when_title_missing(): void
{
$request = $this->makeRequest(
[
'textId' => 0,
'parentNodeId' => null,
],
$this->user,
);
$response = $this->controller->createNode(
$request,
new Response(),
new CreateNode($this->nodeRepo, $this->textRepo),
);
$this->assertEquals(400, $response->getStatusCode());
}
public function test_create_node_returns_404_when_text_not_found(): void
{
$request = $this->makeRequest(
[
'textId' => 99,
'title' => 'Some Node',
'parentNodeId' => null,
],
$this->user,
);
$response = $this->controller->createNode(
$request,
new Response(),
new CreateNode($this->nodeRepo, $this->textRepo),
);
$this->assertEquals(404, $response->getStatusCode());
}
public function test_create_node_returns_403_when_not_owner(): void
{
$request = $this->makeRequest(
[
'textId' => 0,
'title' => 'Hijack',
'parentNodeId' => null,
],
$this->otherUser,
);
$response = $this->controller->createNode(
$request,
new Response(),
new CreateNode($this->nodeRepo, $this->textRepo),
);
$this->assertEquals(403, $response->getStatusCode());
}
public function test_create_node_allows_admin_on_any_text(): void
{
$request = $this->makeRequest(
[
'textId' => 0,
'title' => 'Admin Root',
'parentNodeId' => null,
],
$this->admin,
);
$response = $this->controller->createNode(
$request,
new Response(),
new CreateNode($this->nodeRepo, $this->textRepo),
);
$this->assertEquals(201, $response->getStatusCode());
}
}