Merge pull request #1263 from acelaya-forks/feature/api-tests-coverage

Feature/api tests coverage
This commit is contained in:
Alejandro Celaya 2021-12-10 18:25:38 +01:00 committed by GitHub
commit 537152450f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 69 additions and 27 deletions

View file

@ -202,7 +202,7 @@ jobs:
strategy: strategy:
matrix: matrix:
php-version: ['8.0', '8.1'] php-version: ['8.0', '8.1']
test-group: ['unit', 'db'] test-group: ['unit', 'db', 'api']
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v2 uses: actions/checkout@v2
@ -222,8 +222,8 @@ jobs:
run: composer infect:ci:unit run: composer infect:ci:unit
env: env:
INFECTION_BADGE_API_KEY: ${{ secrets.INFECTION_BADGE_API_KEY }} INFECTION_BADGE_API_KEY: ${{ secrets.INFECTION_BADGE_API_KEY }}
- if: ${{ matrix.test-group == 'db' }} - if: ${{ matrix.test-group != 'unit' }}
run: composer infect:ci:db run: composer infect:ci:${{ matrix.test-group }}
upload-coverage: upload-coverage:
needs: needs:

View file

@ -25,6 +25,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com), and this
The official docker image has also been updated to use PHP 8.1 by default. The official docker image has also been updated to use PHP 8.1 by default.
### Changed ### Changed
* [#844](https://github.com/shlinkio/shlink/issues/844) Added mutation checks to API tests.
* [#1218](https://github.com/shlinkio/shlink/issues/1218) Updated to symfony/mercure 0.6. * [#1218](https://github.com/shlinkio/shlink/issues/1218) Updated to symfony/mercure 0.6.
* [#1223](https://github.com/shlinkio/shlink/issues/1223) Updated to phpstan 1.0. * [#1223](https://github.com/shlinkio/shlink/issues/1223) Updated to phpstan 1.0.
* Added `domain` field to `DeleteShortUrlException` exception. * Added `domain` field to `DeleteShortUrlException` exception.

View file

@ -74,7 +74,7 @@
"phpunit/phpunit": "^9.5", "phpunit/phpunit": "^9.5",
"roave/security-advisories": "dev-master", "roave/security-advisories": "dev-master",
"shlinkio/php-coding-standard": "~2.2.0", "shlinkio/php-coding-standard": "~2.2.0",
"shlinkio/shlink-test-utils": "^2.4", "shlinkio/shlink-test-utils": "^2.5",
"symfony/var-dumper": "^6.0", "symfony/var-dumper": "^6.0",
"veewee/composer-run-parallel": "^1.1" "veewee/composer-run-parallel": "^1.1"
}, },
@ -113,7 +113,7 @@
], ],
"ci:parallel": [ "ci:parallel": [
"@parallel cs stan swagger:validate test:unit:ci test:db:sqlite:ci test:db:mysql test:db:maria test:db:postgres test:db:ms", "@parallel cs stan swagger:validate test:unit:ci test:db:sqlite:ci test:db:mysql test:db:maria test:db:postgres test:db:ms",
"@parallel test:api infect:ci:unit infect:ci:db" "@parallel infect:test:api infect:ci:unit infect:ci:db"
], ],
"cs": "phpcs", "cs": "phpcs",
"cs:fix": "phpcbf", "cs:fix": "phpcbf",
@ -130,7 +130,7 @@
], ],
"test:unit": "@php vendor/bin/phpunit --order-by=random --colors=always --coverage-php build/coverage-unit.cov --testdox", "test:unit": "@php vendor/bin/phpunit --order-by=random --colors=always --coverage-php build/coverage-unit.cov --testdox",
"test:unit:ci": "@test:unit --coverage-xml=build/coverage-unit/coverage-xml --log-junit=build/coverage-unit/junit.xml", "test:unit:ci": "@test:unit --coverage-xml=build/coverage-unit/coverage-xml --log-junit=build/coverage-unit/junit.xml",
"test:unit:pretty": "@php vendor/bin/phpunit --order-by=random --colors=always --coverage-html build/coverage-unit-html", "test:unit:pretty": "@php vendor/bin/phpunit --order-by=random --colors=always --coverage-html build/coverage-unit/coverage-html",
"test:db": "@parallel test:db:sqlite:ci test:db:mysql test:db:maria test:db:postgres test:db:ms", "test:db": "@parallel test:db:sqlite:ci test:db:mysql test:db:maria test:db:postgres test:db:ms",
"test:db:sqlite": "APP_ENV=test php vendor/bin/phpunit --order-by=random --colors=always --testdox -c phpunit-db.xml", "test:db:sqlite": "APP_ENV=test php vendor/bin/phpunit --order-by=random --colors=always --testdox -c phpunit-db.xml",
"test:db:sqlite:ci": "@test:db:sqlite --coverage-php build/coverage-db.cov --coverage-xml=build/coverage-db/coverage-xml --log-junit=build/coverage-db/junit.xml", "test:db:sqlite:ci": "@test:db:sqlite --coverage-php build/coverage-db.cov --coverage-xml=build/coverage-db/coverage-xml --log-junit=build/coverage-db/junit.xml",
@ -142,15 +142,20 @@
"infect:ci:base": "infection --threads=4 --log-verbosity=default --only-covered --only-covering-test-cases --skip-initial-tests", "infect:ci:base": "infection --threads=4 --log-verbosity=default --only-covered --only-covering-test-cases --skip-initial-tests",
"infect:ci:unit": "@infect:ci:base --coverage=build/coverage-unit --min-msi=83", "infect:ci:unit": "@infect:ci:base --coverage=build/coverage-unit --min-msi=83",
"infect:ci:db": "@infect:ci:base --coverage=build/coverage-db --min-msi=95 --configuration=infection-db.json", "infect:ci:db": "@infect:ci:base --coverage=build/coverage-db --min-msi=95 --configuration=infection-db.json",
"infect:ci": "@parallel infect:ci:unit infect:ci:db", "infect:ci:api": "@infect:ci:base --coverage=build/coverage-api --min-msi=80 --configuration=infection-api.json",
"infect:ci": "@parallel infect:ci:unit infect:ci:db infect:ci:api",
"infect:test": [ "infect:test": [
"@parallel test:unit:ci test:db:sqlite:ci", "@parallel test:unit:ci test:db:sqlite:ci test:api",
"@infect:ci" "@infect:ci"
], ],
"infect:test:unit": [ "infect:test:unit": [
"@test:unit:ci", "@test:unit:ci",
"@infect:ci:unit" "@infect:ci:unit"
], ],
"infect:test:api": [
"@test:api",
"@infect:ci:api"
],
"swagger:validate": "php-openapi validate docs/swagger/swagger.json", "swagger:validate": "php-openapi validate docs/swagger/swagger.json",
"swagger:inline": "php-openapi inline docs/swagger/swagger.json docs/swagger/swagger-inlined.json", "swagger:inline": "php-openapi inline docs/swagger/swagger.json docs/swagger/swagger-inlined.json",
"clean:dev": "rm -f data/database.sqlite && rm -f config/params/generated_config.php" "clean:dev": "rm -f data/database.sqlite && rm -f config/params/generated_config.php"

