Skip to content

Commit 5f68e76

Browse files
committed
ISSUE-345: add tests
1 parent fcc12db commit 5f68e76

14 files changed

+660
-44
lines changed

composer.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
},
3232
"require": {
3333
"php": "^8.1",
34-
"phplist/core": "dev-ISSUE-345",
34+
"phplist/core": "v5.0.0-alpha6",
3535
"friendsofsymfony/rest-bundle": "*",
3636
"symfony/test-pack": "^1.0",
3737
"symfony/process": "^6.4",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpList\RestBundle\Tests\Helpers;
6+
7+
use Doctrine\ORM\EntityRepository;
8+
use PhpList\Core\Domain\Filter\FilterRequestInterface;
9+
use PhpList\Core\Domain\Repository\Interfaces\PaginatableRepositoryInterface;
10+
11+
class DummyPaginatableRepository extends EntityRepository implements PaginatableRepositoryInterface
12+
{
13+
public function getFilteredAfterId(int $lastId, int $limit, ?FilterRequestInterface $filter = null): array
14+
{
15+
return [
16+
(object)['id' => 1, 'name' => 'Item 1'],
17+
(object)['id' => 2, 'name' => 'Item 2'],
18+
];
19+
}
20+
21+
public function count(array $criteria = []): int
22+
{
23+
return 10;
24+
}
25+
}

tests/Helpers/DummyRepository.php

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpList\RestBundle\Tests\Helpers;
6+
7+
use Doctrine\ORM\EntityRepository;
8+
9+
class DummyRepository extends EntityRepository
10+
{
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpList\RestBundle\Tests\Integration\EventListener;
6+
7+
use PhpList\RestBundle\EventListener\ExceptionListener;
8+
use PhpList\RestBundle\Exception\SubscriptionCreationException;
9+
use PHPUnit\Framework\TestCase;
10+
use Symfony\Component\HttpFoundation\JsonResponse;
11+
use Symfony\Component\HttpKernel\Event\ExceptionEvent;
12+
use Symfony\Component\HttpKernel\HttpKernelInterface;
13+
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
14+
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
15+
use Symfony\Component\HttpFoundation\Request;
16+
17+
class ExceptionListenerTest extends TestCase
18+
{
19+
private function createExceptionEvent(\Throwable $exception): ExceptionEvent
20+
{
21+
$kernel = $this->createMock(HttpKernelInterface::class);
22+
$request = new Request();
23+
return new ExceptionEvent($kernel, $request, HttpKernelInterface::MAIN_REQUEST, $exception);
24+
}
25+
26+
public function testAccessDeniedExceptionHandled(): void
27+
{
28+
$listener = new ExceptionListener();
29+
$event = $this->createExceptionEvent(new AccessDeniedHttpException('Forbidden'));
30+
31+
$listener->onKernelException($event);
32+
$response = $event->getResponse();
33+
34+
$this->assertInstanceOf(JsonResponse::class, $response);
35+
$this->assertEquals(403, $response->getStatusCode());
36+
$this->assertEquals(['message' => 'Forbidden'], json_decode($response->getContent(), true));
37+
}
38+
39+
public function testHttpExceptionHandled(): void
40+
{
41+
$listener = new ExceptionListener();
42+
$event = $this->createExceptionEvent(new NotFoundHttpException('Not found'));
43+
44+
$listener->onKernelException($event);
45+
$response = $event->getResponse();
46+
47+
$this->assertInstanceOf(JsonResponse::class, $response);
48+
$this->assertEquals(404, $response->getStatusCode());
49+
$this->assertEquals(['message' => 'Not found'], json_decode($response->getContent(), true));
50+
}
51+
52+
public function testSubscriptionCreationExceptionHandled(): void
53+
{
54+
$listener = new ExceptionListener();
55+
$exception = new SubscriptionCreationException('Subscription error', 409);
56+
$event = $this->createExceptionEvent($exception);
57+
58+
$listener->onKernelException($event);
59+
$response = $event->getResponse();
60+
61+
$this->assertInstanceOf(JsonResponse::class, $response);
62+
$this->assertEquals(409, $response->getStatusCode());
63+
$this->assertEquals(['message' => 'Subscription error'], json_decode($response->getContent(), true));
64+
}
65+
66+
public function testGenericExceptionHandled(): void
67+
{
68+
$listener = new ExceptionListener();
69+
$event = $this->createExceptionEvent(new \RuntimeException('Something went wrong'));
70+
71+
$listener->onKernelException($event);
72+
$response = $event->getResponse();
73+
74+
$this->assertInstanceOf(JsonResponse::class, $response);
75+
$this->assertEquals(500, $response->getStatusCode());
76+
$this->assertEquals(['message' => 'Something went wrong'], json_decode($response->getContent(), true));
77+
}
78+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<?php
2+
3+
namespace PhpList\RestBundle\Tests\Integration\EventListener;
4+
5+
use PhpList\RestBundle\EventListener\ResponseListener;
6+
use PHPUnit\Framework\TestCase;
7+
use Symfony\Component\HttpFoundation\JsonResponse;
8+
use Symfony\Component\HttpFoundation\Request;
9+
use Symfony\Component\HttpFoundation\Response;
10+
use Symfony\Component\HttpKernel\Event\ResponseEvent;
11+
use Symfony\Component\HttpKernel\HttpKernelInterface;
12+
13+
class ResponseListenerTest extends TestCase
14+
{
15+
public function testSecurityHeadersAreSetForJsonResponse(): void
16+
{
17+
$listener = new ResponseListener();
18+
19+
$kernel = $this->createMock(HttpKernelInterface::class);
20+
$request = new Request();
21+
$response = new JsonResponse(['data' => 'test']);
22+
23+
$event = new ResponseEvent($kernel, $request, HttpKernelInterface::MAIN_REQUEST, $response);
24+
$listener->onKernelResponse($event);
25+
26+
$this->assertEquals('nosniff', $response->headers->get('X-Content-Type-Options'));
27+
$this->assertEquals("default-src 'none'", $response->headers->get('Content-Security-Policy'));
28+
$this->assertEquals('DENY', $response->headers->get('X-Frame-Options'));
29+
}
30+
31+
public function testNonJsonResponseDoesNotGetHeaders(): void
32+
{
33+
$listener = new ResponseListener();
34+
35+
$kernel = $this->createMock(HttpKernelInterface::class);
36+
$request = new Request();
37+
$response = new Response('OK');
38+
39+
$event = new ResponseEvent($kernel, $request, HttpKernelInterface::MAIN_REQUEST, $response);
40+
$listener->onKernelResponse($event);
41+
42+
$this->assertFalse($response->headers->has('X-Content-Type-Options'));
43+
$this->assertFalse($response->headers->has('Content-Security-Policy'));
44+
$this->assertFalse($response->headers->has('X-Frame-Options'));
45+
}
46+
}

tests/System/Controller/SecuredViewHandlerTest.php renamed to tests/Integration/ViewHandler/SecuredViewHandlerTest.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
declare(strict_types=1);
44

5-
namespace PhpList\RestBundle\Tests\System\Controller;
5+
namespace PhpList\RestBundle\Tests\Integration\ViewHandler;
66

77
use PhpList\RestBundle\Tests\Integration\Controller\AbstractTestController;
88

tests/System/Controller/SessionControllerTest.php

-42
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpList\RestBundle\Tests\Unit\Serializer;
6+
7+
use DateTime;
8+
use InvalidArgumentException;
9+
use PhpList\Core\Domain\Model\Identity\Administrator;
10+
use PhpList\RestBundle\Serializer\AdministratorNormalizer;
11+
use PHPUnit\Framework\TestCase;
12+
13+
class AdministratorNormalizerTest extends TestCase
14+
{
15+
public function testNormalizeValidAdministrator(): void
16+
{
17+
$admin = $this->createMock(Administrator::class);
18+
$admin->method('getId')->willReturn(123);
19+
$admin->method('getLoginName')->willReturn('admin');
20+
$admin->method('getEmail')->willReturn('[email protected]');
21+
$admin->method('isSuperUser')->willReturn(true);
22+
$admin->method('getCreatedAt')->willReturn(new DateTime('2024-01-01T10:00:00+00:00'));
23+
24+
$normalizer = new AdministratorNormalizer();
25+
$data = $normalizer->normalize($admin);
26+
27+
$this->assertIsArray($data);
28+
$this->assertEquals([
29+
'id' => 123,
30+
'login_name' => 'admin',
31+
'email' => '[email protected]',
32+
'super_admin' => true,
33+
'created_at' => '2024-01-01T10:00:00+00:00',
34+
], $data);
35+
}
36+
37+
public function testNormalizeThrowsOnInvalidObject(): void
38+
{
39+
$normalizer = new AdministratorNormalizer();
40+
41+
$this->expectException(InvalidArgumentException::class);
42+
$this->expectExceptionMessage('Expected an Administrator object.');
43+
44+
$normalizer->normalize(new \stdClass());
45+
}
46+
47+
public function testSupportsNormalization(): void
48+
{
49+
$normalizer = new AdministratorNormalizer();
50+
51+
$admin = $this->createMock(Administrator::class);
52+
$this->assertTrue($normalizer->supportsNormalization($admin));
53+
$this->assertFalse($normalizer->supportsNormalization(new \stdClass()));
54+
}
55+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpList\RestBundle\Tests\Unit\Serializer;
6+
7+
use PhpList\RestBundle\Entity\Dto\CursorPaginationResult;
8+
use PhpList\RestBundle\Serializer\CursorPaginationNormalizer;
9+
use PHPUnit\Framework\TestCase;
10+
11+
class CursorPaginationNormalizerTest extends TestCase
12+
{
13+
public function testNormalizeWithItems(): void
14+
{
15+
$items = [
16+
['id' => 1, 'value' => 'A'],
17+
['id' => 2, 'value' => 'B'],
18+
];
19+
20+
$paginationResult = new CursorPaginationResult($items, limit: 2, total: 10);
21+
$normalizer = new CursorPaginationNormalizer();
22+
23+
$result = $normalizer->normalize($paginationResult);
24+
25+
$this->assertIsArray($result);
26+
$this->assertEquals($items, $result['items']);
27+
$this->assertEquals([
28+
'total' => 10,
29+
'limit' => 2,
30+
'has_more' => true,
31+
'next_cursor' => 2,
32+
], $result['pagination']);
33+
}
34+
35+
public function testNormalizeWithFewerItemsThanLimit(): void
36+
{
37+
$items = [
38+
['id' => 5, 'value' => 'X'],
39+
];
40+
41+
$paginationResult = new CursorPaginationResult($items, limit: 5, total: 3);
42+
$normalizer = new CursorPaginationNormalizer();
43+
44+
$result = $normalizer->normalize($paginationResult);
45+
46+
$this->assertFalse($result['pagination']['has_more']);
47+
$this->assertEquals(5, $result['pagination']['next_cursor']);
48+
}
49+
50+
public function testNormalizeWithEmptyItems(): void
51+
{
52+
$paginationResult = new CursorPaginationResult([], limit: 5, total: 0);
53+
$normalizer = new CursorPaginationNormalizer();
54+
55+
$result = $normalizer->normalize($paginationResult);
56+
57+
$this->assertSame([], $result['items']);
58+
$this->assertSame([
59+
'total' => 0,
60+
'limit' => 5,
61+
'has_more' => false,
62+
'next_cursor' => null,
63+
], $result['pagination']);
64+
}
65+
66+
public function testSupportsNormalization(): void
67+
{
68+
$normalizer = new CursorPaginationNormalizer();
69+
70+
$dto = new CursorPaginationResult([], 0, 10);
71+
$this->assertTrue($normalizer->supportsNormalization($dto));
72+
$this->assertFalse($normalizer->supportsNormalization(new \stdClass()));
73+
}
74+
}

0 commit comments

Comments
 (0)