From c7b5e1e5d08e71407eac8465b26af803fafff4a3 Mon Sep 17 00:00:00 2001 From: Vladislav Date: Thu, 8 May 2025 11:45:27 +0300 Subject: [PATCH 01/11] feat(bundle): initialize bundle structure and composer config --- .github/.gitkeep | 0 .gitignore | 4 ++ .infrastructure/.gitkeep | 0 LICENSE | 21 +++++++++++ README.md | 81 +++++++++++++++++++++++++++++++++++++++- composer.json | 65 ++++++++++++++++++++++++++++++++ src/.gitkeep | 0 tests/.gitkeep | 0 8 files changed, 169 insertions(+), 2 deletions(-) create mode 100644 .github/.gitkeep create mode 100644 .gitignore create mode 100644 .infrastructure/.gitkeep create mode 100644 LICENSE create mode 100644 composer.json create mode 100644 src/.gitkeep create mode 100644 tests/.gitkeep diff --git a/.github/.gitkeep b/.github/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4214601 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +/.idea/ +/vendor/ +/composer.lock +.DS_Store diff --git a/.infrastructure/.gitkeep b/.infrastructure/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..7aab1b6 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 MacPaw Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 25a9e6d..8f02060 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,79 @@ -# behat-orm-context -Behat ORM context Bundle +# Behat ORM Context Bundle + +## Installation + +### Step 1: Download the Bundle + +Open a command console, enter your project directory and execute: + +#### Applications that use Symfony Flex [in progress](https://github.com/MacPaw/BehatRedisContext/issues/2) + +```bash +composer require --dev macpaw/behat-orm-context +``` + +#### Applications that don't use Symfony Flex + +Open a command console, enter your project directory and execute the following command to download the latest stable version of this bundle: + +```bash +composer require --dev macpaw/behat-orm-context +``` + +This command requires you to have Composer installed globally, as explained +in the [installation chapter](https://getcomposer.org/doc/00-intro.md) +of the Composer documentation. + + +Then, enable the bundle by adding it to the list of registered bundles +in the `app/AppKernel.php` file of your project: + +```php + ['test' => true], + ); + + // ... + } + + // ... +} +``` + +--- + +### Step 2: Configure Behat + +Go to `behat.yml`: + +```yaml +# ... + contexts: + - BehatOrmContext\Context\OrmContext +# ... +``` + +--- + +## Configuration + +By default, the bundle has the following configuration: + +```yaml +behat_orm_context: + kernel_reset_managers: [] +``` + +You can override it manually in your `config/packages/test/behat_orm_context.yaml`: + +--- + diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..7d6b2df --- /dev/null +++ b/composer.json @@ -0,0 +1,65 @@ +{ + "name": "macpaw/behat-orm-context", + "description": "Behat Context for testing Symfony ORM", + "type": "symfony-bundle", + "license": "MIT", + "keywords": [ + "MacPaw", + "symfony", + "behat", + "BDD", + "Context", + "ORM" + ], + "authors": [ + { + "name": "Vladislav Hanziuk", + "email": "ganzyukv@macpaw.com", + "homepage": "https://macpaw.com/", + "role": "Software Engineer" + }, + { + "name": "Serhii Donii", + "email": "serhii.donii@macpaw.com", + "homepage": "https://macpaw.com/", + "role": "Software Engineer" + }, + { + "name": "Yozhef Hisem", + "email": "hisemjo@gmail.com", + "homepage": "https://macpaw.com/", + "role": "Software Engineer" + } + ], + "require": { + "ext-json": "*", + "php": "^7.4 || ^8.0", + "behat/behat": "^3.0", + "symfony/config": "^4.4 || ^5.4 || ^6.0 || ^7.0", + "symfony/dependency-injection": "^4.4 || ^5.4.34 || ^6.0 || ^7.0.2", + "symfony/http-kernel": "^4.4 || ^5.4 || ^6.0 || ^7.0", + "macpaw/similar-arrays": "^1.0", + "doctrine/orm": "^2.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.4", + "phpunit/phpunit": "^9.3", + "slevomat/coding-standard": "^7.0", + "squizlabs/php_codesniffer": "^3.12" + }, + "autoload": { + "psr-4": { + "BehatOrmContext\\": "src" + } + }, + "autoload-dev": { + "psr-4": { + "BehatOrmContext\\Tests\\": "tests" + } + }, + "config": { + "allow-plugins": { + "dealerdirect/phpcodesniffer-composer-installer": true + } + } +} diff --git a/src/.gitkeep b/src/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/tests/.gitkeep b/tests/.gitkeep new file mode 100644 index 0000000..e69de29 From 23cecf5ea541dbe49e1996c3cbc20c749dfb24e4 Mon Sep 17 00:00:00 2001 From: Vladislav Date: Thu, 8 May 2025 15:11:07 +0300 Subject: [PATCH 02/11] feat(bundle): add Bundle and Extension classes --- CODE_OF_CONDUCT.md | 128 ++++++++++++++++++ SECURITY.md | 19 +++ src/.gitkeep | 0 src/BehatOrmContextBundle.php | 9 ++ src/Context/ORMContext.php | 96 +++++++++++++ .../BehatOrmContextExtension.php | 37 +++++ src/DependencyInjection/Configuration.php | 32 +++++ src/Resources/config/orm_context.xml | 22 +++ .../ResetManager/DoctrineResetManager.php | 38 ++++++ .../ResetManager/ResetManagerInterface.php | 14 ++ 10 files changed, 395 insertions(+) create mode 100644 CODE_OF_CONDUCT.md create mode 100644 SECURITY.md delete mode 100644 src/.gitkeep create mode 100644 src/BehatOrmContextBundle.php create mode 100644 src/Context/ORMContext.php create mode 100644 src/DependencyInjection/BehatOrmContextExtension.php create mode 100644 src/DependencyInjection/Configuration.php create mode 100644 src/Resources/config/orm_context.xml create mode 100644 src/Service/ResetManager/DoctrineResetManager.php create mode 100644 src/Service/ResetManager/ResetManagerInterface.php diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..f96e33e --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,128 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, religion, or sexual identity +and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the + overall community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or + advances of any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email + address, without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +yozhef@macpaw.com. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series +of actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or +permanent ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within +the community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.0, available at +https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. + +Community Impact Guidelines were inspired by [Mozilla's code of conduct +enforcement ladder](https://github.com/mozilla/diversity). + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see the FAQ at +https://www.contributor-covenant.org/faq. Translations are available at +https://www.contributor-covenant.org/translations. diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..8d26322 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,19 @@ +# Security Policy + +## Supported Versions + +| Version | Supported | +| ------- | ------------------ | +| 0.x.x | :white_check_mark: | +| 1.x.x | :white_check_mark: | + +## Reporting a Bug + +Report security bugs by emailing the lead maintainer at yozhef@macpaw.com or create [Issues](https://github.com/MacPaw/BehatMessengerContext/issues) + +## Reporting a Vulnerability + +Please report (suspected) security vulnerabilities to +**[yozhef@macpaw.com](mailto:yozhef@macpaw.com)**. You will receive a response from +us within 48 hours. If the issue is confirmed, we will release a patch as soon +as possible depending on complexity but historically within a few days. diff --git a/src/.gitkeep b/src/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/src/BehatOrmContextBundle.php b/src/BehatOrmContextBundle.php new file mode 100644 index 0000000..5e460e1 --- /dev/null +++ b/src/BehatOrmContextBundle.php @@ -0,0 +1,9 @@ +manager = $manager; + } + + /** + * @And I see :count entities :entityClass + */ + public function andISeeInRepository(int $count, string $entityClass): void + { + $this->seeInRepository($count, $entityClass); + } + + /** + * @Then I see :count entities :entityClass + */ + public function thenISeeInRepository(int $count, string $entityClass): void + { + $this->seeInRepository($count, $entityClass); + } + + /** + * @And I see entity :entity with id :id + */ + public function andISeeEntityInRepositoryWithId(string $entityClass, string $id): void + { + $this->seeInRepository(1, $entityClass, ['id' => $id]); + } + + /** + * @Then I see entity :entity with id :id + */ + public function thenISeeEntityInRepositoryWithId(string $entityClass, string $id): void + { + $this->seeInRepository(1, $entityClass, ['id' => $id]); + } + + /** + * @Then I see entity :entity with properties: + */ + public function andISeeEntityInRepositoryWithProperties(string $entityClass, PyStringNode $string): void + { + $expectedProperties = json_decode(trim($string->getRaw()), true, 512, JSON_THROW_ON_ERROR); + $this->seeInRepository(1, $entityClass, $expectedProperties); + } + + /** + * @param array $params + * + * @throws NonUniqueResultException + * @throws NoResultException + */ + private function seeInRepository(int $count, string $entityClass, ?array $params = null): void + { + $query = $this->manager->createQueryBuilder() + ->from($entityClass, 'e') + ->select('count(e)'); + + if (null !== $params) { + foreach ($params as $columnName => $columnValue) { + if ($columnValue === null) { + $query->andWhere(sprintf('e.%s IS NULL', $columnName)); + } else { + $query->andWhere(sprintf('e.%s = :%s', $columnName, $columnName)) + ->setParameter($columnName, $columnValue); + } + } + } + + $realCount = $query->getQuery() + ->getSingleScalarResult(); + + if ($count !== $realCount) { + throw new RuntimeException( + sprintf('Real count is %d, not %d', $realCount, $count), + ); + } + } +} diff --git a/src/DependencyInjection/BehatOrmContextExtension.php b/src/DependencyInjection/BehatOrmContextExtension.php new file mode 100644 index 0000000..ce64aea --- /dev/null +++ b/src/DependencyInjection/BehatOrmContextExtension.php @@ -0,0 +1,37 @@ + $configs + * + * {@inheritdoc} + */ + public function load(array $configs, ContainerBuilder $container): void + { + $config = $this->processConfiguration(new Configuration(), $configs); + + $loader = new XmlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config')); + $loader->load('orm_context.xml'); + + if (!empty($config['kernel_reset_managers'])) { + $contextDefinition = $container->findDefinition(ORMContext::class); + + foreach ($config['kernel_reset_managers'] as $resetManager) { + $contextDefinition->addMethodCall('addKernelResetManager', [ + $container->findDefinition($resetManager) + ]); + } + } + } +} diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php new file mode 100644 index 0000000..6ffa934 --- /dev/null +++ b/src/DependencyInjection/Configuration.php @@ -0,0 +1,32 @@ +getRootNode()->children(); + + $this->addKernelResetManagersSection($root); + + return $treeBuilder; + } + + private function addKernelResetManagersSection(NodeBuilder $builder): void + { + $builder + ->arrayNode('kernel_reset_managers') + ->scalarPrototype() + ->end() + ->end() + ->end(); + } +} diff --git a/src/Resources/config/orm_context.xml b/src/Resources/config/orm_context.xml new file mode 100644 index 0000000..8a22e1a --- /dev/null +++ b/src/Resources/config/orm_context.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + diff --git a/src/Service/ResetManager/DoctrineResetManager.php b/src/Service/ResetManager/DoctrineResetManager.php new file mode 100644 index 0000000..45139ff --- /dev/null +++ b/src/Service/ResetManager/DoctrineResetManager.php @@ -0,0 +1,38 @@ +getContainer(); + + if ($container->hasParameter('doctrine.entity_managers')) { + /** @var array $entityManagers */ + $entityManagers = $container->getParameter('doctrine.entity_managers'); + + foreach ($entityManagers as $entityManagerId) { + if ($container->initialized($entityManagerId)) { + $em = $container->get($entityManagerId); + $em->clear(); + + $connection = $em->getConnection(); + + if ($connection->isConnected()) { + $connection->close(); + } + } + } + } + } +} diff --git a/src/Service/ResetManager/ResetManagerInterface.php b/src/Service/ResetManager/ResetManagerInterface.php new file mode 100644 index 0000000..8a797fc --- /dev/null +++ b/src/Service/ResetManager/ResetManagerInterface.php @@ -0,0 +1,14 @@ + Date: Thu, 8 May 2025 15:32:55 +0300 Subject: [PATCH 03/11] feat(bundle): add basic test setup and code style checks --- .gitignore | 1 + composer.json | 14 +++++++++++ phpcs.xml.dist | 45 +++++++++++++++++++++++++++++++++++ phpstan.neon.dist | 25 +++++++++++++++++++ phpunit.xml.dist | 25 +++++++++++++++++++ src/BehatOrmContextBundle.php | 2 ++ 6 files changed, 112 insertions(+) create mode 100644 phpcs.xml.dist create mode 100644 phpstan.neon.dist create mode 100644 phpunit.xml.dist diff --git a/.gitignore b/.gitignore index 4214601..1d073d6 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ /vendor/ /composer.lock .DS_Store +/.phpunit.result.cache diff --git a/composer.json b/composer.json index 7d6b2df..ade2bb0 100644 --- a/composer.json +++ b/composer.json @@ -57,6 +57,20 @@ "BehatOrmContext\\Tests\\": "tests" } }, + "scripts": { + "composer-validate": "composer validate", + "phpstan": "./vendor/bin/phpstan analyse -l max", + "code-style": "./vendor/bin/phpcs", + "code-style-fix": "./vendor/bin/phpcbf", + "phpunit": "./vendor/bin/phpunit", + "phpunit-html-coverage": "XDEBUG_MODE=coverage ./vendor/bin/phpunit --coverage-html=coverage", + "dev-checks": [ + "composer validate", + "@phpstan", + "@code-style", + "@phpunit" + ] + }, "config": { "allow-plugins": { "dealerdirect/phpcodesniffer-composer-installer": true diff --git a/phpcs.xml.dist b/phpcs.xml.dist new file mode 100644 index 0000000..a3ac003 --- /dev/null +++ b/phpcs.xml.dist @@ -0,0 +1,45 @@ + + + The coding standard of Behat ORM Context package + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + src/ + tests/ + diff --git a/phpstan.neon.dist b/phpstan.neon.dist new file mode 100644 index 0000000..74422ed --- /dev/null +++ b/phpstan.neon.dist @@ -0,0 +1,25 @@ +parameters: + excludes_analyse: + paths: + - src + level: max + checkExplicitMixed: false + ignoreErrors: + - + message: '#Call to an undefined method Symfony\\Component\\Config\\Definition\\Builder\\NodeDefinition::children\(\).#' + count: 1 + path: ./src/DependencyInjection + - + message: '#.*NodeParentInterface|null.*#' + count: 1 + path: ./src/DependencyInjection + - + message: '#Call to an undefined method object::clear().*#' + count: 1 + path: ./src/Service/ResetManager/DoctrineResetManager + - + message: '#Call to an undefined method object::getConnection().*#' + count: 1 + path: ./src/Service/ResetManager/DoctrineResetManager + - + identifier: missingType.iterableValue diff --git a/phpunit.xml.dist b/phpunit.xml.dist new file mode 100644 index 0000000..9aacc0e --- /dev/null +++ b/phpunit.xml.dist @@ -0,0 +1,25 @@ + + + + + tests + + + + + ./src + + + + + + diff --git a/src/BehatOrmContextBundle.php b/src/BehatOrmContextBundle.php index 5e460e1..4529143 100644 --- a/src/BehatOrmContextBundle.php +++ b/src/BehatOrmContextBundle.php @@ -2,6 +2,8 @@ declare(strict_types=1); +namespace BehatOrmContext; + use Symfony\Component\HttpKernel\Bundle\Bundle; class BehatOrmContextBundle extends Bundle From c58ff125786a5b7aa9bb181f8aadd45a7a2163a1 Mon Sep 17 00:00:00 2001 From: Vladislav Date: Fri, 9 May 2025 13:49:56 +0300 Subject: [PATCH 04/11] feat(bundle): add bundle extension, configuration, and tests --- phpstan.neon.dist | 16 -- .../BehatOrmContextExtension.php | 15 +- src/DependencyInjection/Configuration.php | 18 +- src/Resources/config/orm_context.xml | 9 - .../ResetManager/DoctrineResetManager.php | 38 ---- .../ResetManager/ResetManagerInterface.php | 14 -- tests/.gitkeep | 0 .../BehatOrmContextExtensionTest.php | 35 ++++ .../DependencyInjection/ConfigurationTest.php | 25 +++ tests/Unit/Context/ORMContextTest.php | 177 ++++++++++++++++++ 10 files changed, 239 insertions(+), 108 deletions(-) delete mode 100644 src/Service/ResetManager/DoctrineResetManager.php delete mode 100644 src/Service/ResetManager/ResetManagerInterface.php delete mode 100644 tests/.gitkeep create mode 100644 tests/DependencyInjection/BehatOrmContextExtensionTest.php create mode 100644 tests/DependencyInjection/ConfigurationTest.php create mode 100644 tests/Unit/Context/ORMContextTest.php diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 74422ed..556922c 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -5,21 +5,5 @@ parameters: level: max checkExplicitMixed: false ignoreErrors: - - - message: '#Call to an undefined method Symfony\\Component\\Config\\Definition\\Builder\\NodeDefinition::children\(\).#' - count: 1 - path: ./src/DependencyInjection - - - message: '#.*NodeParentInterface|null.*#' - count: 1 - path: ./src/DependencyInjection - - - message: '#Call to an undefined method object::clear().*#' - count: 1 - path: ./src/Service/ResetManager/DoctrineResetManager - - - message: '#Call to an undefined method object::getConnection().*#' - count: 1 - path: ./src/Service/ResetManager/DoctrineResetManager - identifier: missingType.iterableValue diff --git a/src/DependencyInjection/BehatOrmContextExtension.php b/src/DependencyInjection/BehatOrmContextExtension.php index ce64aea..65b8436 100644 --- a/src/DependencyInjection/BehatOrmContextExtension.php +++ b/src/DependencyInjection/BehatOrmContextExtension.php @@ -4,10 +4,9 @@ namespace BehatOrmContext\DependencyInjection; -use BehatOrmContext\Context\ORMContext; use Symfony\Component\Config\FileLocator; use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Extension\Extension; +use Symfony\Component\HttpKernel\DependencyInjection\Extension; use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; class BehatOrmContextExtension extends Extension @@ -19,19 +18,7 @@ class BehatOrmContextExtension extends Extension */ public function load(array $configs, ContainerBuilder $container): void { - $config = $this->processConfiguration(new Configuration(), $configs); - $loader = new XmlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config')); $loader->load('orm_context.xml'); - - if (!empty($config['kernel_reset_managers'])) { - $contextDefinition = $container->findDefinition(ORMContext::class); - - foreach ($config['kernel_reset_managers'] as $resetManager) { - $contextDefinition->addMethodCall('addKernelResetManager', [ - $container->findDefinition($resetManager) - ]); - } - } } } diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php index 6ffa934..c0f9c7b 100644 --- a/src/DependencyInjection/Configuration.php +++ b/src/DependencyInjection/Configuration.php @@ -4,7 +4,6 @@ namespace BehatOrmContext\DependencyInjection; -use Symfony\Component\Config\Definition\Builder\NodeBuilder; use Symfony\Component\Config\Definition\Builder\TreeBuilder; use Symfony\Component\Config\Definition\ConfigurationInterface; @@ -12,21 +11,6 @@ class Configuration implements ConfigurationInterface { public function getConfigTreeBuilder(): TreeBuilder { - $treeBuilder = new TreeBuilder('behat_orm_context'); - $root = $treeBuilder->getRootNode()->children(); - - $this->addKernelResetManagersSection($root); - - return $treeBuilder; - } - - private function addKernelResetManagersSection(NodeBuilder $builder): void - { - $builder - ->arrayNode('kernel_reset_managers') - ->scalarPrototype() - ->end() - ->end() - ->end(); + return new TreeBuilder('behat_orm_context'); } } diff --git a/src/Resources/config/orm_context.xml b/src/Resources/config/orm_context.xml index 8a22e1a..bd82e7f 100644 --- a/src/Resources/config/orm_context.xml +++ b/src/Resources/config/orm_context.xml @@ -2,7 +2,6 @@ - - - - - diff --git a/src/Service/ResetManager/DoctrineResetManager.php b/src/Service/ResetManager/DoctrineResetManager.php deleted file mode 100644 index 45139ff..0000000 --- a/src/Service/ResetManager/DoctrineResetManager.php +++ /dev/null @@ -1,38 +0,0 @@ -getContainer(); - - if ($container->hasParameter('doctrine.entity_managers')) { - /** @var array $entityManagers */ - $entityManagers = $container->getParameter('doctrine.entity_managers'); - - foreach ($entityManagers as $entityManagerId) { - if ($container->initialized($entityManagerId)) { - $em = $container->get($entityManagerId); - $em->clear(); - - $connection = $em->getConnection(); - - if ($connection->isConnected()) { - $connection->close(); - } - } - } - } - } -} diff --git a/src/Service/ResetManager/ResetManagerInterface.php b/src/Service/ResetManager/ResetManagerInterface.php deleted file mode 100644 index 8a797fc..0000000 --- a/src/Service/ResetManager/ResetManagerInterface.php +++ /dev/null @@ -1,14 +0,0 @@ -load([], $container); + + self::assertInstanceOf(Extension::class, $extension); + + self::assertTrue($container->has(OrmContext::class)); + } + + public function testOrmContextIsCorrectlyDefined(): void + { + $extension = new BehatOrmContextExtension(); + $container = new ContainerBuilder(); + $extension->load([], $container); + + $definition = $container->getDefinition(OrmContext::class); + self::assertSame(OrmContext::class, $definition->getClass()); + } +} diff --git a/tests/DependencyInjection/ConfigurationTest.php b/tests/DependencyInjection/ConfigurationTest.php new file mode 100644 index 0000000..298dded --- /dev/null +++ b/tests/DependencyInjection/ConfigurationTest.php @@ -0,0 +1,25 @@ +processConfiguration($configuration, []); + + self::assertSame([], $configs); + } +} diff --git a/tests/Unit/Context/ORMContextTest.php b/tests/Unit/Context/ORMContextTest.php new file mode 100644 index 0000000..f721a9d --- /dev/null +++ b/tests/Unit/Context/ORMContextTest.php @@ -0,0 +1,177 @@ +createContext('App\Entity\SomeEntity', self::COUNT); + $context->andISeeInRepository(self::COUNT, 'App\Entity\SomeEntity'); + } + + public function testAndISeeCountInRepositoryFailed(): void + { + $context = $this->createContext('App\Entity\SomeEntity', self::COUNT); + self::expectException(RuntimeException::class); + $context->andISeeInRepository(self::COUNT + 1, 'App\Entity\SomeEntity'); + } + + public function testThenISeeCountInRepository(): void + { + $context = $this->createContext('App\Entity\SomeEntity', self::COUNT); + $context->thenISeeInRepository(self::COUNT, 'App\Entity\SomeEntity'); + } + + public function testThenISeeCountInRepositoryFailed(): void + { + $context = $this->createContext('App\Entity\SomeEntity', self::COUNT); + self::expectException(RuntimeException::class); + $context->thenISeeInRepository(self::COUNT + 1, 'App\Entity\SomeEntity'); + } + + public function testThenISeeCountInRepositoryWithId(): void + { + $context = $this->createContext( + 'App\Entity\SomeEntity', + 1, + ['id' => self::UUID], + ); + $context->thenISeeEntityInRepositoryWithId( + 'App\Entity\SomeEntity', + self::UUID, + ); + } + + public function testThenISeeCountInRepositoryWithIdFailed(): void + { + $context = $this->createContext( + 'App\Entity\SomeEntity', + 1, + ['id' => self::UUID], + ); + $context->andISeeEntityInRepositoryWithId( + 'App\Entity\SomeEntity', + self::UUID, + ); + } + + public function testThenISeeEntityInRepositoryWithProperties(): void + { + $context = $this->createContext( + 'App\Entity\SomeEntity', + 1, + [ + 'id' => self::UUID, + 'someProperty' => 'someValue', + 'otherProperty' => 'otherValue', + ], + ); + $context->andISeeEntityInRepositoryWithProperties( + 'App\Entity\SomeEntity', + new PyStringNode([ + <<<'PSN' + { + "id": "e809639f-011a-4ae0-9ae3-8fcb460fe950", + "someProperty": "someValue", + "otherProperty": "otherValue" + } + PSN + ], 1), + ); + } + + public function testThenISeeEntityInRepositoryWithPropertyNull(): void + { + $context = $this->createContext( + 'App\Entity\SomeEntity', + 1, + [ + 'id' => self::UUID, + 'someProperty' => null, + ], + ); + $context->andISeeEntityInRepositoryWithProperties( + 'App\Entity\SomeEntity', + new PyStringNode([ + <<<'PSN' + { + "id": "e809639f-011a-4ae0-9ae3-8fcb460fe950", + "someProperty": null + } + PSN + ], 1), + ); + } + + private function createContext( + string $entityName, + int $count = 1, + ?array $properties = null + ): ORMContext { + $queryMock = $this->getMockBuilder(Query::class) + ->disableOriginalConstructor() + ->getMock(); + + $queryMock->expects(self::once()) + ->method('getSingleScalarResult') + ->willReturn($count); + + $entityManagerMock = $this->getMockBuilder(EntityManagerInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $queryBuilderMock = $this->getMockBuilder(QueryBuilder::class) + ->disableOriginalConstructor() + ->getMock(); + + $queryBuilderMock->expects(self::once()) + ->method('from') + ->with( + $entityName, + 'e', + )->willReturn($queryBuilderMock); + + if (null !== $properties) { + foreach ($properties as $name => $value) { + $queryBuilderMock->expects(self::exactly(count($properties))) + ->method('andWhere') + ->willReturnSelf(); + $setParametersCount = count(array_filter($properties, function ($value) { + return !is_null($value); + })); + $queryBuilderMock->expects(self::exactly($setParametersCount)) + ->method('setParameter') + ->willReturnSelf(); + } + } + + $queryBuilderMock->expects(self::once()) + ->method('select') + ->with('count(e)') + ->willReturn($queryBuilderMock); + + $queryBuilderMock->expects(self::once()) + ->method('getQuery') + ->willReturn($queryMock); + + $entityManagerMock->expects(self::once()) + ->method('createQueryBuilder') + ->willReturn($queryBuilderMock); + + return new ORMContext($entityManagerMock); + } +} From a8adbb3d7f9ab451db142da93c2d6c6e231ea92a Mon Sep 17 00:00:00 2001 From: Vladislav Date: Fri, 9 May 2025 13:57:03 +0300 Subject: [PATCH 05/11] feat(bundle): add bundle extension, configuration, and tests --- README.md | 1 - composer.json | 6 +++--- phpstan.neon.dist | 3 --- src/DependencyInjection/BehatOrmContextExtension.php | 2 +- 4 files changed, 4 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 8f02060..c21145a 100644 --- a/README.md +++ b/README.md @@ -70,7 +70,6 @@ By default, the bundle has the following configuration: ```yaml behat_orm_context: - kernel_reset_managers: [] ``` You can override it manually in your `config/packages/test/behat_orm_context.yaml`: diff --git a/composer.json b/composer.json index ade2bb0..8967c9e 100644 --- a/composer.json +++ b/composer.json @@ -42,9 +42,9 @@ "doctrine/orm": "^2.0" }, "require-dev": { - "phpstan/phpstan": "^1.4", - "phpunit/phpunit": "^9.3", - "slevomat/coding-standard": "^7.0", + "phpstan/phpstan": "^1.12", + "phpunit/phpunit": "^9.6", + "slevomat/coding-standard": "^7.2", "squizlabs/php_codesniffer": "^3.12" }, "autoload": { diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 556922c..fbb2748 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -4,6 +4,3 @@ parameters: - src level: max checkExplicitMixed: false - ignoreErrors: - - - identifier: missingType.iterableValue diff --git a/src/DependencyInjection/BehatOrmContextExtension.php b/src/DependencyInjection/BehatOrmContextExtension.php index 65b8436..5cfb72c 100644 --- a/src/DependencyInjection/BehatOrmContextExtension.php +++ b/src/DependencyInjection/BehatOrmContextExtension.php @@ -12,7 +12,7 @@ class BehatOrmContextExtension extends Extension { /** - * @param array $configs + * @param array> $configs * * {@inheritdoc} */ From 27412394832ba4efbe461c14874e1b85129e33ac Mon Sep 17 00:00:00 2001 From: Vladislav Date: Fri, 9 May 2025 14:06:19 +0300 Subject: [PATCH 06/11] feat(bundle): add docker and config for CI process --- .github/.gitkeep | 0 .github/workflows/ci.yaml | 98 ++++++++++++++++++++++++++ .github/workflows/security.yaml | 24 +++++++ .github/workflows/static-analysis.yaml | 55 +++++++++++++++ .infrastructure/.gitkeep | 0 .infrastructure/docker/Dockerfile | 10 +++ docker-compose.yaml | 14 ++++ 7 files changed, 201 insertions(+) delete mode 100644 .github/.gitkeep create mode 100644 .github/workflows/ci.yaml create mode 100644 .github/workflows/security.yaml create mode 100644 .github/workflows/static-analysis.yaml delete mode 100644 .infrastructure/.gitkeep create mode 100644 .infrastructure/docker/Dockerfile create mode 100644 docker-compose.yaml diff --git a/.github/.gitkeep b/.github/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 0000000..11fdc48 --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,98 @@ +name: CI + +on: + pull_request: ~ + +jobs: + run: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + php: + - '8.1' + - '8.2' + - '8.3' + coverage: ['none'] + symfony-versions: + - '4.4.*' + - '5.4.*' + - '6.0.*' + - '6.2.*' + - '7.0.*' + exclude: + - php: '8.1' + symfony-versions: '7.0.*' + include: + - php: '7.4' + symfony-versions: '^4.4' + coverage: 'none' + - php: '7.4' + symfony-versions: '^5.4' + coverage: 'none' + - php: '8.0' + symfony-versions: '^4.4' + coverage: 'none' + - php: '8.0' + symfony-versions: '^5.4' + coverage: 'none' + - php: '8.2' + coverage: 'xdebug' + symfony-versions: '^7.0' + description: 'Log Code Coverage' + + name: PHP ${{ matrix.php }} Symfony ${{ matrix.symfony-versions }} ${{ matrix.description }} + steps: + - name: Checkout + uses: actions/checkout@v2 + + - uses: actions/cache@v4 + 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@v4 + 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 + + - name: Install dependencies + run: composer install + + - name: Run PHPUnit tests + run: composer phpunit + if: matrix.coverage == 'none' + + - name: PHPUnit tests and Log Code coverage + run: vendor/bin/phpunit --coverage-clover=coverage.xml + if: matrix.coverage == 'xdebug' + + - name: Run codecov + uses: codecov/codecov-action@v4.0.1 + if: matrix.coverage == 'xdebug' + with: + token: ${{ secrets.CODECOV_TOKEN }} + file: './coverage.xml' + fail_ci_if_error: true 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/.github/workflows/static-analysis.yaml b/.github/workflows/static-analysis.yaml new file mode 100644 index 0000000..c54cc67 --- /dev/null +++ b/.github/workflows/static-analysis.yaml @@ -0,0 +1,55 @@ +name: Code style and static analysis + +on: + pull_request: + push: + branches: [ main, develop ] + +jobs: + php-cs-fixer: + name: PHP-CS-Fixer + 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: Run script + run: composer code-style + + phpstan: + name: PHPStan + 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: Run script + run: composer phpstan + + composer-validate: + name: Composer validate + 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: Run script + run: composer composer-validate diff --git a/.infrastructure/.gitkeep b/.infrastructure/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/.infrastructure/docker/Dockerfile b/.infrastructure/docker/Dockerfile new file mode 100644 index 0000000..22e32ad --- /dev/null +++ b/.infrastructure/docker/Dockerfile @@ -0,0 +1,10 @@ +ARG from_image + +FROM $from_image + +ENV XDEBUG_MODE=off + +RUN apk add --no-cache bash linux-headers $PHPIZE_DEPS \ + && pecl install xdebug \ + && docker-php-ext-enable xdebug \ + && curl --silent https://getcomposer.org/composer-stable.phar -o /usr/bin/composer && chmod a+x /usr/bin/composer diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100644 index 0000000..cf844e5 --- /dev/null +++ b/docker-compose.yaml @@ -0,0 +1,14 @@ +version: '3.9' + +services: + php80: + build: + context: .infrastructure + dockerfile: docker/Dockerfile + args: + from_image: php:8.0-fpm-alpine + working_dir: /app + environment: + PATH: "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/app/vendor/bin" + volumes: + - ./:/app From 5eef53ad633132a75a4251f2ba66f6fb9a523da3 Mon Sep 17 00:00:00 2001 From: Vladislav Date: Tue, 13 May 2025 18:10:04 +0300 Subject: [PATCH 07/11] feat(ci): replace deprecated set-output with GITHUB_OUTPUT --- .github/workflows/ci.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 11fdc48..8ccdf51 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -62,7 +62,7 @@ jobs: - name: Set composer cache directory id: composer-cache - run: echo "::set-output name=dir::$(composer config cache-files-dir)" + run: echo "dir=$(composer config cache-files-dir)" >> "$GITHUB_OUTPUT" - name: Cache composer uses: actions/cache@v4 From 04ec96a7f8265de03fdf308fbd696e344022a2cc Mon Sep 17 00:00:00 2001 From: Vladislav Date: Tue, 13 May 2025 18:14:53 +0300 Subject: [PATCH 08/11] feat(bundle): add build statuses to readme.md --- README.md | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index c21145a..3d61107 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,10 @@ # Behat ORM Context Bundle +| Version | Build Status | Code Coverage | +|:---------:|:---------------------------------------------------------:|:------------------------------------------------------------------------:| +| `master` | [![CI][master Build Status Image]][master Build Status] | [![Coverage Status][master Code Coverage Image]][master Code Coverage] | +| `develop` | [![CI][develop Build Status Image]][develop Build Status] | [![Coverage Status][develop Code Coverage Image]][develop Code Coverage] | + ## Installation ### Step 1: Download the Bundle @@ -14,7 +19,8 @@ composer require --dev macpaw/behat-orm-context #### Applications that don't use Symfony Flex -Open a command console, enter your project directory and execute the following command to download the latest stable version of this bundle: +Open a command console, enter your project directory and execute the following command to download the latest stable +version of this bundle: ```bash composer require --dev macpaw/behat-orm-context @@ -24,7 +30,6 @@ This command requires you to have Composer installed globally, as explained in the [installation chapter](https://getcomposer.org/doc/00-intro.md) of the Composer documentation. - Then, enable the bundle by adding it to the list of registered bundles in the `app/AppKernel.php` file of your project: @@ -57,8 +62,8 @@ Go to `behat.yml`: ```yaml # ... - contexts: - - BehatOrmContext\Context\OrmContext +contexts: + - BehatOrmContext\Context\OrmContext # ... ``` @@ -76,3 +81,13 @@ You can override it manually in your `config/packages/test/behat_orm_context.yam --- + +[master Build Status]: https://github.com/macpaw/behat-orm-context/actions?query=workflow%3ACI+branch%3Amaster +[master Build Status Image]: https://github.com/macpaw/behat-orm-context/workflows/CI/badge.svg?branch=master +[develop Build Status]: https://github.com/macpaw/behat-orm-context/actions?query=workflow%3ACI+branch%3Adevelop +[develop Build Status Image]: https://github.com/macpaw/behat-orm-context/workflows/CI/badge.svg?branch=develop +[master Code Coverage]: https://codecov.io/gh/macpaw/behat-orm-context/branch/master +[master Code Coverage Image]: https://img.shields.io/codecov/c/github/macpaw/behat-orm-context/master?logo=codecov +[develop Code Coverage]: https://codecov.io/gh/macpaw/behat-orm-context/branch/develop +[develop Code Coverage Image]: https://img.shields.io/codecov/c/github/macpaw/behat-orm-context/develop?logo=codecov + From b396dd218be380d8fe4407253b23bec102de50a1 Mon Sep 17 00:00:00 2001 From: Yozhef Hisem Date: Fri, 16 May 2025 15:17:20 +0300 Subject: [PATCH 09/11] feat: orc-11 create documentation method --- docs/ORMContext/see-count-in-repository.md | 26 ++++++++++++ .../see-entity-in-repository-with-id.md | 26 ++++++++++++ ...ee-entity-in-repository-with-properties.md | 40 +++++++++++++++++++ 3 files changed, 92 insertions(+) create mode 100644 docs/ORMContext/see-count-in-repository.md create mode 100644 docs/ORMContext/see-entity-in-repository-with-id.md create mode 100644 docs/ORMContext/see-entity-in-repository-with-properties.md diff --git a/docs/ORMContext/see-count-in-repository.md b/docs/ORMContext/see-count-in-repository.md new file mode 100644 index 0000000..0db7c27 --- /dev/null +++ b/docs/ORMContext/see-count-in-repository.md @@ -0,0 +1,26 @@ +### I See X Entities in Repository + +#### Step Definition: + +This step checks if the database contains exactly the specified number of entities for a given entity class. + +#### Gherkin Example: + +```gherkin +And I see 5 entities "App\Entity\User" +``` + +#### Description: + +This step allows you to verify that a specific number of entities exist in the database. It executes a count query on the database for the specified entity class and verifies that the result matches the expected count. + +#### Parameters: + +- `count`: The expected number of entities +- `entityClass`: The fully-qualified class name of the entity to check + +#### Use Cases: + +- Verifying that data setup was successful +- Checking that operations have created/deleted the expected number of records +- Validating database state after data manipulation steps \ No newline at end of file diff --git a/docs/ORMContext/see-entity-in-repository-with-id.md b/docs/ORMContext/see-entity-in-repository-with-id.md new file mode 100644 index 0000000..2b88155 --- /dev/null +++ b/docs/ORMContext/see-entity-in-repository-with-id.md @@ -0,0 +1,26 @@ +### I See Entity with Specific ID + +#### Step Definition: + +This step checks if the database contains an entity of a specific type with the given ID. + +#### Gherkin Example: + +```gherkin +And I see entity "App\Entity\Product" with id "abc123" +``` + +#### Description: + +This step allows you to verify that a specific entity exists in the database by its ID. It performs a count query with a condition on the ID field and expects exactly one matching entity. + +#### Parameters: + +- `entityClass`: The fully-qualified class name of the entity to check +- `id`: The ID value to match against the entity's ID field + +#### Use Cases: + +- Verifying that a specific record exists after creation +- Confirming entity persistence during a multi-step process +- Checking that an entity with a specific identifier is still present \ No newline at end of file diff --git a/docs/ORMContext/see-entity-in-repository-with-properties.md b/docs/ORMContext/see-entity-in-repository-with-properties.md new file mode 100644 index 0000000..1187bb2 --- /dev/null +++ b/docs/ORMContext/see-entity-in-repository-with-properties.md @@ -0,0 +1,40 @@ +### I See Entity with Specific Properties + +#### Step Definition: + +This step checks if the database contains an entity of a specific type with the given properties. + +#### Gherkin Example: + +```gherkin +Then I see entity "App\Entity\User" with properties: + """ + { + "email": "user@example.com", + "firstName": "John", + "lastName": "Doe", + "active": true + } + """ +``` + +#### Description: + +This step allows you to verify that a specific entity exists in the database by matching multiple property values. It performs a count query with conditions on each specified property and expects exactly one matching entity. + +#### Parameters: + +- `entityClass`: The fully-qualified class name of the entity to check +- `properties`: A JSON string containing key-value pairs representing entity properties to match + +#### Use Cases: + +- Verifying that an entity with specific field values exists +- Checking that entity properties match expected values after operations +- Validating complex entity state with multiple property conditions +- Testing business logic that modifies entity properties + +#### Notes: + +- Properties with `null` values are queried using `IS NULL` condition +- All other properties are matched using equality \ No newline at end of file From af0940f58496be04a954d02048684381bb2ba3bc Mon Sep 17 00:00:00 2001 From: Yozhef Hisem Date: Fri, 16 May 2025 15:22:00 +0300 Subject: [PATCH 10/11] feat: orc-11 create documentation method --- README.md | 79 ++++++++----------------------------------------- docs/install.md | 71 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+), 67 deletions(-) create mode 100644 docs/install.md diff --git a/README.md b/README.md index 3d61107..d47c4bc 100644 --- a/README.md +++ b/README.md @@ -5,82 +5,27 @@ | `master` | [![CI][master Build Status Image]][master Build Status] | [![Coverage Status][master Code Coverage Image]][master Code Coverage] | | `develop` | [![CI][develop Build Status Image]][develop Build Status] | [![Coverage Status][develop Code Coverage Image]][develop Code Coverage] | -## Installation - -### Step 1: Download the Bundle - -Open a command console, enter your project directory and execute: - -#### Applications that use Symfony Flex [in progress](https://github.com/MacPaw/BehatRedisContext/issues/2) - -```bash -composer require --dev macpaw/behat-orm-context -``` - -#### Applications that don't use Symfony Flex - -Open a command console, enter your project directory and execute the following command to download the latest stable -version of this bundle: - -```bash -composer require --dev macpaw/behat-orm-context -``` - -This command requires you to have Composer installed globally, as explained -in the [installation chapter](https://getcomposer.org/doc/00-intro.md) -of the Composer documentation. +Behat context for testing Doctrine ORM integration. -Then, enable the bundle by adding it to the list of registered bundles -in the `app/AppKernel.php` file of your project: +## Description -```php - ['test' => true], - ); - - // ... - } - - // ... -} -``` - ---- - -### Step 2: Configure Behat - -Go to `behat.yml`: - -```yaml -# ... -contexts: - - BehatOrmContext\Context\OrmContext -# ... -``` - ---- +## Installation -## Configuration +See the [installation instructions](docs/install.md). -By default, the bundle has the following configuration: +## Features -```yaml -behat_orm_context: -``` +The bundle provides several Behat step definitions for ORM testing: -You can override it manually in your `config/packages/test/behat_orm_context.yaml`: +* [See X entities in repository](docs/ORMContext/see-count-in-repository.md) - Check if the count of entities in the repository matches expected +* [See entity with ID](docs/ORMContext/see-entity-in-repository-with-id.md) - Check if an entity with a specific ID exists +* [See entity with properties](docs/ORMContext/see-entity-in-repository-with-properties.md) - Check if an entity with specific properties exists ---- +## License +This bundle is released under the MIT license. See the included [LICENSE](LICENSE) file for more information. [master Build Status]: https://github.com/macpaw/behat-orm-context/actions?query=workflow%3ACI+branch%3Amaster [master Build Status Image]: https://github.com/macpaw/behat-orm-context/workflows/CI/badge.svg?branch=master diff --git a/docs/install.md b/docs/install.md new file mode 100644 index 0000000..3e44b62 --- /dev/null +++ b/docs/install.md @@ -0,0 +1,71 @@ +# Installation + +## Step 1: Download the Bundle + +Open a command console, enter your project directory and execute: + +### Applications that use Symfony Flex + +```bash + composer require --dev macpaw/behat-orm-context +``` + +### Applications that don't use Symfony Flex + +Open a command console, enter your project directory and execute the following command to download the latest stable +version of this bundle: + +```bash + composer require --dev macpaw/behat-orm-context +``` + +This command requires you to have Composer installed globally, as explained +in the [installation chapter](https://getcomposer.org/doc/00-intro.md) +of the Composer documentation. + +Then, enable the bundle by adding it to the list of registered bundles +in the `app/AppKernel.php` file of your project: + +```php + ['test' => true], + ); + + // ... + } + + // ... +} +``` + +## Step 2: Configure Behat + +Go to `behat.yml`: + +```yaml +# ... +contexts: + - BehatOrmContext\Context\OrmContext: + manager: '@doctrine.orm.entity_manager' +# ... +``` + +## Configuration + +By default, the bundle has the following configuration: + +```yaml +behat_orm_context: + # Currently no specific configuration options are available +``` + +You can override it manually in your `config/packages/test/behat_orm_context.yaml`. \ No newline at end of file From 6644a4553c06a3b59ad7ffaceae3873fa56f7572 Mon Sep 17 00:00:00 2001 From: Yozhef Hisem Date: Fri, 16 May 2025 17:43:15 +0300 Subject: [PATCH 11/11] feat: orc-11 create documentation method --- docs/install.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/install.md b/docs/install.md index 3e44b62..ee3e510 100644 --- a/docs/install.md +++ b/docs/install.md @@ -54,8 +54,7 @@ Go to `behat.yml`: ```yaml # ... contexts: - - BehatOrmContext\Context\OrmContext: - manager: '@doctrine.orm.entity_manager' + - BehatOrmContext\Context\OrmContext # ... ```