diff --git a/.github/workflows/tests.yml b/.github/workflows/checks.yml similarity index 66% rename from .github/workflows/tests.yml rename to .github/workflows/checks.yml index bfcb8d5..8d290d6 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/checks.yml @@ -1,4 +1,4 @@ -name: Pull Request +name: Checks on: push: @@ -25,7 +25,7 @@ jobs: - php_version: 8.3 laravel_version: 11 - name: PHP ${{ matrix.environment.php_version }} & Laravel ${{ matrix.environment.laravel_version }} - ubuntu-latest + name: PHP ${{ matrix.environment.php_version }} & Laravel ${{ matrix.environment.laravel_version }} steps: - name: Checkout code @@ -38,4 +38,21 @@ jobs: laravel_version: ${{ matrix.environment.laravel_version }} - name: Execute tests - run: vendor/bin/phpunit + run: composer test + + php-codesniffer: + runs-on: ubuntu-latest + name: PHP Codesniffer + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup + uses: ./.github/actions/setup + with: + php_version: 8.3 + laravel_version: 11 + + - name: Run codesniffer + run: composer phpcs diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0172869..48a3efe 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,14 +1,12 @@ -CONTRIBUTING -============ +# CONTRIBUTING + Contributions are welcome and we look forward to seeing what you can bring to the project. We do reserve the right to reject changes that we feel will not benefit users or take the project in a direction we do not wish to take. We recommend discussing any large changes with us prior to making your changes, you can do this via an issue or email if you prefer. - -Issues ------- +## Issues Issues should include a title and clear description with as much relevant information as possible. @@ -16,9 +14,7 @@ If you'd like to discuss a potential change, you can open an issue or reach out If you think you have identified a security vulnerability, please contact our team via **security@ukfast.co.uk** instead of using the issue tracker. - -Submitting Changes ------------------- +## Submitting Changes Before submitting your [pull request](https://help.github.com/en/articles/about-pull-requests), please make sure that the coding standards are respected and that all tests are passing. @@ -30,14 +26,25 @@ but larger changes should follow [The Seven Rules](https://chris.beams.io/posts/ We don't mind squashing small changes to tidy things up but please don't squash an entire branch as this can hinder code reviews. +## Coding Standards + +### All Standards + +All coding standards can be checked by running `composer standards:check` and any issues which can be automatically +resolved can be fixed by running `composer standards:fix`. -Coding Standards ---------------- +### PSR12 This project adheres to the [PSR-12 Coding Standard](https://www.php-fig.org/psr/psr-12/). +#### PHP CodeSniffer && PHP Code Beautifier + +This can be checked with `composer phpcs` and issues which can be fixed automatically can be fixed by running `composer phpcs:fix`. + + +## Testing -Testing -------- Please ensure all new functionality is matched with appropriate tests, we will advise if we feel more is needed. + +Tests can be run with `composer test`. diff --git a/composer.json b/composer.json index d85ef8e..a438f40 100644 --- a/composer.json +++ b/composer.json @@ -63,13 +63,14 @@ "league/flysystem-ftp": "^3.28" }, "require-dev": { - "phpunit/phpunit": "^10.0|^11.0", - "orchestra/testbench": "^8.0|^9.0", - "mockery/mockery": "^1.0", + "guzzlehttp/guzzle": "^7.5", "illuminate/database": "^10.0|^11.0", "illuminate/log": "^10.0|^11.0", "illuminate/redis": "^10.0|^11.0", - "guzzlehttp/guzzle": "^7.5" + "mockery/mockery": "^1.0", + "orchestra/testbench": "^8.0|^9.0", + "phpunit/phpunit": "^10.0|^11.0", + "squizlabs/php_codesniffer": "^3.10" }, "suggest": { "illuminate/database": "Allows the database health check to function.", @@ -79,5 +80,16 @@ "league/flysystem-ftp": "Allows the ftp health check to function.", "guzzlehttp/guzzle": "Allows the http health check to function.", "enlightn/security-checker": "Allows the package security health check to function." + }, + "scripts": { + "standards:check": [ + "@phpcs" + ], + "standards:fix": [ + "@phpcs:fix" + ], + "test": "phpunit", + "phpcs": "./vendor/bin/phpcs --standard=PSR12 --ignore=vendor .", + "phpcs:fix": "./vendor/bin/phpcbf --standard=PSR12 --ignore=vendor ." } } diff --git a/src/AppHealth.php b/src/AppHealth.php index ccc60c0..985bea2 100644 --- a/src/AppHealth.php +++ b/src/AppHealth.php @@ -8,7 +8,6 @@ class AppHealth { - public function __construct( /** * @var Collection diff --git a/src/Checks/LogHealthCheck.php b/src/Checks/LogHealthCheck.php index 9715e6d..3ab029a 100644 --- a/src/Checks/LogHealthCheck.php +++ b/src/Checks/LogHealthCheck.php @@ -35,4 +35,4 @@ public function status(): Status return $this->okay(); } -} \ No newline at end of file +} diff --git a/src/Checks/PackageSecurityHealthCheck.php b/src/Checks/PackageSecurityHealthCheck.php index a695e8c..979faf9 100644 --- a/src/Checks/PackageSecurityHealthCheck.php +++ b/src/Checks/PackageSecurityHealthCheck.php @@ -35,7 +35,10 @@ public function status(): Status try { if (! static::checkDependency(SecurityChecker::class)) { if (static::checkDependency('SensioLabs\Security\SecurityChecker')) { - throw new Exception('The sensiolabs/security-checker package has been archived. Install enlightn/security-checker instead.'); + throw new Exception( + 'The sensiolabs/security-checker package has been archived.' + . ' Install enlightn/security-checker instead.' + ); } throw new Exception('You need to install the enlightn/security-checker package to use this check.'); } diff --git a/src/HealthCheck.php b/src/HealthCheck.php index 786c5c9..82e30e0 100644 --- a/src/HealthCheck.php +++ b/src/HealthCheck.php @@ -17,7 +17,7 @@ public function name(): string public function problem($message = '', $context = []): Status { - return (new Status) + return (new Status()) ->problem($message) ->withContext($context) ->withName($this->name()); @@ -25,7 +25,7 @@ public function problem($message = '', $context = []): Status public function degraded($message = '', $context = []): Status { - return (new Status) + return (new Status()) ->degraded($message) ->withContext($context) ->withName($this->name()); @@ -33,7 +33,7 @@ public function degraded($message = '', $context = []): Status public function okay($context = []): Status { - return (new Status) + return (new Status()) ->okay() ->withContext($context) ->withName($this->name()); diff --git a/src/HealthCheckServiceProvider.php b/src/HealthCheckServiceProvider.php index ddf6b0b..91d0320 100644 --- a/src/HealthCheckServiceProvider.php +++ b/src/HealthCheckServiceProvider.php @@ -15,11 +15,12 @@ public function boot(): void { $this->configure(); - $this->app->make('router')->get($this->withBasePath(config('healthcheck.route-paths.health', '/health')), [ - 'middleware' => config('healthcheck.middleware'), - 'uses' => HealthCheckController::class, - 'as' => config('healthcheck.route-name') - ]); + $this->app->make('router') + ->get($this->withBasePath(config('healthcheck.route-paths.health', '/health')), [ + 'middleware' => config('healthcheck.middleware'), + 'uses' => HealthCheckController::class, + 'as' => config('healthcheck.route-name') + ]); $this->app->bind('app-health', function ($app) { $checks = collect(); @@ -38,16 +39,17 @@ public function boot(): void ]); } - $this->app->make('router')->get($this->withBasePath(config('healthcheck.route-paths.ping', '/ping')), PingController::class); + $this->app->make('router') + ->get($this->withBasePath(config('healthcheck.route-paths.ping', '/ping')), PingController::class); } protected function configure(): void { - $this->mergeConfigFrom(__DIR__.'/../config/healthcheck.php', 'healthcheck'); - $configPath = $this->app->basePath().'/config/healthcheck.php'; + $this->mergeConfigFrom(__DIR__ . '/../config/healthcheck.php', 'healthcheck'); + $configPath = $this->app->basePath() . '/config/healthcheck.php'; $this->publishes([ - __DIR__.'/../config/healthcheck.php' => $configPath, + __DIR__ . '/../config/healthcheck.php' => $configPath, ], 'config'); if ($this->app instanceof \Laravel\Lumen\Application) { diff --git a/src/Status.php b/src/Status.php index 978822f..ba3e269 100644 --- a/src/Status.php +++ b/src/Status.php @@ -4,11 +4,11 @@ class Status { - const PROBLEM = 'PROBLEM'; + public const PROBLEM = 'PROBLEM'; - const DEGRADED = 'DEGRADED'; + public const DEGRADED = 'DEGRADED'; - const OKAY = 'OK'; + public const OKAY = 'OK'; protected string|null $status = null; diff --git a/tests/AppHealthTest.php b/tests/AppHealthTest.php index c20bd23..1e171c9 100644 --- a/tests/AppHealthTest.php +++ b/tests/AppHealthTest.php @@ -2,17 +2,17 @@ namespace Tests; -use RuntimeException; +use Tests\Stubs\Checks\AlwaysDownCheck; +use Tests\Stubs\Checks\AlwaysUpCheck; +use Tests\Stubs\Checks\UnreliableCheck; use UKFast\HealthCheck\AppHealth; use UKFast\HealthCheck\Exceptions\CheckNotFoundException; -use UKFast\HealthCheck\HealthCheck; -use UKFast\HealthCheck\Status; class AppHealthTest extends TestCase { public function testCanSeeIfACheckPassesByName(): void { - $appHealth = new AppHealth(collect([new AlwaysUpCheck, new AlwaysDownCheck])); + $appHealth = new AppHealth(collect([new AlwaysUpCheck(), new AlwaysDownCheck()])); $this->assertTrue($appHealth->passes('always-up')); $this->assertFalse($appHealth->passes('always-down')); @@ -20,7 +20,7 @@ public function testCanSeeIfACheckPassesByName(): void public function testCanSeeIfACheckFailsByName(): void { - $appHealth = new AppHealth(collect([new AlwaysUpCheck, new AlwaysDownCheck])); + $appHealth = new AppHealth(collect([new AlwaysUpCheck(), new AlwaysDownCheck()])); $this->assertFalse($appHealth->fails('always-up')); $this->assertTrue($appHealth->fails('always-down')); @@ -28,53 +28,17 @@ public function testCanSeeIfACheckFailsByName(): void public function testReturnsFalseIfCheckThrowsException(): void { - $appHealth = new AppHealth(collect([new UnreliableCheck])); + $appHealth = new AppHealth(collect([new UnreliableCheck()])); $this->assertFalse($appHealth->passes('unreliable')); } public function testThrowsExceptionIfCheckDoesNotExist(): void { - $appHealth = new AppHealth(collect([new AlwaysUpCheck, new AlwaysDownCheck])); + $appHealth = new AppHealth(collect([new AlwaysUpCheck(), new AlwaysDownCheck()])); $this->expectException(CheckNotFoundException::class); $appHealth->passes('does-not-exist'); } } - -class AlwaysUpCheck extends HealthCheck -{ - protected string $name = 'always-up'; - - public function status(): Status - { - return $this->okay(); - } -} - -class AlwaysDownCheck extends HealthCheck -{ - protected string $name = 'always-down'; - - public function status(): Status - { - return $this->problem('Something went wrong', [ - 'debug' => 'info', - ]); - } -} - - -class UnreliableCheck extends HealthCheck -{ - protected string $name = 'unreliable'; - - /** - * @throws RuntimeException - */ - public function status(): never - { - throw new RuntimeException('Something went badly wrong'); - } -} \ No newline at end of file diff --git a/tests/Checks/CacheHealthCheckTest.php b/tests/Checks/CacheHealthCheckTest.php index c0db0bf..cc8bfe9 100644 --- a/tests/Checks/CacheHealthCheckTest.php +++ b/tests/Checks/CacheHealthCheckTest.php @@ -2,8 +2,8 @@ namespace Tests\Checks; -use Exception; use Illuminate\Foundation\Application; +use Tests\Stubs\Cache\BadStore; use Tests\TestCase; use Illuminate\Support\Facades\Cache; use UKFast\HealthCheck\Checks\CacheHealthCheck; @@ -65,14 +65,3 @@ public function testShowsOkayIfCanWriteToCache(): void $this->assertTrue($status->isOkay()); } } - -class BadStore -{ - /** - * @throws Exception - */ - public function __call($name, $arguments): never - { - throw new Exception(); - } -} \ No newline at end of file diff --git a/tests/Checks/DatabaseHealthCheckTest.php b/tests/Checks/DatabaseHealthCheckTest.php index 62bde15..733cc88 100644 --- a/tests/Checks/DatabaseHealthCheckTest.php +++ b/tests/Checks/DatabaseHealthCheckTest.php @@ -2,12 +2,10 @@ namespace Tests\Checks; -use Exception; -use Illuminate\Database\Connection; -use Illuminate\Database\ConnectionResolver; -use Illuminate\Database\DatabaseManager as IlluminateDatabaseManager; use Illuminate\Foundation\Application; -use InvalidArgumentException; +use Tests\Stubs\Database\BadConnection; +use Tests\Stubs\Database\DatabaseManager; +use Tests\Stubs\Database\HealthyConnection; use Tests\TestCase; use UKFast\HealthCheck\Checks\DatabaseHealthCheck; use UKFast\HealthCheck\HealthCheckServiceProvider; @@ -29,8 +27,8 @@ public function testShowsProblemWhenCantConnectToDb(): void 'healthcheck.database.connections' => ['default'], ]); - $db = new DatabaseManager; - $db->addConnection('default', new BadConnection); + $db = new DatabaseManager(); + $db->addConnection('default', new BadConnection()); $status = (new DatabaseHealthCheck($db))->status(); @@ -43,8 +41,8 @@ public function testShowsOkayWhenCanConnectToDb(): void 'healthcheck.database.connections' => ['default'], ]); - $db = new DatabaseManager; - $db->addConnection('default', new HealthyConnection); + $db = new DatabaseManager(); + $db->addConnection('default', new HealthyConnection()); $status = (new DatabaseHealthCheck($db))->status(); @@ -57,9 +55,9 @@ public function testShowsWhichConnectionFailed(): void 'healthcheck.database.connections' => ['healthy', 'bad'], ]); - $db = new DatabaseManager; - $db->addConnection('healthy', new HealthyConnection); - $db->addConnection('bad', new BadConnection); + $db = new DatabaseManager(); + $db->addConnection('healthy', new HealthyConnection()); + $db->addConnection('bad', new BadConnection()); $status = (new DatabaseHealthCheck($db))->status(); @@ -67,60 +65,3 @@ public function testShowsWhichConnectionFailed(): void $this->assertSame('bad', $status->context()['connection']); } } - -class HealthyConnection extends Connection -{ - public function __construct() - { - } - - public function getPdo(): bool - { - return true; - } -} - -class BadConnection extends Connection -{ - public function __construct() - { - } - - /** - * @throws Exception - */ - public function getPdo(): never - { - throw new Exception; - } -} - -class DatabaseManager extends IlluminateDatabaseManager -{ - protected $connections = []; - - public function __construct() - { - } - - /** - * @throws InvalidArgumentException - */ - public function connection($name = null) - { - if (!$name) { - return $this->connection('default'); - } - - if (!isset($this->connections[$name])) { - throw new InvalidArgumentException("Database [$name] not configured."); - } - - return $this->connections[$name]; - } - - public function addConnection($name, $connection) - { - $this->connections[$name] = $connection; - } -} diff --git a/tests/Checks/EnvHealthCheckTest.php b/tests/Checks/EnvHealthCheckTest.php index 4e14547..a44e113 100644 --- a/tests/Checks/EnvHealthCheckTest.php +++ b/tests/Checks/EnvHealthCheckTest.php @@ -27,11 +27,12 @@ public function testShowsProblemIfMissingADotenvFile(): void 'REDIS_HOST', 'MYSQL_PASSWORD' ]]); - $status = (new EnvHealthCheck)->status(); + $status = (new EnvHealthCheck())->status(); $this->assertTrue($status->isProblem()); } - function testShowsOkayIfAllRequiredEnvParamsArePresent(): void + + public function testShowsOkayIfAllRequiredEnvParamsArePresent(): void { putenv('REDIS_HOST=here'); putenv('MYSQL_HOST=here'); @@ -41,9 +42,9 @@ function testShowsOkayIfAllRequiredEnvParamsArePresent(): void 'REDIS_HOST', 'MYSQL_PASSWORD' ]]); - $status = (new EnvHealthCheck)->status(); + $status = (new EnvHealthCheck())->status(); - $this->assertTrue($status->isOkay()); + $this->assertTrue($status->isOkay()); } public function testShowsOkayIfRequiredEnvParamIsPresentButNull(): void @@ -53,7 +54,7 @@ public function testShowsOkayIfRequiredEnvParamIsPresentButNull(): void config(['healthcheck.required-env' => [ 'REDIS_PASSWORD', ]]); - $status = (new EnvHealthCheck)->status(); + $status = (new EnvHealthCheck())->status(); $this->assertTrue($status->isOkay()); } diff --git a/tests/Checks/FtpHealthCheckTest.php b/tests/Checks/FtpHealthCheckTest.php index 0281080..3aad6db 100644 --- a/tests/Checks/FtpHealthCheckTest.php +++ b/tests/Checks/FtpHealthCheckTest.php @@ -26,11 +26,12 @@ public function testShowsProblemWhenCantConnectToFtpServer(): void public function testShowsOkayWhenCanConnectToFtpServer(): void { - function generator(): iterable { + function generator(): iterable + { yield 'foo'; yield 'bar'; yield 'baz'; - }; + } $ftp = Mockery::mock(FtpAdapter::class) ->expects('listContents') diff --git a/tests/Checks/HttpHealthCheckTest.php b/tests/Checks/HttpHealthCheckTest.php index 10cc35b..d0656a3 100644 --- a/tests/Checks/HttpHealthCheckTest.php +++ b/tests/Checks/HttpHealthCheckTest.php @@ -3,7 +3,10 @@ namespace Tests\Checks; use GuzzleHttp\Client; +use GuzzleHttp\Exception\ConnectException; +use GuzzleHttp\Exception\TooManyRedirectsException; use GuzzleHttp\Handler\MockHandler; +use GuzzleHttp\Psr7\Request; use Illuminate\Foundation\Application; use Tests\TestCase; use UKFast\HealthCheck\Checks\HttpHealthCheck; @@ -80,7 +83,7 @@ public function testShowsProblemOnConnectException(): void $this->app->bind(Client::class, function ($app, $args) { $exceptions = [ - (new \GuzzleHttp\Exception\ConnectException('Connection refused', new \GuzzleHttp\Psr7\Request('GET', 'test'))), + (new ConnectException('Connection refused', new Request('GET', 'test'))), ]; $mockHandler = new MockHandler($exceptions); @@ -104,7 +107,7 @@ public function testShowsProblemOnGeneralException(): void $this->app->bind(Client::class, function ($app, $args) { $exceptions = [ - (new \GuzzleHttp\Exception\TooManyRedirectsException('Will not follow more than 5 redirects', new \GuzzleHttp\Psr7\Request('GET', 'test'))), + (new TooManyRedirectsException('Will not follow more than 5 redirects', new Request('GET', 'test'))), ]; $mockHandler = new MockHandler($exceptions); diff --git a/tests/Checks/LogHealthCheckTest.php b/tests/Checks/LogHealthCheckTest.php index e4c4fde..816f702 100644 --- a/tests/Checks/LogHealthCheckTest.php +++ b/tests/Checks/LogHealthCheckTest.php @@ -3,11 +3,10 @@ namespace Tests\Checks; use Illuminate\Foundation\Application; -use Psr\Log\LoggerInterface; -use Stringable; +use Tests\Stubs\Log\BadLogger; +use Tests\Stubs\Log\NullLogger; use Tests\TestCase; use UKFast\HealthCheck\Checks\LogHealthCheck; -use Exception; use UKFast\HealthCheck\HealthCheckServiceProvider; class LogHealthCheckTest extends TestCase @@ -24,7 +23,7 @@ public function getPackageProviders($app): array public function testShowsProblemIfCannotWriteToLogs(): void { $this->app->bind('log', function () { - return new BadLogger; + return new BadLogger(); }); $status = (new LogHealthCheck($this->app))->status(); @@ -34,126 +33,10 @@ public function testShowsProblemIfCannotWriteToLogs(): void public function testShowsOkayIfCanWriteToLogs(): void { $this->app->bind('log', function () { - return new NullLogger; + return new NullLogger(); }); $status = (new LogHealthCheck($this->app))->status(); $this->assertTrue($status->isOkay()); } } - -class BadLogger implements LoggerInterface -{ - /** - * @throws Exception - */ - public function emergency(Stringable | string $message, array $context = []): void - { - throw new Exception('Failed to log'); - } - - /** - * @throws Exception - */ - public function alert(Stringable | string $message, array $context = []): void - { - throw new Exception('Failed to log'); - } - - /** - * @throws Exception - */ - public function critical(Stringable | string $message, array $context = []): void - { - throw new Exception('Failed to log'); - } - - /** - * @throws Exception - */ - public function error(Stringable | string $message, array $context = []): void - { - throw new Exception('Failed to log'); - } - - /** - * @throws Exception - */ - public function warning(Stringable | string $message, array $context = []): void - { - throw new Exception('Failed to log'); - } - - /** - * @throws Exception - */ - public function notice(Stringable | string $message, array $context = []): void - { - throw new Exception('Failed to log'); - } - - /** - * @throws Exception - */ - public function info(Stringable | string $message, array $context = []): void - { - throw new Exception('Failed to log'); - } - - /** - * @throws Exception - */ - public function debug(Stringable | string $message, array $context = []): void - { - throw new Exception('Failed to log'); - } - - /** - * @throws Exception - */ - public function log($level, Stringable | string $message, array $context = []): void - { - throw new Exception('Failed to log'); - } -} - -class NullLogger implements LoggerInterface -{ - - public function emergency(Stringable | string $message, array $context = []): void - { - } - - public function alert(Stringable | string $message, array $context = []): void - { - } - - public function critical(Stringable | string $message, array $context = []): void - { - } - - public function error(Stringable | string $message, array $context = []): void - { - } - - public function warning(Stringable | string $message, array $context = []): void - { - } - - public function notice(Stringable | string $message, array $context = []): void - { - // TODO: Implement notice() method. - } - - public function info(Stringable | string $message, array $context = []): void - { - } - - public function debug(Stringable | string $message, array $context = []): void - { - } - - public function log($level, Stringable | string $message, array $context = []): void - { - } -} diff --git a/tests/Checks/MigrationUpToDateHealthCheckTest.php b/tests/Checks/MigrationUpToDateHealthCheckTest.php index beccedf..5c0c2ea 100644 --- a/tests/Checks/MigrationUpToDateHealthCheckTest.php +++ b/tests/Checks/MigrationUpToDateHealthCheckTest.php @@ -60,7 +60,6 @@ public function testCanReturnFalseWhenSchemaIsOutdated(): void $status = $this->healthCheck->status(); $this->assertFalse($status->isOkay()); $this->assertSame(['pending_migrations' => ['missing_migration.php']], $status->context()); - } public function testCanReturnFalseWhenRanMigrationCouldNotBeRetrieved(): void diff --git a/tests/Checks/PackageSecurityHealthCheckTest.php b/tests/Checks/PackageSecurityHealthCheckTest.php index dc2cebc..4f8e2ed 100644 --- a/tests/Checks/PackageSecurityHealthCheckTest.php +++ b/tests/Checks/PackageSecurityHealthCheckTest.php @@ -7,6 +7,7 @@ use Illuminate\Foundation\Application; use Mockery; use Mockery\MockInterface; +use Tests\Stubs\Checks\PackageSecurityHealthCheck as StubPackageSecurityHealthCheck; use Tests\TestCase; use UKFast\HealthCheck\Checks\PackageSecurityHealthCheck; use UKFast\HealthCheck\HealthCheckServiceProvider; @@ -36,31 +37,36 @@ protected function partialMock($abstract, Closure $mock = null): MockInterface public function testShowsProblemIfRequiredPackageNotLoaded() { - $status = (new MockedPackageSecurityHealthCheck)->status(); + $status = (new StubPackageSecurityHealthCheck())->status(); $this->assertTrue($status->isProblem()); - $this->assertSame('You need to install the enlightn/security-checker package to use this check.', $status->context()['exception']['error']); + $this->assertSame( + 'You need to install the enlightn/security-checker package to use this check.', + $status->context()['exception']['error'] + ); } public function testShowsProblemIfIncorrectPackageLoaded(): void { - MockedPackageSecurityHealthCheck::$classResults = [ + StubPackageSecurityHealthCheck::$classResults = [ 'Enlightn\SecurityChecker\SecurityChecker' => false, 'SensioLabs\Security\SecurityChecker' => true, ]; - $status = (new MockedPackageSecurityHealthCheck)->status(); + $status = (new StubPackageSecurityHealthCheck())->status(); $this->assertTrue($status->isProblem()); - $this->assertSame('The sensiolabs/security-checker package has been archived. Install enlightn/security-checker instead.', $status->context()['exception']['error']); + $this->assertSame( + 'The sensiolabs/security-checker package has been archived. Install enlightn/security-checker instead.', + $status->context()['exception']['error'] + ); } public function testShowsProblemIfCannotCheckPackages(): void { $this->partialMock('overload:Enlightn\SecurityChecker\SecurityChecker', fn (MockInterface $mock) => - $mock->shouldReceive('check')->andThrow(new Exception('File not found at [/tmp/composer.lock]')) - ); + $mock->shouldReceive('check')->andThrow(new Exception('File not found at [/tmp/composer.lock]'))); - $status = (new PackageSecurityHealthCheck)->status(); + $status = (new PackageSecurityHealthCheck())->status(); $this->assertTrue($status->isProblem()); } @@ -69,10 +75,12 @@ public function testShowsProblemIfPackageHasVulnerability(): void { $this->partialMock('overload:Enlightn\SecurityChecker\SecurityChecker', fn (MockInterface $mock) => $mock->shouldReceive('check') - ->andReturn(json_decode(file_get_contents('tests/json/securityCheckerPackageHasVulnerability.json'), true)) - ); + ->andReturn(json_decode( + file_get_contents('tests/json/securityCheckerPackageHasVulnerability.json'), + true + ))); - $status = (new PackageSecurityHealthCheck)->status(); + $status = (new PackageSecurityHealthCheck())->status(); $this->assertTrue($status->isProblem()); } @@ -87,10 +95,12 @@ public function testIgnoresPackageIfInConfig(): void $this->partialMock('overload:Enlightn\SecurityChecker\SecurityChecker', fn (MockInterface $mock) => $mock->shouldReceive('check') - ->andReturn(json_decode(file_get_contents('tests/json/securityCheckerPackageHasVulnerability.json'), true)) - ); + ->andReturn(json_decode( + file_get_contents('tests/json/securityCheckerPackageHasVulnerability.json'), + true + ))); - $status = (new PackageSecurityHealthCheck)->status(); + $status = (new PackageSecurityHealthCheck())->status(); $this->assertTrue($status->isOkay()); } @@ -99,27 +109,10 @@ public function testShowsOkayIfNoPackagesHaveVulnerabilities(): void { $this->partialMock('overload:Enlightn\SecurityChecker\SecurityChecker', fn(MockInterface $mock) => $mock->shouldReceive('check') - ->andReturn([]) - ); + ->andReturn([])); - $status = (new PackageSecurityHealthCheck)->status(); + $status = (new PackageSecurityHealthCheck())->status(); $this->assertTrue($status->isOkay()); } } - -class MockedPackageSecurityHealthCheck extends PackageSecurityHealthCheck -{ - /** - * @var array - */ - public static array $classResults = [ - 'Enlightn\SecurityChecker\SecurityChecker' => false, - 'SensioLabs\Security\SecurityChecker' => false, - ]; - - public static function checkDependency(string $class): bool - { - return static::$classResults[$class]; - } -} diff --git a/tests/Checks/RedisHealthCheckTest.php b/tests/Checks/RedisHealthCheckTest.php index 6e02ed8..63a4884 100644 --- a/tests/Checks/RedisHealthCheckTest.php +++ b/tests/Checks/RedisHealthCheckTest.php @@ -19,7 +19,6 @@ class RedisHealthCheckTest extends TestCase * @return array */ public function getPackageProviders($app): array - { return [HealthCheckServiceProvider::class]; } @@ -39,7 +38,7 @@ public function testShowsOkayIfCanPingPredis(): void Redis::swap($redis); - $status = (new RedisHealthCheck)->status(); + $status = (new RedisHealthCheck())->status(); $this->assertTrue($status->isOkay()); } @@ -58,7 +57,7 @@ public function testShowsProblemIfCannotPingPredis(): void Redis::swap($redis); - $status = (new RedisHealthCheck)->status(); + $status = (new RedisHealthCheck())->status(); $this->assertFalse($status->isOkay()); } @@ -77,7 +76,7 @@ public function testShowsOkayIfCannotPingPredisCluster(): void Redis::swap($redis); - $status = (new RedisHealthCheck)->status(); + $status = (new RedisHealthCheck())->status(); $this->assertTrue($status->isOkay()); } @@ -96,7 +95,7 @@ public function testShowsProblemIfCannotPingPredisCluster(): void Redis::swap($redis); - $status = (new RedisHealthCheck)->status(); + $status = (new RedisHealthCheck())->status(); $this->assertFalse($status->isOkay()); } @@ -124,7 +123,7 @@ public function testShowsOkayIfCanPingPhpredis(): void Redis::swap($redis); - $status = (new RedisHealthCheck)->status(); + $status = (new RedisHealthCheck())->status(); $this->assertTrue($status->isOkay()); } @@ -152,7 +151,7 @@ public function testShowsProblemIfCannotPingPhpredis(): void Redis::swap($redis); - $status = (new RedisHealthCheck)->status(); + $status = (new RedisHealthCheck())->status(); $this->assertFalse($status->isOkay()); } @@ -196,7 +195,7 @@ public function testShowsOkayIfCanPingPhpredisCluster(): void Redis::swap($redis); - $status = (new RedisHealthCheck)->status(); + $status = (new RedisHealthCheck())->status(); $this->assertTrue($status->isOkay()); } @@ -223,7 +222,7 @@ public function testShowsProblemIfCannotPingFirstPhpredisClusterMaster(): void $matcher = $this->exactly(1); $redisConn->expects($matcher) ->method('ping') - ->willReturnCallback(function() use ($matcher) { + ->willReturnCallback(function () use ($matcher) { return match ($matcher->numberOfInvocations()) { 1 => [['master1', '6379']], }; @@ -238,7 +237,7 @@ public function testShowsProblemIfCannotPingFirstPhpredisClusterMaster(): void Redis::swap($redis); - $status = (new RedisHealthCheck)->status(); + $status = (new RedisHealthCheck())->status(); $this->assertFalse($status->isOkay()); } @@ -282,7 +281,7 @@ public function testShowsProblemIfCannotPingThirdPhpredisClusterMaster(): void Redis::swap($redis); - $status = (new RedisHealthCheck)->status(); + $status = (new RedisHealthCheck())->status(); $this->assertFalse($status->isOkay()); } } diff --git a/tests/Checks/StorageHealthCheckTest.php b/tests/Checks/StorageHealthCheckTest.php index ddb1628..7562cd7 100644 --- a/tests/Checks/StorageHealthCheckTest.php +++ b/tests/Checks/StorageHealthCheckTest.php @@ -2,8 +2,8 @@ namespace Tests\Checks; -use Exception; use Illuminate\Foundation\Application; +use Tests\Stubs\Storage\BadDisk; use Tests\TestCase; use Illuminate\Support\Facades\Storage; use UKFast\HealthCheck\Checks\StorageHealthCheck; @@ -53,7 +53,7 @@ public function testShowsProblemIfIncorrectReadFromStorage(): void $this->assertTrue($status->isProblem()); } - public function test_shows_okay_if_can_write_to_storage(): void + public function testShowsOkayIfCanWriteToStorage(): void { config([ 'healthcheck.storage.disks' => [ @@ -66,14 +66,3 @@ public function test_shows_okay_if_can_write_to_storage(): void $this->assertTrue($status->isOkay()); } } - -class BadDisk -{ - /** - * @throws Exception - */ - public function __call($name, $arguments): never - { - throw new Exception(); - } -} diff --git a/tests/Controllers/HealthCheckControllerTest.php b/tests/Controllers/HealthCheckControllerTest.php index b225b7c..bb1b9bb 100644 --- a/tests/Controllers/HealthCheckControllerTest.php +++ b/tests/Controllers/HealthCheckControllerTest.php @@ -5,11 +5,12 @@ use Illuminate\Foundation\Application; use Illuminate\Http\Response; use Illuminate\Routing\RouteCollection; +use Tests\Stubs\Checks\AlwaysDegradedCheck; +use Tests\Stubs\Checks\AlwaysDownCheck; +use Tests\Stubs\Checks\AlwaysUpCheck; use Tests\TestCase; use UKFast\HealthCheck\Controllers\HealthCheckController; -use UKFast\HealthCheck\HealthCheck; use UKFast\HealthCheck\HealthCheckServiceProvider; -use UKFast\HealthCheck\Status; class HealthCheckControllerTest extends TestCase { @@ -25,7 +26,7 @@ public function getPackageProviders($app): array public function testReturnsOverallStatusOfOkayWhenEverythingIsUp(): void { $this->setChecks([AlwaysUpCheck::class]); - $response = (new HealthCheckController)->__invoke($this->app); + $response = (new HealthCheckController())->__invoke($this->app); $this->assertSame([ 'status' => 'OK', @@ -106,7 +107,6 @@ public function testDefaultsThePingPathIfConfigIsNotSet(): void $response = $this->get('/ping'); $this->assertSame('pong', $response->getContent()); - } public function testDefaultsTheHealthPathIfConfigIsNotSet(): void @@ -152,7 +152,7 @@ public function testReturnsDegradedStatusWithResponseCode200WhenServiceIsDegrade public function testReturnsStatusOfProblemWhenAProblemOccurs(): void { $this->setChecks([AlwaysUpCheck::class, AlwaysDownCheck::class]); - $response = (new HealthCheckController)->__invoke($this->app); + $response = (new HealthCheckController())->__invoke($this->app); $this->assertSame([ 'status' => 'PROBLEM', @@ -168,7 +168,7 @@ public function testReturnsStatusOfProblemWhenAProblemOccurs(): void public function testReturnsStatusOfProblemWhenBothDegradedAndProblemStatusesOccur(): void { $this->setChecks([AlwaysUpCheck::class, AlwaysDegradedCheck::class, AlwaysDownCheck::class]); - $response = (new HealthCheckController)->__invoke($this->app); + $response = (new HealthCheckController())->__invoke($this->app); $this->assertSame([ 'status' => 'PROBLEM', @@ -186,7 +186,7 @@ public function testReturnsStatusOfProblemWhenBothDegradedAndProblemStatusesOccu ], json_decode($response->getContent(), true)); $this->setChecks([AlwaysUpCheck::class, AlwaysDownCheck::class, AlwaysDegradedCheck::class,]); - $response = (new HealthCheckController)->__invoke($this->app); + $response = (new HealthCheckController())->__invoke($this->app); $this->assertSame([ 'status' => 'PROBLEM', @@ -202,7 +202,6 @@ public function testReturnsStatusOfProblemWhenBothDegradedAndProblemStatusesOccu 'context' => ['debug' => 'info'], ], ], json_decode($response->getContent(), true)); - } protected function setChecks($checks): void @@ -210,37 +209,3 @@ protected function setChecks($checks): void config(['healthcheck.checks' => $checks]); } } - -class AlwaysUpCheck extends HealthCheck -{ - protected string $name = 'always-up'; - - public function status(): Status - { - return $this->okay(); - } -} - -class AlwaysDegradedCheck extends HealthCheck -{ - protected string $name = 'always-degraded'; - - public function status(): Status - { - return $this->degraded('Something went wrong', [ - 'debug' => 'info', - ]); - } -} - -class AlwaysDownCheck extends HealthCheck -{ - protected string $name = 'always-down'; - - public function status(): Status - { - return $this->problem('Something went wrong', [ - 'debug' => 'info', - ]); - } -} diff --git a/tests/Controllers/PingControllerTest.php b/tests/Controllers/PingControllerTest.php index b1c03c5..cf319ae 100644 --- a/tests/Controllers/PingControllerTest.php +++ b/tests/Controllers/PingControllerTest.php @@ -20,7 +20,7 @@ public function getPackageProviders($app): array public function testReturnsPong(): void { - $this->assertSame('pong', (new PingController)->__invoke()); + $this->assertSame('pong', (new PingController())->__invoke()); } public function testOverridesDefaultPath(): void diff --git a/tests/Middleware/AddHeadersTest.php b/tests/Middleware/AddHeadersTest.php index aa0ea16..c5b8225 100644 --- a/tests/Middleware/AddHeadersTest.php +++ b/tests/Middleware/AddHeadersTest.php @@ -3,11 +3,11 @@ namespace Tests\Middleware; use Illuminate\Http\Request; +use Tests\Stubs\Checks\AlwaysDownCheck; +use Tests\Stubs\Checks\AlwaysUpCheck; use Tests\TestCase; use UKFast\HealthCheck\AppHealth; -use UKFast\HealthCheck\HealthCheck; use UKFast\HealthCheck\Middleware\AddHeaders; -use UKFast\HealthCheck\Status; class AddHeadersTest extends TestCase { @@ -15,13 +15,13 @@ public function testAddsHeadersForEachCheck(): void { $this->app->bind('app-health', function () { return new AppHealth(collect([ - new AlwaysUpCheck, - new AlwaysDownCheck, + new AlwaysUpCheck(), + new AlwaysDownCheck(), ])); }); $request = Request::create('/health', 'GET'); - $response = (new AddHeaders)->handle($request, function ($request) { + $response = (new AddHeaders())->handle($request, function ($request) { return response()->json(null, 500); }); @@ -29,25 +29,3 @@ public function testAddsHeadersForEachCheck(): void $this->assertSame('0', $response->headers->get('X-always-down-status')); } } - -class AlwaysUpCheck extends HealthCheck -{ - protected string $name = 'always-up'; - - public function status(): Status - { - return $this->okay(); - } -} - -class AlwaysDownCheck extends HealthCheck -{ - protected string $name = 'always-down'; - - public function status(): Status - { - return $this->problem('Something went wrong', [ - 'debug' => 'info', - ]); - } -} diff --git a/tests/Middleware/BasicAuthTest.php b/tests/Middleware/BasicAuthTest.php index 3044871..b619b36 100644 --- a/tests/Middleware/BasicAuthTest.php +++ b/tests/Middleware/BasicAuthTest.php @@ -20,7 +20,7 @@ public function testOnlyShowsStatusCodeIfFailsBasicAuth(): void 'PHP_AUTH_PW' => 'wrong-password', ]); - $response = (new BasicAuth)->handle($request, function () { + $response = (new BasicAuth())->handle($request, function () { return response('body', 500); }); @@ -37,7 +37,7 @@ public function testOnlyShowsStatusCodeIfNoAuthCredentialsArePassed(): void $request = Request::create('/health', 'GET'); - $response = (new BasicAuth)->handle($request, function () { + $response = (new BasicAuth())->handle($request, function () { return response('body', 500); }); @@ -57,7 +57,7 @@ public function testShowsFullResponseIfPassesBasicAuth(): void 'PHP_AUTH_PW' => 'correct-password', ]); - $response = (new BasicAuth)->handle($request, function () { + $response = (new BasicAuth())->handle($request, function () { return response('body', 500); }); diff --git a/tests/StatusTest.php b/tests/StatusTest.php index 297ca85..18cef23 100644 --- a/tests/StatusTest.php +++ b/tests/StatusTest.php @@ -8,7 +8,7 @@ class StatusTest extends TestCase { public function testCanCreateAnOkayStatus(): void { - $status = (new Status)->okay(); + $status = (new Status())->okay(); $this->assertTrue($status->isOkay()); $this->assertFalse($status->isProblem()); @@ -16,7 +16,7 @@ public function testCanCreateAnOkayStatus(): void public function testCanCreateAProblemStatus(): void { - $status = (new Status)->problem(); + $status = (new Status())->problem(); $this->assertTrue($status->isProblem()); $this->assertFalse($status->isOkay()); @@ -24,7 +24,7 @@ public function testCanCreateAProblemStatus(): void public function testCanInjectAContext(): void { - $status = (new Status)->withContext([ + $status = (new Status())->withContext([ 'context' => 'arbitrary context', ]); @@ -38,14 +38,14 @@ public function testCanInjectAContext(): void public function testCanSetANameForAStatus(): void { - $status = (new Status)->withName('redis'); + $status = (new Status())->withName('redis'); $this->assertSame('redis', $status->name()); } public function testWhenCreatingAProblemCanAttachAMessage(): void { - $status = (new Status)->problem('My thing doesnt work'); + $status = (new Status())->problem('My thing doesnt work'); $this->assertSame('My thing doesnt work', $status->message()); } diff --git a/tests/Stubs/Cache/BadStore.php b/tests/Stubs/Cache/BadStore.php new file mode 100644 index 0000000..f1ed8aa --- /dev/null +++ b/tests/Stubs/Cache/BadStore.php @@ -0,0 +1,16 @@ +degraded('Something went wrong', [ + 'debug' => 'info', + ]); + } +} diff --git a/tests/Stubs/Checks/AlwaysDownCheck.php b/tests/Stubs/Checks/AlwaysDownCheck.php new file mode 100644 index 0000000..e0f4c25 --- /dev/null +++ b/tests/Stubs/Checks/AlwaysDownCheck.php @@ -0,0 +1,18 @@ +problem('Something went wrong', [ + 'debug' => 'info', + ]); + } +} diff --git a/tests/Stubs/Checks/AlwaysUpCheck.php b/tests/Stubs/Checks/AlwaysUpCheck.php new file mode 100644 index 0000000..86cbcc4 --- /dev/null +++ b/tests/Stubs/Checks/AlwaysUpCheck.php @@ -0,0 +1,16 @@ +okay(); + } +} diff --git a/tests/Stubs/Checks/PackageSecurityHealthCheck.php b/tests/Stubs/Checks/PackageSecurityHealthCheck.php new file mode 100644 index 0000000..193c41b --- /dev/null +++ b/tests/Stubs/Checks/PackageSecurityHealthCheck.php @@ -0,0 +1,21 @@ + + */ + public static array $classResults = [ + 'Enlightn\SecurityChecker\SecurityChecker' => false, + 'SensioLabs\Security\SecurityChecker' => false, + ]; + + public static function checkDependency(string $class): bool + { + return static::$classResults[$class]; + } +} diff --git a/tests/Stubs/Checks/UnreliableCheck.php b/tests/Stubs/Checks/UnreliableCheck.php new file mode 100644 index 0000000..097640b --- /dev/null +++ b/tests/Stubs/Checks/UnreliableCheck.php @@ -0,0 +1,19 @@ +connection('default'); + } + + if (!isset($this->connections[$name])) { + throw new InvalidArgumentException("Database [$name] not configured."); + } + + return $this->connections[$name]; + } + + public function addConnection($name, $connection) + { + $this->connections[$name] = $connection; + } +} diff --git a/tests/Stubs/Database/HealthyConnection.php b/tests/Stubs/Database/HealthyConnection.php new file mode 100644 index 0000000..1c76a1a --- /dev/null +++ b/tests/Stubs/Database/HealthyConnection.php @@ -0,0 +1,17 @@ + */ + // phpcs:disable PSR2.Methods.MethodDeclaration.Underscore public function _masters(): array { return []; } + // phpcs:enable PSR2.Methods.MethodDeclaration.Underscore } diff --git a/tests/Stubs/Storage/BadDisk.php b/tests/Stubs/Storage/BadDisk.php new file mode 100644 index 0000000..aae32aa --- /dev/null +++ b/tests/Stubs/Storage/BadDisk.php @@ -0,0 +1,16 @@ +