diff --git a/README.md b/README.md index 84b8d2f..1d415d9 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,12 @@ return [ php bin/console ferror:check-openapi-coverage ``` +> You can also specify the --threshold or (--t) option to define coverage level below which the command will fail. + +```bash +php bin/console ferror:check-openapi-coverage --threshold 0.70 +``` + ## Example Result ``` diff --git a/src/Coverage.php b/src/Coverage.php index 1c4741a..fa29c88 100644 --- a/src/Coverage.php +++ b/src/Coverage.php @@ -4,14 +4,29 @@ namespace Ferror\OpenapiCoverage; +use InvalidArgumentException; + final readonly class Coverage { public function __construct(public float $value) { + if ($value < 0) { + throw new InvalidArgumentException('Coverage value cannot be negative number'); + } } public function asPercentage(): float { return round($this->value * 100, 2); } + + public function isGreater(self $self): bool + { + return $this->value > $self->value; + } + + public function isLower(self $self): bool + { + return $this->value < $self->value; + } } diff --git a/src/Symfony/Console/CheckCoverageCommand.php b/src/Symfony/Console/CheckCoverageCommand.php index 648a8be..6dbc117 100644 --- a/src/Symfony/Console/CheckCoverageCommand.php +++ b/src/Symfony/Console/CheckCoverageCommand.php @@ -4,6 +4,7 @@ namespace Ferror\OpenapiCoverage\Symfony\Console; +use Ferror\OpenapiCoverage\Coverage; use Ferror\OpenapiCoverage\RouteCollection; use Ferror\OpenapiCoverage\CoverageCalculator; use Ferror\OpenapiCoverage\Route; @@ -12,6 +13,7 @@ use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Helper\Table; use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Routing\RouterInterface; @@ -25,6 +27,7 @@ public function __construct( private readonly string $prefix = '/v1', ) { parent::__construct('ferror:check-openapi-coverage'); + $this->addOption('threshold', 't', InputOption::VALUE_OPTIONAL); } public function execute(InputInterface $input, OutputInterface $output): int @@ -78,8 +81,9 @@ public function execute(InputInterface $input, OutputInterface $output): int } $coverageCalculator = new CoverageCalculator($paths->count(), $openApiPaths->count() - $notExistingPaths->count()); + $coverage = $coverageCalculator->calculate(); - $output->writeln('Open API coverage: ' . $coverageCalculator->calculate()->asPercentage() . '%'); + $output->writeln('Open API coverage: ' . $coverage->asPercentage() . '%'); if ($missingPaths->count() === 0) { $output->writeln('OpenAPI schema covers all Symfony routes. Good job!'); @@ -95,6 +99,12 @@ public function execute(InputInterface $input, OutputInterface $output): int $table->render(); } + if ($threshold = $input->getOption('threshold')) { + if ($coverage->isLower(new Coverage($threshold))) { + return Command::FAILURE; + } + } + return Command::SUCCESS; } } diff --git a/tests/Integration/CheckCoverageCommandTest.php b/tests/Integration/CheckCoverageCommandTest.php index 33d1f46..87d3d1b 100644 --- a/tests/Integration/CheckCoverageCommandTest.php +++ b/tests/Integration/CheckCoverageCommandTest.php @@ -6,6 +6,7 @@ use Symfony\Bundle\FrameworkBundle\Console\Application; use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; +use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Tester\CommandTester; class CheckCoverageCommandTest extends KernelTestCase @@ -35,4 +36,28 @@ public function testExecuteClass(): void $this->assertEquals($expectedDisplay, $display); } + + public function testPositiveThreshold(): void + { + $kernel = self::bootKernel(); + $application = new Application($kernel); + + $command = $application->find('ferror:check-openapi-coverage'); + $commandTester = new CommandTester($command); + $commandTester->execute(['--threshold' => 0.70]); + + $commandTester->assertCommandIsSuccessful(); + } + + public function testNegativeThreshold(): void + { + $kernel = self::bootKernel(); + $application = new Application($kernel); + + $command = $application->find('ferror:check-openapi-coverage'); + $commandTester = new CommandTester($command); + $commandTester->execute(['--threshold' => 0.80]); + + $this->assertEquals(Command::FAILURE, $commandTester->getStatusCode()); + } } diff --git a/tests/Unit/CoverageTest.php b/tests/Unit/CoverageTest.php new file mode 100644 index 0000000..28bc37b --- /dev/null +++ b/tests/Unit/CoverageTest.php @@ -0,0 +1,43 @@ +expectException(InvalidArgumentException::class); + + new Coverage(-1); + } + + public function testIsGreater(): void + { + $coverage = new Coverage(50.0); + + $this->assertTrue($coverage->isGreater(new Coverage(40.0))); + $this->assertTrue($coverage->isGreater(new Coverage(10.0))); + + $this->assertFalse($coverage->isGreater(new Coverage(50.0))); + $this->assertFalse($coverage->isGreater(new Coverage(60.0))); + $this->assertFalse($coverage->isGreater(new Coverage(100.0))); + } + + public function testIsLower(): void + { + $coverage = new Coverage(50.0); + + $this->assertFalse($coverage->isLower(new Coverage(40.0))); + $this->assertFalse($coverage->isLower(new Coverage(10.0))); + $this->assertFalse($coverage->isLower(new Coverage(50.0))); + + $this->assertTrue($coverage->isLower(new Coverage(60.0))); + $this->assertTrue($coverage->isLower(new Coverage(100.0))); + } +}