From 2b3404d2e8b0fe9b295690bd3dc2dfe5325bc51c Mon Sep 17 00:00:00 2001 From: Serhii Donii Date: Tue, 28 May 2024 21:29:07 +0300 Subject: [PATCH 1/3] feat(o10r-symfony-template-145): Upgrade MacPaw/doctrine-aws-iam-rds-auth-bundle for symfony 7 support --- .github/workflows/ci.yaml | 71 +++++ .gitignore | 5 + composer.json | 19 +- config/services.yaml | 9 +- phpunit.xml.dist | 59 ++++ src/Aws/Token/RdsTokenProvider.php | 14 +- .../Token/RdsTokenProviderCacheDecorator.php | 8 +- src/Cache/CacheStorage.php | 2 +- .../DoctrineAwsIamRdsAuthExtension.php | 2 +- src/Doctrine/Driver/IamDecorator.php | 8 +- src/Doctrine/Driver/IamMiddleware.php | 8 +- src/DoctrineAwsIamRdsAuthBundle.php | 2 +- src/Factory/AuthTokenGeneratorFactory.php | 18 ++ tests/App/Kernel.php | 36 +++ tests/App/config/bundles.php | 11 + tests/App/config/package/cache.yaml | 4 + .../config/package/doctrine_aws_iam_rds.yaml | 1 + tests/App/config/package/framework.yaml | 1 + .../package/test/doctrine_aws_iam_rds.yaml | 7 + tests/App/config/package/test/framework.yaml | 2 + tests/App/config/services.yaml | 16 + .../PublicCompilerPass.php | 28 ++ tests/App/var/.gitignore | 2 + tests/Functional/AbstractFunctionalTest.php | 17 ++ .../Functional/Container/DIExtensionTest.php | 86 ++++++ tests/Unit/CacheRdsTokenProviderTest.php | 99 ++++++ tests/Unit/IamDecoratorTest.php | 281 ++++++++++++++++++ tests/Unit/RdsTokenProviderTest.php | 73 +++++ tests/bootstrap.php | 7 + 29 files changed, 869 insertions(+), 27 deletions(-) create mode 100644 .github/workflows/ci.yaml create mode 100644 phpunit.xml.dist create mode 100644 src/Factory/AuthTokenGeneratorFactory.php create mode 100644 tests/App/Kernel.php create mode 100644 tests/App/config/bundles.php create mode 100644 tests/App/config/package/cache.yaml create mode 100644 tests/App/config/package/doctrine_aws_iam_rds.yaml create mode 100644 tests/App/config/package/framework.yaml create mode 100644 tests/App/config/package/test/doctrine_aws_iam_rds.yaml create mode 100644 tests/App/config/package/test/framework.yaml create mode 100644 tests/App/config/services.yaml create mode 100644 tests/App/dependencyInjection/PublicCompilerPass.php create mode 100644 tests/App/var/.gitignore create mode 100644 tests/Functional/AbstractFunctionalTest.php create mode 100644 tests/Functional/Container/DIExtensionTest.php create mode 100644 tests/Unit/CacheRdsTokenProviderTest.php create mode 100644 tests/Unit/IamDecoratorTest.php create mode 100644 tests/Unit/RdsTokenProviderTest.php create mode 100644 tests/bootstrap.php diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 0000000..27ec3e4 --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,71 @@ +name: CI + +on: + pull_request: + push: + branches: [ main, develop ] + +jobs: + run: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + php: + - '8.3' + coverage: ['none'] + symfony-versions: + - '6.4.*' + - '7.0.*' + include: + - php: '8.3' + symfony-versions: '^6.4' + coverage: 'none' + + name: PHP ${{ matrix.php }} Symfony ${{ matrix.symfony-versions }} ${{ matrix.description }} + steps: + - name: Checkout + uses: actions/checkout@v2 + + - uses: actions/cache@v2 + with: + path: ~/.composer/cache/files + key: ${{ matrix.php }}-${{ matrix.symfony-versions }} + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + coverage: ${{ matrix.coverage }} + + - name: Add PHPUnit matcher + run: echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json" + + - name: Set composer cache directory + id: composer-cache + run: echo "::set-output name=dir::$(composer config cache-files-dir)" + + - name: Cache composer + uses: actions/cache@v2.1.2 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: ${{ runner.os }}-${{ matrix.php }}-${{ matrix.symfony-versions }}-composer-${{ hashFiles('composer.json') }} + restore-keys: ${{ runner.os }}-${{ matrix.php }}-${{ matrix.symfony-versions }}-composer + + - name: Update Symfony version + if: matrix.symfony-versions != '' + run: | + composer require symfony/config:${{ matrix.symfony-versions }} --no-update --no-scripts + composer require symfony/dependency-injection:${{ matrix.symfony-versions }} --no-update --no-scripts + composer require symfony/http-kernel:${{ matrix.symfony-versions }} --no-update --no-scripts + composer require symfony/cache:${{ matrix.symfony-versions }} --no-update --no-scripts + composer require --dev symfony/yaml:${{ matrix.symfony-versions }} --no-update --no-scripts + composer require --dev symfony/phpunit-bridge:${{ matrix.symfony-versions }} --no-update --no-scripts + composer require --dev symfony/framework-bundle:${{ matrix.symfony-versions }} --no-update --no-scripts + + - name: Install dependencies + run: composer install + + - name: Run PHPUnit tests + run: vendor/bin/phpunit + if: matrix.coverage == 'none' diff --git a/.gitignore b/.gitignore index 1473148..416d30e 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,8 @@ /phpunit.xml .phpunit.result.cache ###< phpunit/phpunit ### + +/reports +/coverage +/tests/App/var +/var diff --git a/composer.json b/composer.json index d81020f..ee9c7d4 100644 --- a/composer.json +++ b/composer.json @@ -7,18 +7,27 @@ "Macpaw\\DoctrineAwsIamRdsAuthBundle\\": "src/" } }, + "autoload-dev": { + "psr-4": { + "Macpaw\\DoctrineAwsIamRdsAuthBundle\\Tests\\": "tests/" + } + }, "require": { "php": ">=8.3", "doctrine/orm": "^2.17", - "symfony/dependency-injection": "^6.4", - "symfony/config": "^6.4", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/config": "^6.4|^7.0", "aws/aws-sdk-php": "^3.303", - "symfony/cache": "^6.4", - "symfony/http-kernel": "^6.4" + "symfony/cache": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0" }, "require-dev": { "phpstan/phpstan": "^1.10", - "squizlabs/php_codesniffer": "^3.9" + "squizlabs/php_codesniffer": "^3.9", + "phpunit/phpunit": "^11.1", + "symfony/phpunit-bridge": "^7.0", + "symfony/framework-bundle": "^6.4|^7.0", + "symfony/yaml": "^6.4|^7.0" }, "config": { "allow-plugins": { diff --git a/config/services.yaml b/config/services.yaml index e75d7c9..2ef9e48 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -2,11 +2,18 @@ services: _defaults: autowire: true autoconfigure: true - public: false + public: true + + Macpaw\DoctrineAwsIamRdsAuthBundle\Factory\AuthTokenGeneratorFactory: ~ + + rds.authTokenGenerator: + class: Aws\Rds\AuthTokenGenerator + factory: '@Macpaw\DoctrineAwsIamRdsAuthBundle\Factory\AuthTokenGeneratorFactory' Macpaw\DoctrineAwsIamRdsAuthBundle\Aws\Token\RdsTokenProvider: arguments: $lifetime: '%env(RDS_TOKEN_LIFETIME_MINUTES)%' + $generator: '@rds.authTokenGenerator' Macpaw\DoctrineAwsIamRdsAuthBundle\Aws\Token\RdsTokenProviderCacheDecorator: decorates: Macpaw\DoctrineAwsIamRdsAuthBundle\Aws\Token\RdsTokenProvider diff --git a/phpunit.xml.dist b/phpunit.xml.dist new file mode 100644 index 0000000..9cf8d9a --- /dev/null +++ b/phpunit.xml.dist @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + tests/PHPUnit/Unit + + + tests/PHPUnit/Functional + + + tests/PHPUnit/Integration + + + + + ./src + + + src + src/Infrastructure/Migrations + src/Infrastructure/DataFixtures + src/Infrastructure/Exception + src/Component/DB/Exception + src/Domain/Exception + src/Kernel.php + + + + + + + + + diff --git a/src/Aws/Token/RdsTokenProvider.php b/src/Aws/Token/RdsTokenProvider.php index d6b2b9e..18b9a13 100644 --- a/src/Aws/Token/RdsTokenProvider.php +++ b/src/Aws/Token/RdsTokenProvider.php @@ -7,15 +7,17 @@ use Aws\Credentials\CredentialProvider; use Aws\Rds\AuthTokenGenerator; -class RdsTokenProvider implements TokenProviderInterface +final readonly class RdsTokenProvider implements TokenProviderInterface { - private readonly int $lifetime; - private readonly AuthTokenGenerator $generator; + private int $lifetime; + private AuthTokenGenerator $generator; - public function __construct(int $lifetime) - { + public function __construct( + AuthTokenGenerator $generator, + int $lifetime, + ) { $this->lifetime = $lifetime; - $this->generator = new AuthTokenGenerator(CredentialProvider::defaultProvider()); + $this->generator = $generator; } public function getToken(string $endpoint, string $region, string $username, bool $refresh = false): string diff --git a/src/Aws/Token/RdsTokenProviderCacheDecorator.php b/src/Aws/Token/RdsTokenProviderCacheDecorator.php index fe8294c..7f5fe68 100644 --- a/src/Aws/Token/RdsTokenProviderCacheDecorator.php +++ b/src/Aws/Token/RdsTokenProviderCacheDecorator.php @@ -6,14 +6,14 @@ use Macpaw\DoctrineAwsIamRdsAuthBundle\Cache\CacheStorageInterface; -class RdsTokenProviderCacheDecorator implements TokenProviderInterface +final readonly class RdsTokenProviderCacheDecorator implements TokenProviderInterface { private const string CACHE_KEY_PREFIX = 'rds_token_'; public function __construct( - private readonly TokenProviderInterface $tokenProvider, - private readonly CacheStorageInterface $cacheStorage, - private readonly int $ttl + private TokenProviderInterface $tokenProvider, + private CacheStorageInterface $cacheStorage, + private int $ttl ) { } diff --git a/src/Cache/CacheStorage.php b/src/Cache/CacheStorage.php index 9ee1e52..4d50a76 100644 --- a/src/Cache/CacheStorage.php +++ b/src/Cache/CacheStorage.php @@ -7,7 +7,7 @@ use Psr\Cache\InvalidArgumentException; use Symfony\Component\Cache\Adapter\AdapterInterface; -class CacheStorage implements CacheStorageInterface +final readonly class CacheStorage implements CacheStorageInterface { public function __construct(private AdapterInterface $cacheAdapter) { diff --git a/src/DependencyInjection/DoctrineAwsIamRdsAuthExtension.php b/src/DependencyInjection/DoctrineAwsIamRdsAuthExtension.php index 7b00267..66c9837 100644 --- a/src/DependencyInjection/DoctrineAwsIamRdsAuthExtension.php +++ b/src/DependencyInjection/DoctrineAwsIamRdsAuthExtension.php @@ -9,7 +9,7 @@ use Symfony\Component\DependencyInjection\Extension\Extension; use Symfony\Component\DependencyInjection\Loader\YamlFileLoader; -class DoctrineAwsIamRdsAuthExtension extends Extension +final class DoctrineAwsIamRdsAuthExtension extends Extension { public function load(array $configs, ContainerBuilder $container) { diff --git a/src/Doctrine/Driver/IamDecorator.php b/src/Doctrine/Driver/IamDecorator.php index ffb4e19..c3ebfad 100644 --- a/src/Doctrine/Driver/IamDecorator.php +++ b/src/Doctrine/Driver/IamDecorator.php @@ -14,12 +14,12 @@ use Doctrine\DBAL\Schema\AbstractSchemaManager; use Macpaw\DoctrineAwsIamRdsAuthBundle\Aws\Token\TokenProviderInterface; -class IamDecorator implements Driver +readonly class IamDecorator implements Driver { public function __construct( - private readonly Driver $subject, - private readonly TokenProviderInterface $tokenProvider, - private readonly string $region, + private Driver $subject, + private TokenProviderInterface $tokenProvider, + private string $region, ) { } diff --git a/src/Doctrine/Driver/IamMiddleware.php b/src/Doctrine/Driver/IamMiddleware.php index d072206..852e5cf 100644 --- a/src/Doctrine/Driver/IamMiddleware.php +++ b/src/Doctrine/Driver/IamMiddleware.php @@ -8,12 +8,12 @@ use Doctrine\DBAL\Driver; use Doctrine\DBAL\Driver\Middleware; -class IamMiddleware implements Middleware +readonly class IamMiddleware implements Middleware { public function __construct( - private readonly TokenProviderInterface $tokenProvider, - private readonly string $region, - private readonly bool $useIam, + private TokenProviderInterface $tokenProvider, + private string $region, + private bool $useIam, ) { } diff --git a/src/DoctrineAwsIamRdsAuthBundle.php b/src/DoctrineAwsIamRdsAuthBundle.php index 0a3bc0a..c6c0c28 100644 --- a/src/DoctrineAwsIamRdsAuthBundle.php +++ b/src/DoctrineAwsIamRdsAuthBundle.php @@ -6,6 +6,6 @@ use Symfony\Component\HttpKernel\Bundle\Bundle; -class DoctrineAwsIamRdsAuthBundle extends Bundle +final class DoctrineAwsIamRdsAuthBundle extends Bundle { } diff --git a/src/Factory/AuthTokenGeneratorFactory.php b/src/Factory/AuthTokenGeneratorFactory.php new file mode 100644 index 0000000..15b6af7 --- /dev/null +++ b/src/Factory/AuthTokenGeneratorFactory.php @@ -0,0 +1,18 @@ +addCompilerPass(new PublicCompilerPass()); + + return $builder; + } +} diff --git a/tests/App/config/bundles.php b/tests/App/config/bundles.php new file mode 100644 index 0000000..4f18211 --- /dev/null +++ b/tests/App/config/bundles.php @@ -0,0 +1,11 @@ + ['all' => true], + DoctrineAwsIamRdsAuthBundle::class => ['all' => true], +]; diff --git a/tests/App/config/package/cache.yaml b/tests/App/config/package/cache.yaml new file mode 100644 index 0000000..79d6416 --- /dev/null +++ b/tests/App/config/package/cache.yaml @@ -0,0 +1,4 @@ +framework: + cache: + app: cache.adapter.filesystem + system: cache.adapter.filesystem diff --git a/tests/App/config/package/doctrine_aws_iam_rds.yaml b/tests/App/config/package/doctrine_aws_iam_rds.yaml new file mode 100644 index 0000000..c67dd69 --- /dev/null +++ b/tests/App/config/package/doctrine_aws_iam_rds.yaml @@ -0,0 +1 @@ +doctrine_aws_iam_rds: diff --git a/tests/App/config/package/framework.yaml b/tests/App/config/package/framework.yaml new file mode 100644 index 0000000..6455b36 --- /dev/null +++ b/tests/App/config/package/framework.yaml @@ -0,0 +1 @@ +framework: diff --git a/tests/App/config/package/test/doctrine_aws_iam_rds.yaml b/tests/App/config/package/test/doctrine_aws_iam_rds.yaml new file mode 100644 index 0000000..e34f93a --- /dev/null +++ b/tests/App/config/package/test/doctrine_aws_iam_rds.yaml @@ -0,0 +1,7 @@ +doctrine_aws_iam_rds: + +services: + Macpaw\DoctrineAwsIamRdsAuthBundle\Aws\Token\RdsTokenProvider: + public: true + arguments: + [ '@rds.authTokenGenerator', '@?' ] diff --git a/tests/App/config/package/test/framework.yaml b/tests/App/config/package/test/framework.yaml new file mode 100644 index 0000000..2ee7eb4 --- /dev/null +++ b/tests/App/config/package/test/framework.yaml @@ -0,0 +1,2 @@ +framework: + test: true diff --git a/tests/App/config/services.yaml b/tests/App/config/services.yaml new file mode 100644 index 0000000..47e192a --- /dev/null +++ b/tests/App/config/services.yaml @@ -0,0 +1,16 @@ +services: + _defaults: + public: true + autowire: true + autoconfigure: true + + test.service_container: + alias: 'service_container' + + Macpaw\DoctrineAwsIamRdsAuthBundle\Cache\CacheStorageInterface: + class: Macpaw\DoctrineAwsIamRdsAuthBundle\Cache\CacheStorage + arguments: + $cacheAdapter: '@cache.app' + +parameters: + 'kernel.secret': '%env(APP_SECRET)%' diff --git a/tests/App/dependencyInjection/PublicCompilerPass.php b/tests/App/dependencyInjection/PublicCompilerPass.php new file mode 100644 index 0000000..4c78097 --- /dev/null +++ b/tests/App/dependencyInjection/PublicCompilerPass.php @@ -0,0 +1,28 @@ +getDefinitions(); + + foreach ($definitions as $name => $definition) { + $class = $definition->getClass(); + + if (!str_starts_with(strtolower($class ?? $name), 'macpaw\\')) { + continue; + } + + $definition->setPublic(true); + } + } +} diff --git a/tests/App/var/.gitignore b/tests/App/var/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/tests/App/var/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/tests/Functional/AbstractFunctionalTest.php b/tests/Functional/AbstractFunctionalTest.php new file mode 100644 index 0000000..c0eac69 --- /dev/null +++ b/tests/Functional/AbstractFunctionalTest.php @@ -0,0 +1,17 @@ +get(RdsTokenProvider::class); + + self::assertInstanceOf( + RdsTokenProviderCacheDecorator::class, + $provider, + ); + + $reflection = new ReflectionClass($provider); + $prop = $reflection->getProperty('tokenProvider'); + $prop->setAccessible(true); + + $provider = $prop->getValue($provider); + self::assertInstanceOf(RdsTokenProvider::class, $provider); + + $reflection = new ReflectionClass($provider); + $prop = $reflection->getProperty('lifetime'); + $prop->setAccessible(true); + + self::assertEquals( + 10, + $prop->getValue($provider), + ); + + $reflection = new ReflectionClass($provider); + $prop = $reflection->getProperty('generator'); + $prop->setAccessible(true); + + self::assertInstanceOf( + AuthTokenGenerator::class, + $prop->getValue($provider), + ); + } + + public function testIamDecorator(): void + { + $tokenProvider = self::getContainer()->get(RdsTokenProviderCacheDecorator::class); + $iamDecorator = self::getContainer()->get(IamMiddleware::class); + + $reflection = new ReflectionClass($iamDecorator); + $prop = $reflection->getProperty('tokenProvider'); + $prop->setAccessible(true); + + self::assertEquals( + $tokenProvider, + $prop->getValue($iamDecorator), + ); + } + + protected function setUp(): void + { + parent::setUp(); + + $cacheDecorator = self::getContainer()->get(CacheStorageInterface::class); + $reflection = new ReflectionClass($cacheDecorator); + $prop = $reflection->getProperty('cacheAdapter'); + /** @var FilesystemAdapter $cacheAdapter */ + $cacheAdapter = $prop->getValue($cacheDecorator); + $cacheAdapter->clear(); + + putenv('APP_ENV=test'); + } +} diff --git a/tests/Unit/CacheRdsTokenProviderTest.php b/tests/Unit/CacheRdsTokenProviderTest.php new file mode 100644 index 0000000..eb585ff --- /dev/null +++ b/tests/Unit/CacheRdsTokenProviderTest.php @@ -0,0 +1,99 @@ +createMock(AuthTokenGenerator::class); + $mock->expects(self::once()) + ->method('createToken') + ->with( + self::ENDPOINT, + self::REGION, + self::USERNAME, + self::TTL, + ) + ->willReturn('token'); + + $provider = $this->getCacheProvider($mock); + + $token = $provider->getToken( + self::ENDPOINT, + self::REGION, + self::USERNAME, + ); + + self::assertEquals(self::TOKEN, $token); + + $token = $provider->getToken( + self::ENDPOINT, + self::REGION, + self::USERNAME, + ); + + self::assertEquals(self::TOKEN, $token); + } + + public function testInvalidTTL(): void + { + $mock = $this->createMock(AuthTokenGenerator::class); + $mock->expects(self::once()) + ->method('createToken') + ->with( + self::ENDPOINT, + self::REGION, + self::USERNAME, + -1, + ) + ->willThrowException(new InvalidArgumentException()); + + $provider = $this->getProvider($mock, -1); + + self::expectException(InvalidArgumentException::class); + $provider->getToken( + self::ENDPOINT, + self::REGION, + self::USERNAME, + ); + } + + private function getProvider( + AuthTokenGenerator $generator, + int $ttl = self::TTL, + ): RdsTokenProvider { + return new RdsTokenProvider($generator, $ttl); + } + + private function getCacheProvider( + AuthTokenGenerator $generator, + int $ttl = self::TTL, + ): RdsTokenProviderCacheDecorator + { + $adapter = new FilesystemAdapter(''); + $adapter->clear(); + + return new RdsTokenProviderCacheDecorator( + $this->getProvider($generator, $ttl), + new CacheStorage($adapter), + $ttl, + ); + } +} diff --git a/tests/Unit/IamDecoratorTest.php b/tests/Unit/IamDecoratorTest.php new file mode 100644 index 0000000..a6cf19e --- /dev/null +++ b/tests/Unit/IamDecoratorTest.php @@ -0,0 +1,281 @@ + self::ENDPOINT, + 'port' => 5432, + 'user' => self::USERNAME, + ]; + $driverMock = $this->createMock(Driver::class); + $connectionMock = $this->createMock(Connection::class); + + $driverMock->expects(self::once()) + ->method('connect') + ->with( + array_merge( + $params, + ['password' => self::TOKEN], + ), + )->willReturn($connectionMock); + + $cacheStorageMock = $this->createMock( + CacheStorageInterface::class, + ); + $cacheStorageMock->expects(self::once()) + ->method('get') + ->willReturn(null); + + $authMethodMock = $this->createMock(AuthTokenGenerator::class); + $authMethodMock->expects(self::once()) + ->method('createToken') + ->with( + sprintf("%s:%d", self::ENDPOINT, 5432), + self::REGION, + self::USERNAME, + self::TTL, + ) + ->willReturn(self::TOKEN); + + $tokenProvider = new RdsTokenProvider($authMethodMock, self::TTL); + + $tokenProvider = new RdsTokenProviderCacheDecorator( + $tokenProvider, + $cacheStorageMock, + self::TTL, + ); + + $decorator = new IamDecorator( + $driverMock, + $tokenProvider, + self::REGION, + ); + + $decorator->connect($params); + + return [ + 'driver' => $driverMock, + 'iam' => $decorator, + ]; + } + + public function testSuccessfullyReConnected(): void + { + $params = [ + 'host' => self::ENDPOINT, + 'port' => 5432, + 'user' => self::USERNAME, + ]; + $driverMock = $this->createMock(Driver::class); + $connectionMock = $this->createMock(Connection::class); + + $count = 0; + $driverMock->expects(self::exactly(2)) + ->method('connect') + ->with( + array_merge( + $params, + ['password' => self::TOKEN], + ), + )->willReturnCallback(function () use ($connectionMock) : Connection { + $this->count++; + + if (1 === $this->count) { + throw new DriverException( + new ConnectionException( + new Driver\Exception\UnknownParameterType('test'), + null, + ), + new Query('', [], []), + ); + } + + return $connectionMock; + }); + $exceptionConverter = $this->createMock(ExceptionConverter::class); + $exceptionConverter->expects(self::once()) + ->method('convert') + ->willReturn( new ConnectionException( + new Driver\Exception\UnknownParameterType('test'), + null, + )); + $driverMock->expects(self::once()) + ->method('getExceptionConverter') + ->willReturn($exceptionConverter); + + $cacheStorageMock = $this->createMock( + CacheStorageInterface::class, + ); + $cacheStorageMock->expects(self::exactly(2)) + ->method('get') + ->willReturn(null); + + $authMethodMock = $this->createMock( + AuthTokenGenerator::class + ); + $authMethodMock->expects(self::exactly(2)) + ->method('createToken') + ->with( + sprintf("%s:%d", self::ENDPOINT, 5432), + self::REGION, + self::USERNAME, + self::TTL, + ) + ->willReturn(self::TOKEN); + + $tokenProvider = new RdsTokenProvider($authMethodMock, self::TTL); + + $tokenProvider = new RdsTokenProviderCacheDecorator( + $tokenProvider, + $cacheStorageMock, + self::TTL, + ); + + $decorator = new IamDecorator( + $driverMock, + $tokenProvider, + self::REGION, + ); + + $decorator->connect($params); + } + + public function testErrorException(): void + { + $params = [ + 'host' => self::ENDPOINT, + 'port' => 5432, + 'user' => self::USERNAME, + ]; + $driverMock = $this->createMock(Driver::class); + + $driverMock->expects(self::once()) + ->method('connect') + ->with( + array_merge( + $params, + ['password' => self::TOKEN], + ), + )->willReturnCallback(function (): Connection { + throw new InvalidArgumentException(); + }); + + $cacheStorageMock = $this->createMock( + CacheStorageInterface::class, + ); + $cacheStorageMock->expects(self::once()) + ->method('get') + ->willReturn(null); + + $authMethodMock = $this->createMock( + AuthTokenGenerator::class + ); + $authMethodMock->expects(self::once()) + ->method('createToken') + ->with( + sprintf("%s:%d", self::ENDPOINT, 5432), + self::REGION, + self::USERNAME, + self::TTL, + ) + ->willReturn(self::TOKEN); + + $tokenProvider = new RdsTokenProvider($authMethodMock, self::TTL); + + $tokenProvider = new RdsTokenProviderCacheDecorator( + $tokenProvider, + $cacheStorageMock, + self::TTL, + ); + + $decorator = new IamDecorator( + $driverMock, + $tokenProvider, + self::REGION, + ); + + $this->expectException(InvalidArgumentException::class); + $decorator->connect($params); + } + + /** + * @param array{ + * driver: Driver&MockObject, + * iam: IamDecorator&MockObject, + * } $data + */ + #[Depends(methodName: 'testSuccessfullyConnected')] + public function testGetDatabasePlatform(array $data): void + { + /** @var Driver&MockObject $driver */ + $driver = $data['driver']; + /** @var IamDecorator&MockObject $iam */ + $iam = $data['iam']; + $platform = $this->createMock(AbstractPlatform::class); + $driver->expects(self::once()) + ->method('getDatabasePlatform') + ->willReturn($platform); + + self::assertEquals($platform, $iam->getDatabasePlatform()); + } + + /** + * @param array{ + * driver: Driver&MockObject, + * iam: IamDecorator&MockObject, + * } $data + */ + #[Depends(methodName: 'testSuccessfullyConnected')] + public function testGetExceptionConverter(array $data): void + { + /** @var Driver&MockObject $driver */ + $driver = $data['driver']; + /** @var IamDecorator&MockObject $iam */ + $iam = $data['iam']; + $exceptionConverter = $this->createMock(ExceptionConverter::class); + $driver->expects(self::once()) + ->method('getExceptionConverter') + ->willReturn($exceptionConverter); + + self::assertEquals($exceptionConverter, $iam->getExceptionConverter()); + } +} diff --git a/tests/Unit/RdsTokenProviderTest.php b/tests/Unit/RdsTokenProviderTest.php new file mode 100644 index 0000000..cca64df --- /dev/null +++ b/tests/Unit/RdsTokenProviderTest.php @@ -0,0 +1,73 @@ +createMock(AuthTokenGenerator::class); + $mock->expects(self::once()) + ->method('createToken') + ->with( + self::ENDPOINT, + self::REGION, + self::USERNAME, + self::TTL, + ) + ->willReturn(self::TOKEN); + + $provider = $this->getProvider($mock); + + $token = $provider->getToken( + self::ENDPOINT, + self::REGION, + self::USERNAME, + ); + + self::assertEquals(self::TOKEN, $token); + } + + public function testInvalidTTL(): void + { + $mock = $this->createMock(AuthTokenGenerator::class); + $mock->expects(self::once()) + ->method('createToken') + ->with( + self::ENDPOINT, + self::REGION, + self::USERNAME, + -1, + ) + ->willThrowException(new InvalidArgumentException()); + + $provider = $this->getProvider($mock, -1); + + self::expectException(InvalidArgumentException::class); + $provider->getToken( + self::ENDPOINT, + self::REGION, + self::USERNAME, + ); + } + + private function getProvider( + AuthTokenGenerator $generator, + int $ttl = self::TTL, + ): RdsTokenProvider { + return new RdsTokenProvider($generator, $ttl); + } +} diff --git a/tests/bootstrap.php b/tests/bootstrap.php new file mode 100644 index 0000000..2409459 --- /dev/null +++ b/tests/bootstrap.php @@ -0,0 +1,7 @@ + Date: Tue, 28 May 2024 21:31:53 +0300 Subject: [PATCH 2/3] feat(o10r-symfony-template-145): Upgrade MacPaw/doctrine-aws-iam-rds-auth-bundle for symfony 7 support --- .github/workflows/ci.yaml | 12 ++++------ .github/workflows/security.yaml | 24 +++++++++++++++++++ config/services.yaml | 2 +- phpunit.xml.dist | 19 ++++----------- ...ctionalTest.php => AbstractFunctional.php} | 2 +- .../{DIExtensionTest.php => DIExtension.php} | 4 ++-- 6 files changed, 37 insertions(+), 26 deletions(-) create mode 100644 .github/workflows/security.yaml rename tests/Functional/{AbstractFunctionalTest.php => AbstractFunctional.php} (85%) rename tests/Functional/Container/{DIExtensionTest.php => DIExtension.php} (97%) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 27ec3e4..179e516 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -17,12 +17,8 @@ jobs: symfony-versions: - '6.4.*' - '7.0.*' - include: - - php: '8.3' - symfony-versions: '^6.4' - coverage: 'none' - name: PHP ${{ matrix.php }} Symfony ${{ matrix.symfony-versions }} ${{ matrix.description }} + name: Test with PHP ${{ matrix.php }} Symfony ${{ matrix.symfony-versions }} ${{ matrix.description }} steps: - name: Checkout uses: actions/checkout@v2 @@ -36,7 +32,8 @@ jobs: uses: shivammathur/setup-php@v2 with: php-version: ${{ matrix.php }} - coverage: ${{ matrix.coverage }} + coverage: xdebug + extensions: xdebug - name: Add PHPUnit matcher run: echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json" @@ -67,5 +64,4 @@ jobs: run: composer install - name: Run PHPUnit tests - run: vendor/bin/phpunit - if: matrix.coverage == 'none' + run: XDEBUG_MODE=coverage vendor/bin/phpunit diff --git a/.github/workflows/security.yaml b/.github/workflows/security.yaml new file mode 100644 index 0000000..248d5d0 --- /dev/null +++ b/.github/workflows/security.yaml @@ -0,0 +1,24 @@ +on: + pull_request: + push: + branches: [ main, develop ] + +jobs: + security-checker: + name: Security checker + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + + - name: Install dependencies + run: composer install --no-progress --no-interaction --prefer-dist + + - name: Download local-php-security-checker + run: curl -s -L -o local-php-security-checker https://github.com/fabpot/local-php-security-checker/releases/download/v1.0.0/local-php-security-checker_1.0.0_linux_amd64 + + - name: Run local-php-security-checker + run: chmod +x local-php-security-checker && ./local-php-security-checker diff --git a/config/services.yaml b/config/services.yaml index 2ef9e48..4c6de91 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -2,7 +2,7 @@ services: _defaults: autowire: true autoconfigure: true - public: true + public: false Macpaw\DoctrineAwsIamRdsAuthBundle\Factory\AuthTokenGeneratorFactory: ~ diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 9cf8d9a..1118df7 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -15,21 +15,19 @@ - + + - tests/PHPUnit/Unit + tests/Unit - tests/PHPUnit/Functional - - - tests/PHPUnit/Integration + tests/Functional @@ -37,13 +35,6 @@ ./src - src - src/Infrastructure/Migrations - src/Infrastructure/DataFixtures - src/Infrastructure/Exception - src/Component/DB/Exception - src/Domain/Exception - src/Kernel.php @@ -53,7 +44,7 @@ ignoreDeprecatedCodeUnits="true" disableCodeCoverageIgnore="true"> - + diff --git a/tests/Functional/AbstractFunctionalTest.php b/tests/Functional/AbstractFunctional.php similarity index 85% rename from tests/Functional/AbstractFunctionalTest.php rename to tests/Functional/AbstractFunctional.php index c0eac69..0a0f7eb 100644 --- a/tests/Functional/AbstractFunctionalTest.php +++ b/tests/Functional/AbstractFunctional.php @@ -8,7 +8,7 @@ use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; use Symfony\Component\DependencyInjection\Container; -abstract class AbstractFunctionalTest extends KernelTestCase +abstract class AbstractFunctional extends KernelTestCase { protected static function getKernelClass(): string { diff --git a/tests/Functional/Container/DIExtensionTest.php b/tests/Functional/Container/DIExtension.php similarity index 97% rename from tests/Functional/Container/DIExtensionTest.php rename to tests/Functional/Container/DIExtension.php index f0f0f9d..2e16114 100644 --- a/tests/Functional/Container/DIExtensionTest.php +++ b/tests/Functional/Container/DIExtension.php @@ -12,13 +12,13 @@ use Macpaw\DoctrineAwsIamRdsAuthBundle\Cache\CacheStorageInterface; use Macpaw\DoctrineAwsIamRdsAuthBundle\Doctrine\Driver\IamDecorator; use Macpaw\DoctrineAwsIamRdsAuthBundle\Doctrine\Driver\IamMiddleware; -use Macpaw\DoctrineAwsIamRdsAuthBundle\Tests\Functional\AbstractFunctionalTest; +use Macpaw\DoctrineAwsIamRdsAuthBundle\Tests\Functional\AbstractFunctional; use PHPUnit\Framework\Attributes\Depends; use PHPUnit\Framework\TestCase; use ReflectionClass; use Symfony\Component\Cache\Adapter\FilesystemAdapter; -final class DIExtensionTest extends AbstractFunctionalTest +final class DIExtension extends AbstractFunctional { public function testRdsTokenProviderInit(): void { From 000a93a31e49759610c6469dadd03e72c390279d Mon Sep 17 00:00:00 2001 From: Serhii Donii Date: Tue, 28 May 2024 22:22:50 +0300 Subject: [PATCH 3/3] feat(o10r-symfony-template-145): Upgrade MacPaw/doctrine-aws-iam-rds-auth-bundle for symfony 7 support --- .github/workflows/ci.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 179e516..90f3600 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -65,3 +65,8 @@ jobs: - name: Run PHPUnit tests run: XDEBUG_MODE=coverage vendor/bin/phpunit + + - name: Upload coverage reports to Codecov + uses: codecov/codecov-action@v4.0.1 + with: + token: ${{ secrets.CODECOV_TOKEN }}