Skip to content

[PHPUnit/Install command] Integration testing fails if a custom command uses an entity repository (Might affect initial setup command) #39702

Open
@bhennesAdv

Description

@bhennesAdv

Summary

Hello,

As disclaimer, I'm not 100% sure if I'm doing everything good around this. I just want to be sure that this is an intended behavior.

Description:

Related documentation : https://developer.adobe.com/commerce/testing/guide/integration/
When executing integration testing on a magento custom instance, I encountered a crash :

---- /magento/dev/tests/integration> /usr/local/bin/php /magento/./vendor/phpunit/phpunit/phpunit  

Exception: Starting Magento installation:
File permissions check...
[Progress: 1 / 1791]
Required extensions check...
[Progress: 2 / 1791]
Enabling Maintenance Mode...
[Progress: 3 / 1791]
Installing deployment configuration...
[Progress: 4 / 1791]
Installing database schema:

In Mysql.php line 672:

  [Magento\Framework\DB\Adapter\TableNotFoundException (1146)]
  SQLSTATE[42S02]: Base table or view not found: 1146 Table 'test_database.eav_entity_type' doesn't exist, query was: SELECT `main_table`.* FROM `eav_entity_type` AS `main_table`


Exception trace:
  at /magento/vendor/magento/framework/DB/Adapter/Pdo/Mysql.php:672
 Magento\Framework\DB\Adapter\Pdo\Mysql->performQuery() at /magento/vendor/magento/framework/DB/Adapter/Pdo/Mysql.php:614
[...]

By diving into this to understand the issue, I find out the following :

  • The first step of the test is to execute an install command to run the tests on a clean database (See the stack trace error)
 Magento\Setup\Model\Installer->installSchema() at n/a:n/a
 call_user_func_array() at /magento/setup/src/Magento/Setup/Model/Installer.php:407
 Magento\Setup\Model\Installer->install() at /magento/setup/src/Magento/Setup/Console/Command/InstallCommand.php:237
 Magento\Setup\Console\Command\InstallCommand->execute() at /magento/vendor/symfony/console/Command/Command.php:326
 Symfony\Component\Console\Command\Command->run() at /magento/vendor/symfony/console/Application.php:1078
 Symfony\Component\Console\Application->doRunCommand() at /magento/vendor/symfony/console/Application.php:324
 Symfony\Component\Console\Application->doRun() at /magento/vendor/magento/framework/Console/Cli.php:118
 Magento\Framework\Console\Cli->doRun() at /magento/vendor/symfony/console/Application.php:175
 Symfony\Component\Console\Application->run() at /magento/bin/magento:23

During the installation script, the object manager is loaded in the following file :
https://github.com/magento/magento2/blob/2.4-develop/setup/src/Magento/Setup/Model/Installer.php

[...] // l.957 in the current file
    public function installSchema(array $request)
    {
        /** @var \Magento\Framework\Registry $registry */
        $registry = $this->objectManagerProvider->get()->get(\Magento\Framework\Registry::class); // <=== This call is triggering the error
        //For backward compatibility in install and upgrade scripts with enabled parallelization.
        $registry->register('setup-mode-enabled', true);
[...]

Stack trace is quiet unclear as the object manager is creating multiple instances of multiples objects for multiple DI.
What I found is that :
I have a module with a declared command using \Magento\Catalog\Api\ProductRepositoryInterface
Any repository or ResourceModel would have the same effect as \Magento\Eav\Model\Config::getEntityType is called by every \Magento\Eav\Model\Entity\AbstractEntity::setType which is called by any ResourceModel.

As we are currently installing the database (So tables are not available yet), the following call with always fail :

[...]
     $this->entityTypeCollectionFactory->create()->getData();
[...]

For me, the issue is not that deep but my understanding of the install process is really low.
IMHO :

  • Commands can use entity repository without impact.
  • Install command must not try to load all listed commands as these commands are not necessary at this point (Obviously, we just want to registry object for legacy purpose, no need to have all commands loaded)

Issue would be in the following file :
https://github.com/magento/magento2/blob/2.4-develop/setup/src/Magento/Setup/Model/ObjectManagerProvider.php

[...]
public function get()
[...]
           if (PHP_SAPI == 'cli') {
                $this->createCliCommands(); // <== For me, this is the issue here
            }
[...]

Magento should load a minimal object manager to get only the registry we want at this point and not load CliCommands here.
Commenting this line fixes the issue.

Please let me know if you can confirm that this is a code issue or if it's the intended behavior.
Kind Regards,
Baptiste

Examples

Adding a minimal command with the following code would probably trigger the issue

namespace Test\TestModule\Console;

class CommandWithRepositoryTest extends \Symfony\Component\Console\Command\Command
{
    public function __construct(
        private readonly \Magento\Catalog\Api\ProductRepositoryInterface $productRepository
    ){
        parent::__construct();
    }

    /**
     * @inheritDoc
     */
    protected function configure()
    {
        $this->setName('test:command');
        parent::configure();
    }
    /**
     * @inheritDoc
     */
    protected function execute(
        \Symfony\Component\Console\Input\InputInterface $input,
        \Symfony\Component\Console\Output\OutputInterface $output)
    : int{
        // Useless, we just want to test the command
        $this->productRepository->get('test');
        return 0;
    }
}

di.xml file :

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <type name="Magento\Framework\Console\CommandList">
        <arguments>
            <argument name="commands" xsi:type="array">
                <item name="TestCommand" xsi:type="object">Test\TestModule\Console\CommandWithRepositoryTest</item>
            </argument>
        </arguments>
    </type>
</config>

Then run a phpunit integration test (An install command should trigger the bug too)

Proposed solution

As I said before :

In my humble opinion, Magento should load a minimal object manager to get only the registry we want at this point and not load CliCommands here. This can be done by adding another method or a parameter to the ObjectManagerProvider to allow getting an ObjectManager without CliCommands.

Release note

No response

Triage and priority

  • Severity: S0 - Affects critical data or functionality and leaves users without workaround.
  • Severity: S1 - Affects critical data or functionality and forces users to employ a workaround.
  • Severity: S2 - Affects non-critical data or functionality and forces users to employ a workaround.
  • Severity: S3 - Affects non-critical data or functionality and does not force users to employ a workaround.
  • Severity: S4 - Affects aesthetics, professional look and feel, “quality” or “usability”.

Metadata

Metadata

Assignees

Labels

Issue: needs updateAdditional information is require, waiting for responseReported on 2.4.xIndicates original Magento version for the Issue report.Triage: Dev.ExperienceIssue related to Developer Experience and needs help with Triage to Confirm or Reject it

Type

No type

Projects

Status

Needs Update

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions