diff --git a/README.md b/README.md index 65ca778..96ed1b2 100644 --- a/README.md +++ b/README.md @@ -32,12 +32,14 @@ declare(strict_types=1); namespace Fixtures; use Dynamite\AbstractTable; +use Dynamite\Attribute\Groups; use Dynamite\Enum\KeyTypeEnum; use Dynamite\Enum\ProjectionTypeEnum; use Dynamite\Enum\ScalarAttributeTypeEnum; use Dynamite\TableInterface; use Dynamite\Schema\Attribute; +#[Groups(['group1'])] // groups can be used optionally with console command final class UsersTable extends AbstractTable implements TableInterface { protected function configure(): void @@ -71,10 +73,12 @@ declare(strict_types=1); namespace Fixtures; use Dynamite\AbstractFixture; +use Dynamite\Attribute\Groups; use Dynamite\FixtureInterface; use Dynamite\Schema\Record; use Dynamite\Schema\Value; +#[Groups(['group1'])] // groups can be used optionally with console command final class UserFixtures extends AbstractFixture implements FixtureInterface { protected function configure(): void @@ -150,6 +154,7 @@ declare(strict_types=1); use Dynamite\Client; use Dynamite\Executor; +use Dynamite\Loader; use Dynamite\Serializer\PropertyNameConverter; use Symfony\Component\Serializer\Normalizer\BackedEnumNormalizer; use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; @@ -170,12 +175,20 @@ $clientFactory = new ClientFactory($serializer); $loader = new Loader($validator, $serializer); $loader->loadFromDirectory('/path/to/YourFixtures'); +$groups = ['group1']; // loading fixtures belong to the selected group only + $executor = new Executor($clientFactory->createAsyncAwsClient()); -$executor->execute($loader->getFixtures(), $loader->getTables()); +$executor->execute($loader->getFixtures($groups), $loader->getTables($groups)); ``` **Important!** Each executor class comes with a purger class which executed before, drop tables and truncate data. +### Load fixtures via console command + +```shell +bin/console dynamite:fixtures:load --path path/to/fixtures +``` + ### Debug logger Execution process debug logs can be enabled by passing PSR-3 logger into executor: diff --git a/composer.json b/composer.json index f658b48..594f687 100644 --- a/composer.json +++ b/composer.json @@ -13,6 +13,7 @@ "require": { "php": "^8.1", "psr/log": "^2.0 || ^3.0", + "symfony/console": "^5.4 || ^6.0", "symfony/property-access": "^5.4 || ^6.0", "symfony/serializer": "^5.4 || ^6.0", "symfony/validator": "^5.4 || ^6.0", diff --git a/composer.lock b/composer.lock index d36c430..ef95c60 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,61 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "7b03f58324f883f836a504cc739df06f", + "content-hash": "3b65e39cd8d2d51b03c4b9760a04e914", "packages": [ + { + "name": "psr/container", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "shasum": "" + }, + "require": { + "php": ">=7.4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/2.0.2" + }, + "time": "2021-11-05T16:47:00+00:00" + }, { "name": "psr/log", "version": "3.0.0", @@ -56,6 +109,102 @@ }, "time": "2021-07-14T16:46:02+00:00" }, + { + "name": "symfony/console", + "version": "v6.2.10", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "12288d9f4500f84a4d02254d4aa968b15488476f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/12288d9f4500f84a4d02254d4aa968b15488476f", + "reference": "12288d9f4500f84a4d02254d4aa968b15488476f", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/polyfill-mbstring": "~1.0", + "symfony/service-contracts": "^1.1|^2|^3", + "symfony/string": "^5.4|^6.0" + }, + "conflict": { + "symfony/dependency-injection": "<5.4", + "symfony/dotenv": "<5.4", + "symfony/event-dispatcher": "<5.4", + "symfony/lock": "<5.4", + "symfony/process": "<5.4" + }, + "provide": { + "psr/log-implementation": "1.0|2.0|3.0" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^5.4|^6.0", + "symfony/dependency-injection": "^5.4|^6.0", + "symfony/event-dispatcher": "^5.4|^6.0", + "symfony/lock": "^5.4|^6.0", + "symfony/process": "^5.4|^6.0", + "symfony/var-dumper": "^5.4|^6.0" + }, + "suggest": { + "psr/log": "For using the console logger", + "symfony/event-dispatcher": "", + "symfony/lock": "", + "symfony/process": "" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Eases the creation of beautiful and testable command line interfaces", + "homepage": "https://symfony.com", + "keywords": [ + "cli", + "command-line", + "console", + "terminal" + ], + "support": { + "source": "https://github.com/symfony/console/tree/v6.2.10" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-04-28T13:37:43+00:00" + }, { "name": "symfony/deprecation-contracts", "version": "v3.2.1", @@ -723,6 +872,91 @@ ], "time": "2023-04-18T13:57:49+00:00" }, + { + "name": "symfony/service-contracts", + "version": "v3.2.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/service-contracts.git", + "reference": "a8c9cedf55f314f3a186041d19537303766df09a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/a8c9cedf55f314f3a186041d19537303766df09a", + "reference": "a8c9cedf55f314f3a186041d19537303766df09a", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/container": "^2.0" + }, + "conflict": { + "ext-psr": "<1.1|>=2" + }, + "suggest": { + "symfony/service-implementation": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.3-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Service\\": "" + }, + "exclude-from-classmap": [ + "/Test/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to writing services", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/service-contracts/tree/v3.2.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-03-01T10:32:47+00:00" + }, { "name": "symfony/string", "version": "v6.2.8", @@ -3584,59 +3818,6 @@ }, "time": "2021-02-03T23:26:27+00:00" }, - { - "name": "psr/container", - "version": "2.0.2", - "source": { - "type": "git", - "url": "https://github.com/php-fig/container.git", - "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", - "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", - "shasum": "" - }, - "require": { - "php": ">=7.4.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Container\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "Common Container Interface (PHP FIG PSR-11)", - "homepage": "https://github.com/php-fig/container", - "keywords": [ - "PSR-11", - "container", - "container-interface", - "container-interop", - "psr" - ], - "support": { - "issues": "https://github.com/php-fig/container/issues", - "source": "https://github.com/php-fig/container/tree/2.0.2" - }, - "time": "2021-11-05T16:47:00+00:00" - }, { "name": "psr/http-client", "version": "1.0.2", @@ -4814,102 +4995,6 @@ ], "time": "2023-05-11T14:04:07+00:00" }, - { - "name": "symfony/console", - "version": "v6.2.10", - "source": { - "type": "git", - "url": "https://github.com/symfony/console.git", - "reference": "12288d9f4500f84a4d02254d4aa968b15488476f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/12288d9f4500f84a4d02254d4aa968b15488476f", - "reference": "12288d9f4500f84a4d02254d4aa968b15488476f", - "shasum": "" - }, - "require": { - "php": ">=8.1", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/polyfill-mbstring": "~1.0", - "symfony/service-contracts": "^1.1|^2|^3", - "symfony/string": "^5.4|^6.0" - }, - "conflict": { - "symfony/dependency-injection": "<5.4", - "symfony/dotenv": "<5.4", - "symfony/event-dispatcher": "<5.4", - "symfony/lock": "<5.4", - "symfony/process": "<5.4" - }, - "provide": { - "psr/log-implementation": "1.0|2.0|3.0" - }, - "require-dev": { - "psr/log": "^1|^2|^3", - "symfony/config": "^5.4|^6.0", - "symfony/dependency-injection": "^5.4|^6.0", - "symfony/event-dispatcher": "^5.4|^6.0", - "symfony/lock": "^5.4|^6.0", - "symfony/process": "^5.4|^6.0", - "symfony/var-dumper": "^5.4|^6.0" - }, - "suggest": { - "psr/log": "For using the console logger", - "symfony/event-dispatcher": "", - "symfony/lock": "", - "symfony/process": "" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Console\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Eases the creation of beautiful and testable command line interfaces", - "homepage": "https://symfony.com", - "keywords": [ - "cli", - "command-line", - "console", - "terminal" - ], - "support": { - "source": "https://github.com/symfony/console/tree/v6.2.10" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2023-04-28T13:37:43+00:00" - }, { "name": "symfony/error-handler", "version": "v6.2.10", @@ -5295,91 +5380,6 @@ ], "time": "2022-11-03T14:55:06+00:00" }, - { - "name": "symfony/service-contracts", - "version": "v3.2.1", - "source": { - "type": "git", - "url": "https://github.com/symfony/service-contracts.git", - "reference": "a8c9cedf55f314f3a186041d19537303766df09a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/a8c9cedf55f314f3a186041d19537303766df09a", - "reference": "a8c9cedf55f314f3a186041d19537303766df09a", - "shasum": "" - }, - "require": { - "php": ">=8.1", - "psr/container": "^2.0" - }, - "conflict": { - "ext-psr": "<1.1|>=2" - }, - "suggest": { - "symfony/service-implementation": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "3.3-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Contracts\\Service\\": "" - }, - "exclude-from-classmap": [ - "/Test/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Generic abstractions related to writing services", - "homepage": "https://symfony.com", - "keywords": [ - "abstractions", - "contracts", - "decoupling", - "interfaces", - "interoperability", - "standards" - ], - "support": { - "source": "https://github.com/symfony/service-contracts/tree/v3.2.1" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2023-03-01T10:32:47+00:00" - }, { "name": "symfony/var-dumper", "version": "v6.2.10", diff --git a/src/Command/FixturesLoadCommand.php b/src/Command/FixturesLoadCommand.php new file mode 100644 index 0000000..972962c --- /dev/null +++ b/src/Command/FixturesLoadCommand.php @@ -0,0 +1,70 @@ +getOption('path'); + $groups = $input->getOption('group') ?? []; + $onlyFixtures = (bool) ($input->getOption('only-fixtures') ?? false); + $onlyTables = (bool) ($input->getOption('only-tables') ?? false); + + $this->loader->loadFromDirectory($path); + + $fixtures = $onlyTables ? [] : $this->loader->getFixtures($groups); + if (!$onlyTables && \count($fixtures) < 1) { + $message = 'Could not find any fixture to load'; + if (\count($groups) > 0) { + $message .= sprintf(' in the groups (%s)', implode(', ', $groups)); + } + + $ui->error($message); + + return Command::FAILURE; + } + + $tables = $onlyFixtures ? [] : $this->loader->getTables($groups); + + $this->executor->execute($fixtures, $tables); + + $ui->info('Fixtures loaded'); + + return Command::SUCCESS; + } + + protected function configure(): void + { + $this + ->addOption('path', null, InputOption::VALUE_REQUIRED, 'Fixtures load directory path') + ->addOption('group', null, InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, 'Only create tables and load fixtures that belong to this group') + ->addOption('only-fixtures', null, InputOption::VALUE_OPTIONAL, 'Load fixtures only (skip tables creating)') + ->addOption('only-tables', null, InputOption::VALUE_OPTIONAL, 'Create tables only (skip fixtures loading)') + ; + } +} diff --git a/tests/Constraint/Composite.php b/tests/Constraint/Composite.php new file mode 100644 index 0000000..5ccfadb --- /dev/null +++ b/tests/Constraint/Composite.php @@ -0,0 +1,72 @@ + + */ + private array $constraints = []; + + private ?ExpectationFailedException $expectationFailedException = null; + + public function __construct(mixed ...$constraints) + { + foreach ($constraints as $constraint) { + if (!$constraint instanceof Constraint) { + $constraint = new IsEqual($constraint); + } + $this->constraints[] = $constraint; + } + } + + public function evaluate(mixed $other, string $description = '', bool $returnResult = false): ?bool + { + $success = true; + foreach ($this->constraints as $constraint) { + try { + $constraint->evaluate($other, $description); + } catch (ExpectationFailedException $ex) { + $success = false; + $this->expectationFailedException = $ex; + break; + } + } + + if ($returnResult) { + return $success; + } + + if (!$success) { + $this->fail($other, $description); + } + + return false; + } + + public function toString(): string + { + if ($this->expectationFailedException instanceof ExpectationFailedException) { + return ThrowableToStringMapper::map($this->expectationFailedException); + } + + $string = []; + foreach ($this->constraints as $constraint) { + $string[] = $constraint->toString(); + } + return implode('; ', $string); + } + + public function count(): int + { + return \count($this->constraints); + } +} diff --git a/tests/Constraint/Delegate.php b/tests/Constraint/Delegate.php new file mode 100644 index 0000000..79c5ffc --- /dev/null +++ b/tests/Constraint/Delegate.php @@ -0,0 +1,27 @@ +matcher->evaluate(($this->getter)($other), $description, $returnResult); + } + + public function toString(): string + { + return $this->name . ' ' . $this->matcher->toString(); + } +} diff --git a/tests/Fixtures/Fixtures/Domain/Table1DomainDataLoader.php b/tests/Fixtures/Fixtures/Domain/Table1DomainDataLoader.php index 4f02f04..478ec03 100644 --- a/tests/Fixtures/Fixtures/Domain/Table1DomainDataLoader.php +++ b/tests/Fixtures/Fixtures/Domain/Table1DomainDataLoader.php @@ -7,12 +7,21 @@ use Dynamite\AbstractFixture; use Dynamite\Attribute\Groups; use Dynamite\FixtureInterface; +use Dynamite\Schema\Record; +use Dynamite\Schema\Value; #[Groups(['group1'])] final class Table1DomainDataLoader extends AbstractFixture implements FixtureInterface { protected function configure(): void { - // no content + $this + ->setTableName('Table1') + ->addRecords([ + new Record([ + Value::stringValue('Column1', 'e5502ec2-42a7-408b-9f03-f8e162b6257e'), + ]), + ]) + ; } } diff --git a/tests/Fixtures/Fixtures/Domain/Table2DomainDataLoader.php b/tests/Fixtures/Fixtures/Domain/Table2DomainDataLoader.php index 714ed06..b3a0300 100644 --- a/tests/Fixtures/Fixtures/Domain/Table2DomainDataLoader.php +++ b/tests/Fixtures/Fixtures/Domain/Table2DomainDataLoader.php @@ -7,12 +7,24 @@ use Dynamite\AbstractFixture; use Dynamite\Attribute\Groups; use Dynamite\FixtureInterface; +use Dynamite\Schema\Record; +use Dynamite\Schema\Value; #[Groups(['group2'])] final class Table2DomainDataLoader extends AbstractFixture implements FixtureInterface { protected function configure(): void { - // no content + $this + ->setTableName('Table2') + ->addRecords([ + new Record([ + Value::stringValue('Column1', 'e5502ec2-42a7-408b-9f03-f8e162b6257e'), + ]), + new Record([ + Value::stringValue('Column1', 'f0cf458c-4fc0-4dd8-ba5b-eca6dba9be63'), + ]), + ]) + ; } } diff --git a/tests/Fixtures/Tables/Table1.php b/tests/Fixtures/Tables/Table1.php index 1e0c355..c0c32ee 100644 --- a/tests/Fixtures/Tables/Table1.php +++ b/tests/Fixtures/Tables/Table1.php @@ -6,6 +6,9 @@ use Dynamite\AbstractTable; use Dynamite\Attribute\Groups; +use Dynamite\Enum\KeyTypeEnum; +use Dynamite\Enum\ScalarAttributeTypeEnum; +use Dynamite\Schema\Attribute; use Dynamite\TableInterface; #[Groups(['group1'])] @@ -13,6 +16,12 @@ final class Table1 extends AbstractTable implements TableInterface { protected function configure(): void { - // no content + $this + ->setTableName('Table1') + ->addAttributes([ + new Attribute('Column1', ScalarAttributeTypeEnum::String, KeyTypeEnum::Hash), + ]) + ->setProvisionedThroughput(1, 1) + ; } } diff --git a/tests/Fixtures/Tables/Table2.php b/tests/Fixtures/Tables/Table2.php index 57fcb1e..48fa845 100644 --- a/tests/Fixtures/Tables/Table2.php +++ b/tests/Fixtures/Tables/Table2.php @@ -5,12 +5,23 @@ namespace Dynamite\Tests\Fixtures\Tables; use Dynamite\AbstractTable; +use Dynamite\Attribute\Groups; +use Dynamite\Enum\KeyTypeEnum; +use Dynamite\Enum\ScalarAttributeTypeEnum; +use Dynamite\Schema\Attribute; use Dynamite\TableInterface; +#[Groups(['group2'])] final class Table2 extends AbstractTable implements TableInterface { protected function configure(): void { - // no content + $this + ->setTableName('Table2') + ->addAttributes([ + new Attribute('Column1', ScalarAttributeTypeEnum::String, KeyTypeEnum::Hash), + ]) + ->setProvisionedThroughput(1, 1) + ; } } diff --git a/tests/Integration/AsyncAws/Command/FixturesLoadCommandTest.php b/tests/Integration/AsyncAws/Command/FixturesLoadCommandTest.php new file mode 100644 index 0000000..e306484 --- /dev/null +++ b/tests/Integration/AsyncAws/Command/FixturesLoadCommandTest.php @@ -0,0 +1,188 @@ +createCommand(); + + $path = realpath(dirname(__DIR__) . '/../../Constraint'); + + Assert::notFalse($path, 'Unable to load fixtures - path is invalid'); + + $tester = new CommandTester($command); + $tester->execute([ + '--path' => $path, + '--group' => ['group1'], + ]); + + self::assertSame(Command::FAILURE, $tester->getStatusCode()); + self::assertSame('[ERROR] Could not find any fixture to load in the groups (group1)', trim($tester->getDisplay())); + } + + /** + * @param array $options + */ + #[DataProvider('executeDataProvider')] + public function testExecute(array $options, Composite $expected): void + { + $command = $this->createCommand(); + + $path = realpath(dirname(__DIR__) . '/../../Fixtures'); + + Assert::notFalse($path, 'Unable to load fixtures - path is invalid'); + + $tester = new CommandTester($command); + $tester->execute(array_merge( + [ + '--path' => $path, + ], + $options + )); + + $tester->assertCommandIsSuccessful(); + + self::assertThat($this->dynamoDbClient, $expected); + } + + /** + * @return iterable, expected: Composite}> + */ + public static function executeDataProvider(): iterable + { + yield 'group1-only' => [ + 'options' => [ + '--group' => ['group1'], + ], + 'expected' => new Composite( + new Delegate( + new IsIdentical(['Table1']), + function (DynamoDbClient $dynamoDbClient) { + $response = $dynamoDbClient->listTables([ + 'Limit' => 10, + ]); + $tables = $response->getTableNames(true); + + return iterator_to_array($tables); // @phpstan-ignore-line + }, + 'tables-list' + ), + new Delegate( + new IsIdentical(1), + static function (DynamoDbClient $dynamoDbClient) { + $response = $dynamoDbClient->scan([ + 'TableName' => 'Table1', + ]); + + return $response->getCount(); + }, + 'table1-has-records' + ) + ), + ]; + + yield 'group2-only' => [ + 'options' => [ + '--group' => ['group2'], + ], + 'expected' => new Composite( + new Delegate( + new IsIdentical(['Table2']), + function (DynamoDbClient $dynamoDbClient) { + $response = $dynamoDbClient->listTables([ + 'Limit' => 10, + ]); + $tables = $response->getTableNames(true); + + return iterator_to_array($tables); // @phpstan-ignore-line + }, + 'tables-list' + ), + new Delegate( + new IsIdentical(2), + static function (DynamoDbClient $dynamoDbClient) { + $response = $dynamoDbClient->scan([ + 'TableName' => 'Table2', + ]); + + return $response->getCount(); + }, + 'table2-has-records' + ) + ), + ]; + + yield 'only-tables' => [ + 'options' => [ + '--only-tables' => true, + ], + 'expected' => new Composite( + new Delegate( + new IsIdentical(['Table1', 'Table2']), + function (DynamoDbClient $dynamoDbClient) { + $response = $dynamoDbClient->listTables([ + 'Limit' => 10, + ]); + $tables = $response->getTableNames(true); + + return iterator_to_array($tables); // @phpstan-ignore-line + }, + 'tables-list' + ), + new Delegate( + new IsIdentical(0), + static function (DynamoDbClient $dynamoDbClient) { + $response = $dynamoDbClient->scan([ + 'TableName' => 'Table1', + ]); + + return $response->getCount(); + }, + 'table1-has-no-records' + ), + new Delegate( + new IsIdentical(0), + static function (DynamoDbClient $dynamoDbClient) { + $response = $dynamoDbClient->scan([ + 'TableName' => 'Table2', + ]); + + return $response->getCount(); + }, + 'table2-has-no-records' + ) + ), + ]; + } + + private function createCommand(): FixturesLoadCommand + { + return new FixturesLoadCommand( + new Loader($this->createValidator(), $this->createSerializer()), + new Executor($this->createClient()) + ); + } +} diff --git a/tests/Integration/AwsSdk/Command/FixturesLoadCommandTest.php b/tests/Integration/AwsSdk/Command/FixturesLoadCommandTest.php new file mode 100644 index 0000000..469adcd --- /dev/null +++ b/tests/Integration/AwsSdk/Command/FixturesLoadCommandTest.php @@ -0,0 +1,166 @@ + $options + */ + #[DataProvider('executeDataProvider')] + public function testExecute(array $options, Composite $expected): void + { + $command = $this->createCommand(); + + $path = realpath(dirname(__DIR__) . '/../../Fixtures'); + + Assert::notFalse($path, 'Unable to load fixtures - path is invalid'); + + $tester = new CommandTester($command); + $tester->execute(array_merge( + [ + '--path' => $path, + ], + $options + )); + + $tester->assertCommandIsSuccessful(); + + self::assertThat($this->dynamoDbClient, $expected); + } + + /** + * @return iterable, expected: Composite}> + */ + public static function executeDataProvider(): iterable + { + yield 'group1-only' => [ + 'options' => [ + '--group' => ['group1'], + ], + 'expected' => new Composite( + new Delegate( + new IsIdentical(['Table1']), + function (DynamoDbClient $dynamoDbClient) { + $response = $dynamoDbClient->listTables([ + 'Limit' => 10, + ]); + + return $response['TableNames']; + }, + 'tables-list' + ), + new Delegate( + new IsIdentical(1), + static function (DynamoDbClient $dynamoDbClient) { + $response = $dynamoDbClient->scan([ + 'TableName' => 'Table1', + ]); + + return $response['Count']; + }, + 'table1-has-records' + ) + ), + ]; + + yield 'group2-only' => [ + 'options' => [ + '--group' => ['group2'], + ], + 'expected' => new Composite( + new Delegate( + new IsIdentical(['Table2']), + function (DynamoDbClient $dynamoDbClient) { + $response = $dynamoDbClient->listTables([ + 'Limit' => 10, + ]); + + return $response['TableNames']; + }, + 'tables-list' + ), + new Delegate( + new IsIdentical(2), + static function (DynamoDbClient $dynamoDbClient) { + $response = $dynamoDbClient->scan([ + 'TableName' => 'Table2', + ]); + + return $response['Count']; + }, + 'table2-has-records' + ) + ), + ]; + + yield 'only-tables' => [ + 'options' => [ + '--only-tables' => true, + ], + 'expected' => new Composite( + new Delegate( + new IsIdentical(['Table1', 'Table2']), + function (DynamoDbClient $dynamoDbClient) { + $response = $dynamoDbClient->listTables([ + 'Limit' => 10, + ]); + + return $response['TableNames']; + }, + 'tables-list' + ), + new Delegate( + new IsIdentical(0), + static function (DynamoDbClient $dynamoDbClient) { + $response = $dynamoDbClient->scan([ + 'TableName' => 'Table1', + ]); + + return $response['Count']; + }, + 'table1-has-no-records' + ), + new Delegate( + new IsIdentical(0), + static function (DynamoDbClient $dynamoDbClient) { + $response = $dynamoDbClient->scan([ + 'TableName' => 'Table2', + ]); + + return $response['Count']; + }, + 'table2-has-no-records' + ) + ), + ]; + } + + private function createCommand(): FixturesLoadCommand + { + return new FixturesLoadCommand( + new Loader($this->createValidator(), $this->createSerializer()), + new Executor($this->createClient()) + ); + } +} diff --git a/tests/Integration/IntegrationTestCase.php b/tests/Integration/IntegrationTestCase.php index e8cefb7..25d0087 100644 --- a/tests/Integration/IntegrationTestCase.php +++ b/tests/Integration/IntegrationTestCase.php @@ -52,6 +52,8 @@ protected function setUp(): void protected function tearDown(): void { $this->client->dropTable(self::TABLE_NAME); + $this->client->dropTable('Table1'); + $this->client->dropTable('Table2'); $this->logger->cleanLogs(); }