diff --git a/.github/workflows/audit.yml b/.github/workflows/audit.yml index 8c29a01..36d272c 100644 --- a/.github/workflows/audit.yml +++ b/.github/workflows/audit.yml @@ -7,22 +7,12 @@ jobs: name: Audit strategy: matrix: - php-version: [ '8.2', '8.3' ] + php-version: [ '8.2', '8.3', '8.4' ] fail-fast: false runs-on: ubuntu-latest steps: - - name: Checkout (Push) + - name: Checkout uses: actions/checkout@v4 - if: github.event_name == 'push' - with: - fetch-depth: 0 - - - name: Checkout (PR) - uses: actions/checkout@v4 - if: github.event_name == 'pull_request' - with: - ref: ${{ github.event.pull_request.head.ref }} - fetch-depth: 0 - name: Setup PHP uses: shivammathur/setup-php@v2 @@ -54,13 +44,13 @@ jobs: echo "::endgroup::" - name: Auditor - uses: docker://nbgrp/auditor:0.23.1 + uses: docker://nbgrp/auditor:0.29.0 - name: Tests run: vendor/bin/simple-phpunit - name: Codecov - uses: codecov/codecov-action@v3 + uses: codecov/codecov-action@v5 with: token: ${{ secrets.CODECOV_TOKEN }} files: ./clover.xml diff --git a/.php-cs-fixer.php b/.php-cs-fixer.php index 92c604a..a8baffc 100644 --- a/.php-cs-fixer.php +++ b/.php-cs-fixer.php @@ -31,6 +31,9 @@ ], 'comment_to_phpdoc' => [ 'ignored_tags' => [ + 'codeCoverageIgnoreStart', + 'codeCoverageIgnoreEnd', + 'codeCoverageIgnore', 'phan-suppress-current-line', 'phan-suppress-next-line', 'see', diff --git a/composer.json b/composer.json index 398a9b4..577755c 100644 --- a/composer.json +++ b/composer.json @@ -24,6 +24,7 @@ "symfony/http-kernel": "^7" }, "require-dev": { + "phpunit/phpunit": "^11.5", "roave/security-advisories": "dev-latest", "symfony/phpunit-bridge": "^7" }, diff --git a/grumphp.yml b/grumphp.yml index 2de630e..5e9f706 100644 --- a/grumphp.yml +++ b/grumphp.yml @@ -7,6 +7,9 @@ grumphp: enabled: false tasks: + composer: + strict: true + composer_normalize: indent_size: 4 indent_style: 'space' @@ -32,4 +35,4 @@ grumphp: no_cache: true show_info: true - securitychecker_local: ~ + securitychecker_composeraudit: ~ diff --git a/phpcs.xml b/phpcs.xml index 5b845ab..f67f2f4 100644 --- a/phpcs.xml +++ b/phpcs.xml @@ -61,6 +61,7 @@ + */tests/* @@ -185,14 +186,17 @@ - + - + + + + @@ -204,9 +208,9 @@ - + - + diff --git a/phpstan.neon b/phpstan.neon index 057b23c..e896512 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -3,12 +3,12 @@ parameters: paths: - src - tests - bootstrapFiles: - - vendor/bin/.phpunit/phpunit/vendor/autoload.php - - checkMissingIterableValueType: false ignoreErrors: - message: '/Cannot cast mixed to (int|float|string)/' path: 'src/ArrayCastEnvVarProcessor.php' + - + identifier: missingType.iterableValue + - + identifier: phpDoc.parseError diff --git a/phpunit.xml.dist b/phpunit.xml.dist index f16d75a..bcf7869 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,33 +1,40 @@ - + cacheDirectory=".phpunit.cache" + requireCoverageMetadata="true" + beStrictAboutCoverageMetadata="true" + beStrictAboutOutputDuringTests="true" + displayDetailsOnPhpunitDeprecations="true" + failOnPhpunitDeprecation="true" + failOnRisky="true" + failOnWarning="true" +> - - + + - ./tests/ + tests - - src + src - - src/CsvEnvVarProcessor.php - src/NbgroupEnvBundle.php - + + + diff --git a/psalm.xml b/psalm.xml index 5abf3e3..85f7654 100644 --- a/psalm.xml +++ b/psalm.xml @@ -17,24 +17,12 @@ - - - - - - - - - - - - diff --git a/src/ArrayCastEnvVarProcessor.php b/src/ArrayCastEnvVarProcessor.php index 2208a26..da851df 100644 --- a/src/ArrayCastEnvVarProcessor.php +++ b/src/ArrayCastEnvVarProcessor.php @@ -10,6 +10,7 @@ final class ArrayCastEnvVarProcessor implements EnvVarProcessorInterface { + #[\Override] public static function getProvidedTypes(): array { return [ @@ -27,6 +28,7 @@ public static function getProvidedTypes(): array * * @psalm-suppress MixedReturnTypeCoercion */ + #[\Override] public function getEnv(string $prefix, string $name, \Closure $getEnv): array { $env = (array) $getEnv($name); @@ -46,6 +48,7 @@ public function getEnv(string $prefix, string $name, \Closure $getEnv): array */ private static function getBooleanMapper(): callable { + /** @psalm-suppress RiskyTruthyFalsyComparison */ return static fn (mixed $value): bool => (bool) (filter_var($value, \FILTER_VALIDATE_BOOLEAN, ['flags' => \FILTER_NULL_ON_FAILURE]) ?? filter_var($value, \FILTER_VALIDATE_INT) ?: filter_var($value, \FILTER_VALIDATE_FLOAT)); } @@ -55,6 +58,7 @@ private static function getBooleanMapper(): callable private static function getIntegerMapper(string $name): callable { return static function (mixed $value) use ($name): int { + /** @psalm-suppress RiskyTruthyFalsyComparison */ if ((filter_var($value, \FILTER_VALIDATE_INT) ?: filter_var($value, \FILTER_VALIDATE_FLOAT)) === false) { throw new RuntimeException('Non-numeric member of environment variable "'.$name.'" cannot be cast to int.'); } diff --git a/src/CsvEnvVarProcessor.php b/src/CsvEnvVarProcessor.php index 742a466..70dc6ff 100644 --- a/src/CsvEnvVarProcessor.php +++ b/src/CsvEnvVarProcessor.php @@ -9,9 +9,11 @@ use Symfony\Component\DependencyInjection\Exception\BadMethodCallException; use Symfony\Component\DependencyInjection\Exception\RuntimeException; +// @codeCoverageIgnoreStart if (class_exists(CsvEnvVarProcessor::class, false)) { return; } +// @codeCoverageIgnoreEnd final class CsvEnvVarProcessor implements EnvVarProcessorInterface { @@ -26,6 +28,7 @@ public function __construct(array $delimiterMap) $this->delimiterMap = $delimiterMap; } + #[\Override] public static function getProvidedTypes(): array { // NB @@ -36,6 +39,7 @@ public static function getProvidedTypes(): array /** * @return non-empty-list */ + #[\Override] public function getEnv(string $prefix, string $name, \Closure $getEnv): array { if (\array_key_exists($prefix, $this->delimiterMap) === false) { @@ -49,6 +53,7 @@ public function getEnv(string $prefix, string $name, \Closure $getEnv): array throw new RuntimeException('Environment variable "'.$name.'" should be a string.'); } + // @phpstan-ignore greaterOrEqual.alwaysTrue return str_getcsv($env, $delimiter, '"', \PHP_VERSION_ID >= 70400 ? '' : '\\'); } } diff --git a/src/DependencyInjection/NbgroupEnvExtension.php b/src/DependencyInjection/NbgroupEnvExtension.php index 3960711..9461fd2 100644 --- a/src/DependencyInjection/NbgroupEnvExtension.php +++ b/src/DependencyInjection/NbgroupEnvExtension.php @@ -20,11 +20,14 @@ class NbgroupEnvExtension extends Extension { private const NB_REPLACEMENT_MARK = '// NB'; + #[\Override] public function getConfiguration(array $config, ContainerBuilder $container): Configuration { return new Configuration(); } + /** @psalm-suppress MixedArrayAccess, MixedArgument */ + #[\Override] public function load(array $configs, ContainerBuilder $container): void { $configuration = new Configuration(); diff --git a/tests/ArrayCastEnvVarProcessorTest.php b/tests/ArrayCastEnvVarProcessorTest.php index 71b834d..58fc1cb 100644 --- a/tests/ArrayCastEnvVarProcessorTest.php +++ b/tests/ArrayCastEnvVarProcessorTest.php @@ -6,19 +6,18 @@ namespace Nbgrp\Tests\EnvBundle; use Nbgrp\EnvBundle\ArrayCastEnvVarProcessor; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Symfony\Component\DependencyInjection\Exception\RuntimeException; /** - * @covers \Nbgrp\EnvBundle\ArrayCastEnvVarProcessor - * * @internal */ +#[CoversClass(ArrayCastEnvVarProcessor::class)] final class ArrayCastEnvVarProcessorTest extends TestCase { - /** - * @dataProvider provideSuccessCases - */ + #[DataProvider('provideSuccessCases')] public function testSuccess(string $prefix, array $envValue, array $expected): void { $processor = new ArrayCastEnvVarProcessor(); @@ -29,7 +28,7 @@ public function testSuccess(string $prefix, array $envValue, array $expected): v /** * @return \Generator */ - public function provideSuccessCases(): iterable + public static function provideSuccessCases(): iterable { yield 'bool-array' => [ 'bool-array', @@ -74,15 +73,13 @@ public function provideSuccessCases(): iterable ]; } - /** - * @dataProvider provideInvalidNumericCases - */ + #[DataProvider('provideInvalidNumericCases')] public function testInvalidNumeric(string $prefix, array $envValue, string $expectedMessageFormat): void { $processor = new ArrayCastEnvVarProcessor(); $this->expectException(RuntimeException::class); - $this->expectExceptionMessage(sprintf($expectedMessageFormat, 'DM')); + $this->expectExceptionMessage(\sprintf($expectedMessageFormat, 'DM')); $processor->getEnv($prefix, 'DM', static fn (): array => $envValue); } @@ -90,7 +87,7 @@ public function testInvalidNumeric(string $prefix, array $envValue, string $expe /** * @return \Generator */ - public function provideInvalidNumericCases(): iterable + public static function provideInvalidNumericCases(): iterable { yield [ 'int-array', @@ -105,15 +102,13 @@ public function provideInvalidNumericCases(): iterable ]; } - /** - * @dataProvider provideInvalidBase64Cases - */ + #[DataProvider('provideInvalidBase64Cases')] public function testInvalidBase64(string $prefix, array $envValue, string $expectedMessageFormat): void { $processor = new ArrayCastEnvVarProcessor(); $this->expectException(RuntimeException::class); - $this->expectExceptionMessage(sprintf($expectedMessageFormat, 'DM')); + $this->expectExceptionMessage(\sprintf($expectedMessageFormat, 'DM')); $processor->getEnv($prefix, 'DM', static fn (): array => $envValue); } @@ -121,7 +116,7 @@ public function testInvalidBase64(string $prefix, array $envValue, string $expec /** * @return \Generator */ - public function provideInvalidBase64Cases(): iterable + public static function provideInvalidBase64Cases(): iterable { yield [ 'base64-array', diff --git a/tests/DependencyInjection/ConfigurationTest.php b/tests/DependencyInjection/ConfigurationTest.php index 61ed40f..71001b1 100644 --- a/tests/DependencyInjection/ConfigurationTest.php +++ b/tests/DependencyInjection/ConfigurationTest.php @@ -6,23 +6,28 @@ namespace Nbgrp\Tests\EnvBundle\DependencyInjection; use Nbgrp\EnvBundle\DependencyInjection\Configuration; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; use Symfony\Component\Config\Definition\Processor; /** - * @covers \Nbgrp\EnvBundle\DependencyInjection\Configuration - * * @internal */ +#[CoversClass(Configuration::class)] final class ConfigurationTest extends TestCase { /** @var Processor */ private $processor; - /** - * @dataProvider provideValidConfigCases - */ + #[\Override] + protected function setUp(): void + { + $this->processor = new Processor(); + } + + #[DataProvider('provideValidConfigCases')] public function testValidConfig(array $config, array $expected, string $description): void { self::assertSame($expected, $this->processor->processConfiguration(new Configuration(), [$config]), $description); @@ -31,7 +36,7 @@ public function testValidConfig(array $config, array $expected, string $descript /** * @return \Generator */ - public function provideValidConfigCases(): iterable + public static function provideValidConfigCases(): iterable { yield [ [], @@ -104,9 +109,7 @@ public function provideValidConfigCases(): iterable ]; } - /** - * @dataProvider provideInvalidConfigCases - */ + #[DataProvider('provideInvalidConfigCases')] public function testInvalidConfig(array $config, string $expectedMessage): void { $this->expectException(InvalidConfigurationException::class); @@ -117,7 +120,7 @@ public function testInvalidConfig(array $config, string $expectedMessage): void /** * @return \Generator */ - public function provideInvalidConfigCases(): iterable + public static function provideInvalidConfigCases(): iterable { yield [ [ @@ -135,9 +138,4 @@ public function provideInvalidConfigCases(): iterable 'Delimiter should be one character only.', ]; } - - protected function setUp(): void - { - $this->processor = new Processor(); - } } diff --git a/tests/DependencyInjection/NbgroupEnvExtensionTest.php b/tests/DependencyInjection/NbgroupEnvExtensionTest.php index 9a49694..b1c0697 100644 --- a/tests/DependencyInjection/NbgroupEnvExtensionTest.php +++ b/tests/DependencyInjection/NbgroupEnvExtensionTest.php @@ -5,15 +5,19 @@ namespace Nbgrp\Tests\EnvBundle\DependencyInjection; +use Nbgrp\EnvBundle\ArrayCastEnvVarProcessor; +use Nbgrp\EnvBundle\DependencyInjection\Configuration; use Nbgrp\EnvBundle\DependencyInjection\NbgroupEnvExtension; +use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; use Symfony\Component\DependencyInjection\ContainerBuilder; /** - * @covers \Nbgrp\EnvBundle\DependencyInjection\NbgroupEnvExtension - * * @internal */ +#[CoversClass(NbgroupEnvExtension::class)] +#[CoversClass(ArrayCastEnvVarProcessor::class)] +#[CoversClass(Configuration::class)] final class NbgroupEnvExtensionTest extends TestCase { public function testComplexUsage(): void diff --git a/tests/ExplodeEnvVarProcessorTest.php b/tests/ExplodeEnvVarProcessorTest.php index 2196e3b..41e57b9 100644 --- a/tests/ExplodeEnvVarProcessorTest.php +++ b/tests/ExplodeEnvVarProcessorTest.php @@ -6,18 +6,20 @@ namespace Nbgrp\Tests\EnvBundle; use Nbgrp\EnvBundle\CsvEnvVarProcessor; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; /** - * @covers \Nbgrp\EnvBundle\CsvEnvVarProcessor - * * @internal */ +#[CoversClass(CsvEnvVarProcessor::class)] final class ExplodeEnvVarProcessorTest extends TestCase { /** - * @dataProvider provideSuccessCases + * @param array $delimiterMap */ + #[DataProvider('provideSuccessCases')] public function testSuccess(array $delimiterMap, string $prefix, string $envValue, array $expected): void { $processor = new CsvEnvVarProcessor($delimiterMap); @@ -28,7 +30,7 @@ public function testSuccess(array $delimiterMap, string $prefix, string $envValu /** * @return \Generator}> */ - public function provideSuccessCases(): iterable + public static function provideSuccessCases(): iterable { $delimiterMap = [ 'csv-dot' => '.', @@ -69,9 +71,10 @@ public function provideSuccessCases(): iterable $delimiterMap, 'csv-dot', '"\".".\.."\""".\.', + // @phpstan-ignore greaterOrEqual.alwaysTrue \PHP_VERSION_ID >= 70400 - ? ['\\', '.\\..\\"""', '\\', ''] - : ['\\".', '\\', '', '\"".\\.'], + ? ['\\', '.\..\"""', '\\', ''] + : ['\".', '\\', '', '\"".\.'], ]; } }