View file

@ -20,8 +20,7 @@ $config = $container->get('config');
$em = $container->get(EntityManager::class); $em = $container->get(EntityManager::class);
$httpClient = $container->get('shlink_test_api_client'); $httpClient = $container->get('shlink_test_api_client');
// Start code coverage collecting on swoole process, and stop it when process shuts down // Dump code coverage when process shuts down
$httpClient->request('GET', sprintf('http://%s:%s/api-tests/start-coverage', SWOOLE_TESTING_HOST, SWOOLE_TESTING_PORT));
register_shutdown_function(function () use ($httpClient): void { register_shutdown_function(function () use ($httpClient): void {
$httpClient->request( $httpClient->request(
'GET', 'GET',

View file

@ -8,13 +8,16 @@ use GuzzleHttp\Client;
use Laminas\ConfigAggregator\ConfigAggregator; use Laminas\ConfigAggregator\ConfigAggregator;
use Laminas\Diactoros\Response\EmptyResponse; use Laminas\Diactoros\Response\EmptyResponse;
use Laminas\ServiceManager\Factory\InvokableFactory; use Laminas\ServiceManager\Factory\InvokableFactory;
use Laminas\Stdlib\Glob;
use Monolog\Handler\StreamHandler; use Monolog\Handler\StreamHandler;
use Monolog\Logger; use Monolog\Logger;
use PHPUnit\Runner\Version; use PHPUnit\Runner\Version;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use SebastianBergmann\CodeCoverage\CodeCoverage; use SebastianBergmann\CodeCoverage\CodeCoverage;
use SebastianBergmann\CodeCoverage\Driver\Selector; use SebastianBergmann\CodeCoverage\Driver\Selector;
use SebastianBergmann\CodeCoverage\Filter; use SebastianBergmann\CodeCoverage\Filter;
use SebastianBergmann\CodeCoverage\Report\Html\Facade as Html;
use SebastianBergmann\CodeCoverage\Report\PHP; use SebastianBergmann\CodeCoverage\Report\PHP;
use SebastianBergmann\CodeCoverage\Report\Xml\Facade as Xml; use SebastianBergmann\CodeCoverage\Report\Xml\Facade as Xml;
@ -29,9 +32,8 @@ use const ShlinkioTest\Shlink\SWOOLE_TESTING_PORT;
$isApiTest = env('TEST_ENV') === 'api'; $isApiTest = env('TEST_ENV') === 'api';
if ($isApiTest) { if ($isApiTest) {
$filter = new Filter(); $filter = new Filter();
foreach (Glob::glob(__DIR__ . '/../../module/*/src') as $item) { $filter->includeDirectory(__DIR__ . '/../../module/Core/src');
$filter->includeDirectory($item); $filter->includeDirectory(__DIR__ . '/../../module/Rest/src');
}
$coverage = new CodeCoverage((new Selector())->forLineCoverage($filter), $filter); $coverage = new CodeCoverage((new Selector())->forLineCoverage($filter), $filter);
} }
@ -113,26 +115,19 @@ return [
], ],
'routes' => !$isApiTest ? [] : [ 'routes' => !$isApiTest ? [] : [
[
'name' => 'start_collecting_coverage',
'path' => '/api-tests/start-coverage',
'middleware' => middleware(static function () use (&$coverage) {
if ($coverage) { // @phpstan-ignore-line
$coverage->start('API tests');
}
return new EmptyResponse();
}),
'allowed_methods' => ['GET'],
],
[ [
'name' => 'dump_coverage', 'name' => 'dump_coverage',
'path' => '/api-tests/stop-coverage', 'path' => '/api-tests/stop-coverage',
'middleware' => middleware(static function () use (&$coverage) { 'middleware' => middleware(static function () use (&$coverage) {
// TODO I have tried moving this block to a listener so that it's invoked automatically,
// but then the coverage is generated empty ¯\_(ツ)_/¯
if ($coverage) { // @phpstan-ignore-line if ($coverage) { // @phpstan-ignore-line
$basePath = __DIR__ . '/../../build/coverage-api'; $basePath = __DIR__ . '/../../build/coverage-api';
$coverage->stop();
// TODO Generate these coverages dynamically based on CLI options
(new PHP())->process($coverage, $basePath . '.cov'); (new PHP())->process($coverage, $basePath . '.cov');
(new Xml(Version::getVersionString()))->process($coverage, $basePath . '/coverage-xml'); (new Xml(Version::getVersionString()))->process($coverage, $basePath . '/coverage-xml');
(new Html())->process($coverage, $basePath . '/coverage-html');
} }
return new EmptyResponse(); return new EmptyResponse();
@ -141,6 +136,24 @@ return [
], ],
], ],
'middleware_pipeline' => !$isApiTest ? [] : [
'capture_code_coverage' => [
'middleware' => middleware(static function (
ServerRequestInterface $req,
RequestHandlerInterface $handler,
) use (&$coverage): ResponseInterface {
$coverage?->start($req->getHeaderLine('x-coverage-id'));
try {
return $handler->handle($req);
} finally {
$coverage?->stop();
}
}),
'priority' => 9999,
],
],
'mercure' => [ 'mercure' => [
'public_hub_url' => null, 'public_hub_url' => null,
'internal_hub_url' => null, 'internal_hub_url' => null,

23
infection-api.json Normal file
View file

@ -0,0 +1,23 @@
{
"source": {
"directories": [
"module/*/src"
]
},
"timeout": 5,
"logs": {
"text": "build/infection-api/infection-log.txt",
"summary": "build/infection-api/summary-log.txt",
"debug": "build/infection-api/debug-log.txt"
},
"tmpDir": "build/infection-api/temp",
"phpUnit": {
"configDir": "."
},
"testFrameworkOptions": "--configuration=phpunit-api.xml",
"mutators": {
"@default": true,
"IdenticalEqual": false,
"NotIdenticalNotEqual": false
}
}

View file

@ -13,7 +13,8 @@
<coverage processUncoveredFiles="true"> <coverage processUncoveredFiles="true">
<include> <include>
<directory suffix=".php">./module/*/src</directory> <directory suffix=".php">./module/Core/src</directory>
<directory suffix=".php">./module/Rest/src</directory>
</include> </include>
</coverage> </coverage>
</phpunit> </phpunit>