Compare commits

..

2 Commits

Author SHA1 Message Date
Alejandro Celaya
d945c28a75 Merge pull request #911 from shlinkio/develop
Release 2.4.2
2020-11-22 11:01:00 +01:00
Alejandro Celaya
7bb40c74c1 Merge pull request #895 from shlinkio/develop
Release 2.4.1
2020-11-10 20:00:26 +01:00
32 changed files with 225 additions and 259 deletions

View File

@@ -4,27 +4,6 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com), and this project adheres to [Semantic Versioning](https://semver.org).
## [Unreleased]
### Added
* [#869](https://github.com/shlinkio/shlink/issues/869) Added support for Mercure Hub 0.10.
* [#833](https://github.com/shlinkio/shlink/issues/833) Added support to connect through unix socket when using an external MySQL, MariaDB or Postgres database.
It can be provided during the installation, or as the `DB_UNIX_SOCKET` env var for the docker image.
### Changed
* [#912](https://github.com/shlinkio/shlink/issues/912) Changed error templates to be plain html files, removing the dependency on `league/plates` package.
### Deprecated
* [#917](https://github.com/shlinkio/shlink/issues/917) Deprecated `/{shortCode}/qr-code/{size}` URL, in favor of providing the size in the query instead, `/{shortCode}/qr-code?size={size}`.
* [#924](https://github.com/shlinkio/shlink/issues/924) Deprecated mechanism to provide config options to the docker image through volumes. Use the env vars instead as a direct replacement.
### Removed
* *Nothing*
### Fixed
* *Nothing*
## [2.4.2] - 2020-11-22
### Added
* *Nothing*

View File

@@ -32,12 +32,13 @@
"laminas/laminas-paginator": "^2.8",
"laminas/laminas-servicemanager": "^3.4",
"laminas/laminas-stdlib": "^3.2",
"lcobucci/jwt": "^4.0",
"lcobucci/jwt": "^4.0@alpha <4.0@beta",
"league/uri": "^6.2",
"lstrojny/functional-php": "^1.9",
"mezzio/mezzio": "^3.2",
"mezzio/mezzio-fastroute": "^3.0",
"mezzio/mezzio-helpers": "^5.3",
"mezzio/mezzio-platesrenderer": "^2.1",
"mezzio/mezzio-problem-details": "^1.1",
"mezzio/mezzio-swoole": "^2.6.4",
"monolog/monolog": "^2.0",
@@ -48,16 +49,16 @@
"predis/predis": "^1.1",
"pugx/shortid-php": "^0.5",
"ramsey/uuid": "^3.9",
"shlinkio/shlink-common": "^3.3.2",
"shlinkio/shlink-common": "^3.3.0",
"shlinkio/shlink-config": "^1.0",
"shlinkio/shlink-event-dispatcher": "^1.4",
"shlinkio/shlink-importer": "^2.0.1",
"shlinkio/shlink-installer": "^5.2.0",
"shlinkio/shlink-installer": "^5.1.0",
"shlinkio/shlink-ip-geolocation": "^1.5",
"symfony/console": "^5.1",
"symfony/filesystem": "^5.1",
"symfony/lock": "^5.1",
"symfony/mercure": "^0.4.0",
"symfony/mercure": "^0.3.0",
"symfony/process": "^5.1",
"symfony/string": "^5.1"
},

View File

@@ -14,7 +14,6 @@ return [
Option\Database\DatabasePortConfigOption::class,
Option\Database\DatabaseUserConfigOption::class,
Option\Database\DatabasePasswordConfigOption::class,
Option\Database\DatabaseUnixSocketConfigOption::class,
Option\Database\DatabaseSqlitePathConfigOption::class,
Option\Database\DatabaseMySqlOptionsConfigOption::class,
Option\UrlShortener\ShortDomainHostConfigOption::class,

View File

@@ -0,0 +1,17 @@
<?php
declare(strict_types=1);
return [
'templates' => [
'extension' => 'phtml',
],
'plates' => [
'extensions' => [
// extension service names or instances
],
],
];

View File

@@ -15,6 +15,7 @@ return (new ConfigAggregator\ConfigAggregator([
Mezzio\ConfigProvider::class,
Mezzio\Router\ConfigProvider::class,
Mezzio\Router\FastRouteRouter\ConfigProvider::class,
Mezzio\Plates\ConfigProvider::class,
Mezzio\Swoole\ConfigProvider::class,
ProblemDetails\ConfigProvider::class,
Diactoros\ConfigProvider::class,

View File

@@ -131,7 +131,7 @@ services:
shlink_mercure:
container_name: shlink_mercure
image: dunglas/mercure:v0.10
image: dunglas/mercure:v0.9
ports:
- "3080:80"
environment:

View File

@@ -157,7 +157,6 @@ This is the complete list of supported env vars:
* **mysql** or **maria** -> `3306`
* **postgres** -> `5432`
* **mssql** -> `1433`
* `DB_UNIX_SOCKET`: Alternatively to the `DB_HOST`, you can provide this to connect through unix sockets when using `mysql`, `maria` or `postgres` drivers.
* `DISABLE_TRACK_PARAM`: The name of a query param that can be used to visit short URLs avoiding the visit to be tracked. This feature won't be available if not value is provided.
* `DELETE_SHORT_URL_THRESHOLD`: The amount of visits on short URLs which will not allow them to be deleted. Defaults to `15`.
* `VALIDATE_URLS`: Boolean which tells if shlink should validate a status 20x is returned (after following redirects) when trying to shorten a URL. Defaults to `false`.
@@ -216,11 +215,7 @@ docker run \
shlinkio/shlink:stable
```
## [DEPRECATED] Provide config via volumes
> As of v2.5.0, providing config through volumes is deprecated, and no new options will be added anymore. Use env vars instead.
>
> Support for config options through volumes will be removed in Shlink v3.0.0
## Provide config via volumes
Rather than providing custom configuration via env vars, it is also possible ot provide config files in json format.

View File

@@ -34,7 +34,6 @@ $helper = new class {
public function getDbConfig(): array
{
$driver = env('DB_DRIVER');
$isMysql = contains(['maria', 'mysql'], $driver);
if ($driver === null || $driver === 'sqlite') {
return [
'driver' => 'pdo_sqlite',
@@ -42,7 +41,7 @@ $helper = new class {
];
}
$driverOptions = ! $isMysql ? [] : [
$driverOptions = ! contains(['maria', 'mysql'], $driver) ? [] : [
// 1002 -> PDO::MYSQL_ATTR_INIT_COMMAND
1002 => 'SET NAMES utf8',
// 1000 -> PDO::MYSQL_ATTR_USE_BUFFERED_QUERY
@@ -53,10 +52,9 @@ $helper = new class {
'dbname' => env('DB_NAME', 'shlink'),
'user' => env('DB_USER'),
'password' => env('DB_PASSWORD'),
'host' => env('DB_HOST', $driver === 'postgres' ? env('DB_UNIX_SOCKET') : null),
'host' => env('DB_HOST'),
'port' => env('DB_PORT', self::DB_PORTS_MAP[$driver]),
'driverOptions' => $driverOptions,
'unix_socket' => $isMysql ? env('DB_UNIX_SOCKET') : null,
];
}

View File

@@ -18,7 +18,7 @@
},
{
"name": "size",
"in": "query",
"in": "path",
"description": "The size of the image to be returned.",
"required": false,
"schema": {

View File

@@ -1,66 +0,0 @@
{
"get": {
"operationId": "shortUrlQrCodeSize",
"deprecated": true,
"tags": [
"URL Shortener"
],
"summary": "Short URL QR code",
"description": "Generates a QR code image pointing to a short URL",
"parameters": [
{
"name": "shortCode",
"in": "path",
"description": "The short code to resolve.",
"required": true,
"schema": {
"type": "string"
}
},
{
"name": "size",
"in": "path",
"description": "The size of the image to be returned.",
"required": false,
"schema": {
"type": "integer",
"minimum": 50,
"maximum": 1000,
"default": 300
}
},
{
"name": "format",
"in": "query",
"description": "The format for the QR code image, being valid values png and svg. Not providing the param or providing any other value will fall back to png.",
"required": false,
"schema": {
"type": "string",
"enum": [
"png",
"svg"
]
}
}
],
"responses": {
"200": {
"description": "QR code in PNG format",
"content": {
"image/png": {
"schema": {
"type": "string",
"format": "binary"
}
},
"image/svg+xml": {
"schema": {
"type": "string",
"format": "binary"
}
}
}
}
}
}
}

View File

@@ -116,9 +116,6 @@
},
"/{shortCode}/qr-code": {
"$ref": "paths/{shortCode}_qr-code.json"
},
"/{shortCode}/qr-code/{size}": {
"$ref": "paths/{shortCode}_qr-code_{size}.json"
}
}
}

View File

@@ -4,7 +4,6 @@ declare(strict_types=1);
namespace ShlinkioTest\Shlink\CLI;
use Laminas\ServiceManager\AbstractFactory\ConfigAbstractFactory;
use PHPUnit\Framework\TestCase;
use Shlinkio\Shlink\CLI\ConfigProvider;
@@ -22,9 +21,7 @@ class ConfigProviderTest extends TestCase
{
$config = ($this->configProvider)();
self::assertCount(3, $config);
self::assertArrayHasKey('cli', $config);
self::assertArrayHasKey('dependencies', $config);
self::assertArrayHasKey(ConfigAbstractFactory::class, $config);
}
}

View File

@@ -5,7 +5,7 @@ declare(strict_types=1);
namespace Shlinkio\Shlink\Core;
use Laminas\ServiceManager\AbstractFactory\ConfigAbstractFactory;
use Laminas\ServiceManager\Factory\InvokableFactory;
use Mezzio\Template\TemplateRendererInterface;
use Psr\EventDispatcher\EventDispatcherInterface;
use Shlinkio\Shlink\Core\ErrorHandler;
use Shlinkio\Shlink\Core\Options\NotFoundRedirectOptions;
@@ -16,7 +16,7 @@ return [
'dependencies' => [
'factories' => [
ErrorHandler\NotFoundRedirectHandler::class => ConfigAbstractFactory::class,
ErrorHandler\NotFoundTemplateHandler::class => InvokableFactory::class,
ErrorHandler\NotFoundTemplateHandler::class => ConfigAbstractFactory::class,
Options\AppOptions::class => ConfigAbstractFactory::class,
Options\DeleteShortUrlsOptions::class => ConfigAbstractFactory::class,
@@ -60,6 +60,7 @@ return [
Util\RedirectResponseHelper::class,
'config.router.base_path',
],
ErrorHandler\NotFoundTemplateHandler::class => [TemplateRendererInterface::class],
Options\AppOptions::class => ['config.app_options'],
Options\DeleteShortUrlsOptions::class => ['config.delete_short_urls'],

View File

@@ -0,0 +1,14 @@
<?php
declare(strict_types=1);
return [
'mezzio' => [
'error_handler' => [
'template_404' => 'ShlinkCore::error/404',
'template_error' => 'ShlinkCore::error/error',
],
],
];

View File

@@ -29,17 +29,7 @@ return [
],
[
'name' => Action\QrCodeAction::class,
'path' => '/{shortCode}/qr-code',
'middleware' => [
Action\QrCodeAction::class,
],
'allowed_methods' => [RequestMethod::METHOD_GET],
],
// Deprecated
[
'name' => 'old_' . Action\QrCodeAction::class,
'path' => '/{shortCode}/qr-code/{size:[0-9]+}',
'path' => '/{shortCode}/qr-code[/{size:[0-9]+}]',
'middleware' => [
Action\QrCodeAction::class,
],

View File

@@ -0,0 +1,13 @@
<?php
declare(strict_types=1);
return [
'templates' => [
'paths' => [
'ShlinkCore' => __DIR__ . '/../templates',
],
],
];

View File

@@ -41,7 +41,7 @@ abstract class AbstractTrackingAction implements MiddlewareInterface, RequestMet
$this->urlResolver = $urlResolver;
$this->visitTracker = $visitTracker;
$this->appOptions = $appOptions;
$this->logger = $logger ?? new NullLogger();
$this->logger = $logger ?: new NullLogger();
}
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface

View File

@@ -34,7 +34,7 @@ class QrCodeAction implements MiddlewareInterface
) {
$this->urlResolver = $urlResolver;
$this->domainConfig = $domainConfig;
$this->logger = $logger ?? new NullLogger();
$this->logger = $logger ?: new NullLogger();
}
public function process(Request $request, RequestHandlerInterface $handler): Response
@@ -48,15 +48,11 @@ class QrCodeAction implements MiddlewareInterface
return $handler->handle($request);
}
$query = $request->getQueryParams();
// Size attribute is deprecated
$size = $this->normalizeSize((int) $request->getAttribute('size', $query['size'] ?? self::DEFAULT_SIZE));
$qrCode = new QrCode($shortUrl->toString($this->domainConfig));
$qrCode->setSize($size);
$qrCode->setSize($this->getSizeParam($request));
$qrCode->setMargin(0);
$format = $query['format'] ?? 'png';
$format = $request->getQueryParams()['format'] ?? 'png';
if ($format === 'svg') {
$qrCode->setWriter(new SvgWriter());
}
@@ -64,8 +60,9 @@ class QrCodeAction implements MiddlewareInterface
return new QrCodeResponse($qrCode);
}
private function normalizeSize(int $size): int
private function getSizeParam(Request $request): int
{
$size = (int) $request->getAttribute('size', self::DEFAULT_SIZE);
if ($size < self::MIN_SIZE) {
return self::MIN_SIZE;
}

View File

@@ -15,7 +15,6 @@ use function Functional\contains;
use function Functional\reduce_left;
use function uksort;
/** @deprecated */
class SimplifiedConfigParser
{
private const SIMPLIFIED_CONFIG_MAPPING = [

View File

@@ -4,37 +4,40 @@ declare(strict_types=1);
namespace Shlinkio\Shlink\Core\ErrorHandler;
use Closure;
use Fig\Http\Message\StatusCodeInterface;
use InvalidArgumentException;
use Laminas\Diactoros\Response;
use Mezzio\Router\RouteResult;
use Mezzio\Template\TemplateRendererInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use function file_get_contents;
use function sprintf;
class NotFoundTemplateHandler implements RequestHandlerInterface
{
private const TEMPLATES_BASE_DIR = __DIR__ . '/../../templates';
public const NOT_FOUND_TEMPLATE = '404.html';
public const INVALID_SHORT_CODE_TEMPLATE = 'invalid-short-code.html';
private Closure $readFile;
public const NOT_FOUND_TEMPLATE = 'ShlinkCore::error/404';
public const INVALID_SHORT_CODE_TEMPLATE = 'ShlinkCore::invalid-short-code';
public function __construct(?callable $readFile = null)
private TemplateRendererInterface $renderer;
public function __construct(TemplateRendererInterface $renderer)
{
$this->readFile = $readFile ? Closure::fromCallable($readFile) : fn (string $file) => file_get_contents($file);
$this->renderer = $renderer;
}
/**
* Dispatch the next available middleware and return the response.
*
*
* @throws InvalidArgumentException
*/
public function handle(ServerRequestInterface $request): ResponseInterface
{
/** @var RouteResult $routeResult */
$routeResult = $request->getAttribute(RouteResult::class) ?? RouteResult::fromRouteFailure(null);
$routeResult = $request->getAttribute(RouteResult::class, RouteResult::fromRouteFailure(null));
$status = StatusCodeInterface::STATUS_NOT_FOUND;
$template = $routeResult->isFailure() ? self::NOT_FOUND_TEMPLATE : self::INVALID_SHORT_CODE_TEMPLATE;
$templateContent = ($this->readFile)(sprintf('%s/%s', self::TEMPLATES_BASE_DIR, $template));
return new Response\HtmlResponse($templateContent, $status);
return new Response\HtmlResponse($this->renderer->render($template), $status);
}
}

View File

@@ -19,6 +19,12 @@ class ShortUrlRepositoryAdapter implements AdapterInterface
$this->params = $params;
}
/**
* Returns a collection of items for a page.
*
* @param int $offset Page offset
* @param int $itemCountPerPage Number of items per page
*/
public function getItems($offset, $itemCountPerPage): array // phpcs:ignore
{
return $this->repository->findList(
@@ -31,6 +37,15 @@ class ShortUrlRepositoryAdapter implements AdapterInterface
);
}
/**
* Count elements of an object
* @link http://php.net/manual/en/countable.count.php
* @return int The custom count as an integer.
* </p>
* <p>
* The return value is cast to an integer.
* @since 5.1.0
*/
public function count(): int
{
return $this->repository->countList(

View File

@@ -33,9 +33,15 @@ class ShortUrlRepository extends EntityRepository implements ShortUrlRepositoryI
?DateRange $dateRange = null
): array {
$qb = $this->createListQueryBuilder($searchTerm, $tags, $dateRange);
$qb->select('DISTINCT s')
->setMaxResults($limit)
->setFirstResult($offset);
$qb->select('DISTINCT s');
// Set limit and offset
if ($limit !== null) {
$qb->setMaxResults($limit);
}
if ($offset !== null) {
$qb->setFirstResult($offset);
}
// In case the ordering has been specified, the query could be more complex. Process it
if ($orderBy !== null && $orderBy->hasOrderField()) {
@@ -141,7 +147,7 @@ class ShortUrlRepository extends EntityRepository implements ShortUrlRepositoryI
WHERE s.shortCode = :shortCode
AND (s.domain IS NULL OR d.authority = :domain)
ORDER BY s.domain {$ordering}
DQL;
DQL;
$query = $this->getEntityManager()->createQuery($dql);
$query->setMaxResults(1)
@@ -214,8 +220,9 @@ class ShortUrlRepository extends EntityRepository implements ShortUrlRepositoryI
}
if ($meta->hasValidUntil()) {
$qb->andWhere($qb->expr()->eq('s.validUntil', ':validUntil'))
->setParameter('validUntil', $meta->getValidUntil());
->setParameter('validUntil', $meta->getValidUntil());
}
if ($meta->hasDomain()) {
$qb->join('s.domain', 'd')
->andWhere($qb->expr()->eq('d.authority', ':domain'))

View File

@@ -1,27 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>Not Found | Shlink</title>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css">
<link rel="shortcut icon" href="/favicon.ico">
<style>
html, body {height: 100%}
.app {height: 100vh; display: flex; align-items: center; justify-content: center; flex-flow: column;}
p {margin-bottom: 20px;}
body {text-align: center;}
</style>
</head>
<body>
<div class="app">
<main class="container">
<h1>404</h1>
<hr>
<h3>Page not found.</h3>
<p>The page you requested could not be found.</p>
</main>
</div>
</body>
</html>

View File

@@ -0,0 +1,19 @@
<?php $this->layout('ShlinkCore::layout/default') ?>
<?php $this->start('title') ?>
Not Found
<?php $this->end() ?>
<?php $this->start('stylesheets') ?>
<style>
p {margin-bottom: 20px;}
body {text-align: center;}
</style>
<?php $this->end() ?>
<?php $this->start('main') ?>
<h1>404</h1>
<hr>
<h3>Page not found.</h3>
<p>The page you requested could not be found.</p>
<?php $this->end() ?>

View File

@@ -0,0 +1,25 @@
<?php $this->layout('ShlinkCore::layout/default') ?>
<?php $this->start('title') ?>
<?= $this->e($status . ' ' . $reason) ?>
<?php $this->end() ?>
<?php $this->start('stylesheets') ?>
<style>
p {margin-bottom: 20px;}
body {text-align: center;}
</style>
<?php $this->end() ?>
<?php $this->start('main') ?>
<h1>Oops!</h1>
<hr>
<?php if ($status !== 404): ?>
<p><?= sprintf('We encountered a %s %s error.', $status, $reason) ?></p>
<?php else: ?>
<p>'This short URL doesn't seem to be valid.</p>
<p>'Make sure you included all the characters, with no extra punctuation.</p>
<?php endif; ?>
<?php $this->end() ?>

View File

@@ -1,27 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>Invalid Short URL | Shlink</title>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css">
<link rel="shortcut icon" href="/favicon.ico">
<style>
html, body {height: 100%}
.app {height: 100vh; display: flex; align-items: center; justify-content: center; flex-flow: column;}
p {margin-bottom: 20px;}
body {text-align: center;}
</style>
</head>
<body>
<div class="app">
<main class="container">
<h1>Oops!</h1>
<hr>
<p>This short URL doesn't seem to be valid.</p>
<p>Make sure you included all the characters, with no extra punctuation.</p>
</main>
</div>
</body>
</html>

View File

@@ -0,0 +1,19 @@
<?php $this->layout('ShlinkCore::layout/default') ?>
<?php $this->start('title') ?>
Invalid Short URL
<?php $this->end() ?>
<?php $this->start('stylesheets') ?>
<style>
p {margin-bottom: 20px;}
body {text-align: center;}
</style>
<?php $this->end() ?>
<?php $this->start('main') ?>
<h1>Oops!</h1>
<hr>
<p>This short URL doesn't seem to be valid.</p>
<p>Make sure you included all the characters, with no extra punctuation.</p>
<?php $this->end() ?>

View File

@@ -0,0 +1,23 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title><?= $this->section('title', '') ?> | Shlink</title>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css">
<link rel="shortcut icon" href="/favicon.ico">
<style>
html, body {height: 100%}
.app {height: 100vh; display: flex; align-items: center; justify-content: center; flex-flow: column;}
</style>
<?= $this->section('stylesheets', '') ?>
</head>
<body>
<div class="app">
<main class="container">
<?= $this->section('main', '') ?>
</main>
</div>
</body>
</html>

View File

@@ -6,13 +6,11 @@ namespace ShlinkioTest\Shlink\Core\Action;
use Laminas\Diactoros\Response;
use Laminas\Diactoros\ServerRequest;
use Laminas\Diactoros\ServerRequestFactory;
use Mezzio\Router\RouterInterface;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
use Prophecy\PhpUnit\ProphecyTrait;
use Prophecy\Prophecy\ObjectProphecy;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Shlinkio\Shlink\Common\Response\QrCodeResponse;
use Shlinkio\Shlink\Core\Action\QrCodeAction;
@@ -21,8 +19,6 @@ use Shlinkio\Shlink\Core\Exception\ShortUrlNotFoundException;
use Shlinkio\Shlink\Core\Model\ShortUrlIdentifier;
use Shlinkio\Shlink\Core\Service\ShortUrl\ShortUrlResolverInterface;
use function getimagesizefromstring;
class QrCodeActionTest extends TestCase
{
use ProphecyTrait;
@@ -55,6 +51,21 @@ class QrCodeActionTest extends TestCase
$process->shouldHaveBeenCalledOnce();
}
/** @test */
public function anInvalidShortCodeWillReturnNotFoundResponse(): void
{
$shortCode = 'abc123';
$this->urlResolver->resolveEnabledShortUrl(new ShortUrlIdentifier($shortCode, ''))
->willThrow(ShortUrlNotFoundException::class)
->shouldBeCalledOnce();
$delegate = $this->prophesize(RequestHandlerInterface::class);
$process = $delegate->handle(Argument::any())->willReturn(new Response());
$this->action->process((new ServerRequest())->withAttribute('shortCode', $shortCode), $delegate->reveal());
$process->shouldHaveBeenCalledOnce();
}
/** @test */
public function aCorrectRequestReturnsTheQrCodeResponse(): void
{
@@ -99,31 +110,4 @@ class QrCodeActionTest extends TestCase
yield 'svg format' => [['format' => 'svg'], 'image/svg+xml'];
yield 'unsupported format' => [['format' => 'jpg'], 'image/png'];
}
/**
* @test
* @dataProvider provideRequestsWithSize
*/
public function imageIsReturnedWithExpectedSize(ServerRequestInterface $req, int $expectedSize): void
{
$code = 'abc123';
$this->urlResolver->resolveEnabledShortUrl(new ShortUrlIdentifier($code, ''))->willReturn(new ShortUrl(''));
$delegate = $this->prophesize(RequestHandlerInterface::class);
$resp = $this->action->process($req->withAttribute('shortCode', $code), $delegate->reveal());
[$size] = getimagesizefromstring((string) $resp->getBody());
self::assertEquals($expectedSize, $size);
}
public function provideRequestsWithSize(): iterable
{
yield 'no size' => [ServerRequestFactory::fromGlobals(), 300];
yield 'size in attr' => [ServerRequestFactory::fromGlobals()->withAttribute('size', '400'), 400];
yield 'size in query' => [ServerRequestFactory::fromGlobals()->withQueryParams(['size' => '123']), 123];
yield 'size in query and attr' => [
ServerRequestFactory::fromGlobals()->withAttribute('size', '350')->withQueryParams(['size' => '123']),
350,
];
}
}

View File

@@ -4,7 +4,6 @@ declare(strict_types=1);
namespace ShlinkioTest\Shlink\Core;
use Laminas\ServiceManager\AbstractFactory\ConfigAbstractFactory;
use PHPUnit\Framework\TestCase;
use Shlinkio\Shlink\Core\ConfigProvider;
@@ -20,13 +19,11 @@ class ConfigProviderTest extends TestCase
/** @test */
public function properConfigIsReturned(): void
{
$config = ($this->configProvider)();
$config = $this->configProvider->__invoke();
self::assertCount(5, $config);
self::assertArrayHasKey('routes', $config);
self::assertArrayHasKey('dependencies', $config);
self::assertArrayHasKey('entity_manager', $config);
self::assertArrayHasKey('events', $config);
self::assertArrayHasKey(ConfigAbstractFactory::class, $config);
self::assertArrayHasKey('templates', $config);
self::assertArrayHasKey('mezzio', $config);
}
}

View File

@@ -4,30 +4,29 @@ declare(strict_types=1);
namespace ShlinkioTest\Shlink\Core\ErrorHandler;
use Closure;
use Laminas\Diactoros\Response;
use Laminas\Diactoros\ServerRequestFactory;
use Mezzio\Router\Route;
use Mezzio\Router\RouteResult;
use Mezzio\Template\TemplateRendererInterface;
use PHPUnit\Framework\TestCase;
use Prophecy\PhpUnit\ProphecyTrait;
use Prophecy\Prophecy\ObjectProphecy;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Shlinkio\Shlink\Core\ErrorHandler\NotFoundTemplateHandler;
class NotFoundTemplateHandlerTest extends TestCase
{
use ProphecyTrait;
private NotFoundTemplateHandler $handler;
private Closure $readFile;
private bool $readFileCalled;
private ObjectProphecy $renderer;
public function setUp(): void
{
$this->readFileCalled = false;
$this->readFile = function (string $fileName): string {
$this->readFileCalled = true;
return $fileName;
};
$this->handler = new NotFoundTemplateHandler($this->readFile);
$this->renderer = $this->prophesize(TemplateRendererInterface::class);
$this->handler = new NotFoundTemplateHandler($this->renderer->reveal());
}
/**
@@ -36,11 +35,13 @@ class NotFoundTemplateHandlerTest extends TestCase
*/
public function properErrorTemplateIsRendered(ServerRequestInterface $request, string $expectedTemplate): void
{
$resp = $this->handler->handle($request->withHeader('Accept', 'text/html'));
$request = $request->withHeader('Accept', 'text/html');
$render = $this->renderer->render($expectedTemplate)->willReturn('');
$resp = $this->handler->handle($request);
self::assertInstanceOf(Response\HtmlResponse::class, $resp);
self::assertStringContainsString($expectedTemplate, (string) $resp->getBody());
self::assertTrue($this->readFileCalled);
$render->shouldHaveBeenCalledOnce();
}
public function provideTemplates(): iterable

View File

@@ -4,7 +4,6 @@ declare(strict_types=1);
namespace ShlinkioTest\Shlink\Rest;
use Laminas\ServiceManager\AbstractFactory\ConfigAbstractFactory;
use PHPUnit\Framework\TestCase;
use Shlinkio\Shlink\Rest\ConfigProvider;
@@ -22,12 +21,8 @@ class ConfigProviderTest extends TestCase
{
$config = ($this->configProvider)();
self::assertCount(5, $config);
self::assertArrayHasKey('routes', $config);
self::assertArrayHasKey('dependencies', $config);
self::assertArrayHasKey('auth', $config);
self::assertArrayHasKey('entity_manager', $config);
self::assertArrayHasKey(ConfigAbstractFactory::class, $config);
}
/**