Files
intotheeast-com-content/plugins/api/tests/Unit/Exceptions/ExceptionTest.php
T

170 lines
6.1 KiB
PHP

<?php
declare(strict_types=1);
namespace Grav\Plugin\Api\Tests\Unit\Exceptions;
use Grav\Plugin\Api\Exceptions\ApiException;
use Grav\Plugin\Api\Exceptions\ConflictException;
use Grav\Plugin\Api\Exceptions\ForbiddenException;
use Grav\Plugin\Api\Exceptions\NotFoundException;
use Grav\Plugin\Api\Exceptions\UnauthorizedException;
use Grav\Plugin\Api\Exceptions\ValidationException;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;
#[CoversClass(ApiException::class)]
#[CoversClass(NotFoundException::class)]
#[CoversClass(ForbiddenException::class)]
#[CoversClass(UnauthorizedException::class)]
#[CoversClass(ValidationException::class)]
#[CoversClass(ConflictException::class)]
class ExceptionTest extends TestCase
{
#[Test]
public function api_exception_properties(): void
{
$exception = new ApiException(
statusCode: 418,
errorTitle: "I'm a Teapot",
detail: 'Short and stout.',
headers: ['X-Teapot' => 'yes'],
);
self::assertSame(418, $exception->getStatusCode());
self::assertSame("I'm a Teapot", $exception->getErrorTitle());
self::assertSame('Short and stout.', $exception->getMessage());
self::assertSame(['X-Teapot' => 'yes'], $exception->getHeaders());
// The code property of RuntimeException should be the HTTP status
self::assertSame(418, $exception->getCode());
}
#[Test]
public function api_exception_with_previous(): void
{
$previous = new \RuntimeException('Root cause');
$exception = new ApiException(500, 'Internal Server Error', 'Something broke.', previous: $previous);
self::assertSame($previous, $exception->getPrevious());
}
#[Test]
public function not_found_exception_defaults(): void
{
$exception = new NotFoundException();
self::assertSame(404, $exception->getStatusCode());
self::assertSame('Not Found', $exception->getErrorTitle());
self::assertSame('The requested resource was not found.', $exception->getMessage());
self::assertSame([], $exception->getHeaders());
}
#[Test]
public function not_found_exception_custom_detail(): void
{
$exception = new NotFoundException('Page /blog/missing was not found.');
self::assertSame(404, $exception->getStatusCode());
self::assertSame('Page /blog/missing was not found.', $exception->getMessage());
}
#[Test]
public function forbidden_exception_defaults(): void
{
$exception = new ForbiddenException();
self::assertSame(403, $exception->getStatusCode());
self::assertSame('Forbidden', $exception->getErrorTitle());
self::assertSame('You do not have permission to perform this action.', $exception->getMessage());
self::assertSame([], $exception->getHeaders());
}
#[Test]
public function unauthorized_exception_has_www_authenticate_header(): void
{
$exception = new UnauthorizedException();
self::assertSame(401, $exception->getStatusCode());
self::assertSame('Unauthorized', $exception->getErrorTitle());
self::assertSame('Authentication is required.', $exception->getMessage());
$headers = $exception->getHeaders();
self::assertArrayHasKey('WWW-Authenticate', $headers);
self::assertSame('Bearer', $headers['WWW-Authenticate']);
}
#[Test]
public function unauthorized_exception_custom_detail(): void
{
$exception = new UnauthorizedException('Token has expired.');
self::assertSame(401, $exception->getStatusCode());
self::assertSame('Token has expired.', $exception->getMessage());
self::assertSame('Bearer', $exception->getHeaders()['WWW-Authenticate']);
}
#[Test]
public function validation_exception_includes_errors(): void
{
$errors = [
['field' => 'email', 'message' => 'Email is required.'],
['field' => 'name', 'message' => 'Name must not be empty.'],
];
$exception = new ValidationException('Validation failed.', $errors);
self::assertSame(422, $exception->getStatusCode());
self::assertSame('Unprocessable Entity', $exception->getErrorTitle());
self::assertSame('Validation failed.', $exception->getMessage());
self::assertSame($errors, $exception->getValidationErrors());
}
#[Test]
public function validation_exception_defaults(): void
{
$exception = new ValidationException();
self::assertSame(422, $exception->getStatusCode());
self::assertSame('The request data is invalid.', $exception->getMessage());
self::assertSame([], $exception->getValidationErrors());
}
#[Test]
public function conflict_exception_defaults(): void
{
$exception = new ConflictException();
self::assertSame(409, $exception->getStatusCode());
self::assertSame('Conflict', $exception->getErrorTitle());
self::assertSame('The resource has been modified. Refresh and try again.', $exception->getMessage());
self::assertSame([], $exception->getHeaders());
}
#[Test]
public function conflict_exception_custom_detail(): void
{
$exception = new ConflictException('ETag mismatch for page /about.');
self::assertSame(409, $exception->getStatusCode());
self::assertSame('ETag mismatch for page /about.', $exception->getMessage());
}
#[Test]
public function all_exceptions_extend_api_exception(): void
{
self::assertInstanceOf(ApiException::class, new NotFoundException());
self::assertInstanceOf(ApiException::class, new ForbiddenException());
self::assertInstanceOf(ApiException::class, new UnauthorizedException());
self::assertInstanceOf(ApiException::class, new ValidationException());
self::assertInstanceOf(ApiException::class, new ConflictException());
}
#[Test]
public function api_exception_extends_runtime_exception(): void
{
self::assertInstanceOf(\RuntimeException::class, new ApiException(500, 'Error', 'test'));
}
}