Skip to content

Commit

Permalink
feat(WPLoader) addd wpdb connection id strict check
Browse files Browse the repository at this point in the history
  • Loading branch information
lucatume committed May 25, 2024
1 parent a987715 commit fc65ae6
Show file tree
Hide file tree
Showing 5 changed files with 218 additions and 3 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
### Changed

- Better messaging when throwing due to disconnected database.
- Add the `WPLoader::beStrictAboutWpdbConnectionId` configuration parameter, defaults to `true`, to throw if db connection changes during setup before class.

### Fixed

Expand Down
6 changes: 5 additions & 1 deletion includes/core-phpunit/includes/abstract-testcase.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<?php

use lucatume\WPBrowser\TestCase\WPTestCase;

require_once __DIR__ . '/factory.php';
require_once __DIR__ . '/trac.php';

Expand Down Expand Up @@ -71,8 +73,10 @@ public static function set_up_before_class() {

$wpdb->suppress_errors = false;
$wpdb->show_errors = true;
if ( empty( lucatume\WPBrowser\Utils\Property::readPrivate( $wpdb, 'dbh' ) ) ) {
if ( WPTestCase::isStrictAboutWpdbConnectionId() && $wpdb->get_var( 'SELECT CONNECTION_ID()' ) !== WPTestCase::getWpdbConnectionId() ) {
self::fail( 'The database connection went away. A `setUpBeforeClassMethod` likely closed the connection.' );
} else {
$wpdb->db_connect();
}
ini_set( 'display_errors', 1 );

Expand Down
19 changes: 17 additions & 2 deletions src/Module/WPLoader.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
use lucatume\WPBrowser\Process\Loop;
use lucatume\WPBrowser\Process\ProcessException;
use lucatume\WPBrowser\Process\WorkerException;
use lucatume\WPBrowser\TestCase\WPTestCase;
use lucatume\WPBrowser\Utils\Arr;
use lucatume\WPBrowser\Utils\CorePHPUnit;
use lucatume\WPBrowser\Utils\Db as DbUtils;
Expand Down Expand Up @@ -127,6 +128,7 @@ class WPLoader extends Module
* backupStaticAttributes?: bool,
* backupStaticAttributesExcludeList?: array<string,string[]>,
* skipInstall?: bool,
* beStrictAboutWpdbConnectionId?: bool
* }
*/
protected $config = [
Expand Down Expand Up @@ -164,7 +166,8 @@ class WPLoader extends Module
'backupGlobalsExcludeList' => [],
'backupStaticAttributes' => false,
'backupStaticAttributesExcludeList' => [],
'skipInstall' => false
'skipInstall' => false,
'beStrictAboutWpdbConnectionId' => true
];

/**
Expand Down Expand Up @@ -344,6 +347,14 @@ static function ($v) {
);
}

if (isset($this->config['beStrictAboutWpdbConnectionId'])
&& !is_bool($this->config['beStrictAboutWpdbConnectionId'])) {
throw new ModuleConfigException(
__CLASS__,
'The `beStrictAboutWpdbConnectionId` configuration parameter must be a boolean.'
);
}

parent::validateConfig();
}

Expand Down Expand Up @@ -404,7 +415,8 @@ public function _initialize(): void
* backupGlobalsExcludeList: string[],
* backupStaticAttributes: bool,
* backupStaticAttributesExcludeList: array<string,string[]>,
* skipInstall: bool
* skipInstall: bool,
* beStrictAboutWpdbConnectionId: bool
* } $config
*/
$config = $this->config;
Expand Down Expand Up @@ -515,6 +527,8 @@ public function _initialize(): void
// If the database does not already exist, then create it now.
$db->create();

WPTestCase::beStrictAboutWpdbConnectionId($config['beStrictAboutWpdbConnectionId']);

$this->loadWordPress();
}

Expand Down Expand Up @@ -1030,6 +1044,7 @@ private function includeCorePHPUniteSuiteBootstrapFile(): void

try {
require $this->wpBootstrapFile;
WPTestCase::setWpdbConnectionId((string)$GLOBALS['wpdb']->get_var('SELECT CONNECTION_ID()'));
} catch (Throwable $t) {
// Not an early exit: Codeception will handle the Exception and print it.
$this->earlyExit = false;
Expand Down
24 changes: 24 additions & 0 deletions src/TestCase/WPTestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,14 @@
class WPTestCase extends Unit
{
use WPTestCasePHPUnitMethodsTrait;
/**
* @var bool
*/
public static $beStrictAboutWpdbConnectionId = true;
/**
* @var string|null
*/
private static $wpdbConnectionId;
/**
* @var string[]|null
*/
Expand Down Expand Up @@ -244,6 +252,22 @@ private static function getCoreTestCase(): WP_UnitTestCase

return $coreTestCase;
}
public static function isStrictAboutWpdbConnectionId(): bool
{
return self::$beStrictAboutWpdbConnectionId;
}
public static function beStrictAboutWpdbConnectionId(bool $beStrictAboutWpdbConnectionId): void
{
self::$beStrictAboutWpdbConnectionId = $beStrictAboutWpdbConnectionId;
}
public static function getWpdbConnectionId(): ?string
{
return self::$wpdbConnectionId;
}
public static function setWpdbConnectionId(string $wpdbConnectionId): void
{
self::$wpdbConnectionId = $wpdbConnectionId;
}
protected function backupAdditionalGlobals(): void
{
if (isset($GLOBALS['_wp_registered_theme_features'])) {
Expand Down
171 changes: 171 additions & 0 deletions tests/unit/lucatume/WPBrowser/Module/WPTestCaseStrictTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
<?php

namespace lucatume\WPBrowser\Module;

use Codeception\Exception\ModuleConfigException;
use Codeception\Lib\Di;
use Codeception\Lib\ModuleContainer;
use Codeception\Test\Unit;
use lucatume\WPBrowser\TestCase\WPTestCase;
use lucatume\WPBrowser\Tests\Traits\LoopIsolation;
use lucatume\WPBrowser\Tests\Traits\TmpFilesCleanup;
use lucatume\WPBrowser\Utils\Env;
use lucatume\WPBrowser\Utils\Filesystem as FS;
use lucatume\WPBrowser\Utils\Random;
use lucatume\WPBrowser\WordPress\Database\MysqlDatabase;
use lucatume\WPBrowser\WordPress\Installation;
use PHPUnit\Framework\Assert;

class WPTestCaseStrictTest extends Unit
{
use LoopIsolation;
use TmpFilesCleanup;

private function module(array $moduleContainerConfig = [], ?array $moduleConfig = null): WPLoader
{
$this->mockModuleContainer = new ModuleContainer(new Di(), $moduleContainerConfig);
return new WPLoader($this->mockModuleContainer, ($moduleConfig ?? $this->config));
}

public function nonBooleanVAluesProvider(): array
{
return [
'int' => [1],
'float' => [1.1],
'array' => [[]],
'object' => [new \stdClass()],
'true string' => ['true'],
'false string' => ['false'],
];
}

/**
* @dataProvider nonBooleanVAluesProvider
*/
public function test_will_throw_if_beStrictAboutWpdbConnectionId_is_not_boolean($value): void
{
$this->expectException(ModuleConfigException::class);
$this->expectExceptionMessage('The `beStrictAboutWpdbConnectionId` configuration parameter must be a boolean.');

$this->config = [
'wpRootFolder' => __DIR__,
'dbUrl' => 'mysql://root:root@mysql:3306/wordpress',
'beStrictAboutWpdbConnectionId' => $value
];
$this->module();
}

public function test_will_fail_if_db_connection_closed_during_setup_before_class(): void
{
$wpRootDir = FS::tmpDir('wploader_');
$dbName = Random::dbName();
$dbHost = Env::get('WORDPRESS_DB_HOST');
$dbUser = Env::get('WORDPRESS_DB_USER');
$dbPassword = Env::get('WORDPRESS_DB_PASSWORD');
$db = new MysqlDatabase($dbName, $dbUser, $dbPassword, $dbHost);
Installation::scaffold($wpRootDir);
$db->create();
$testcaseFile = $wpRootDir . '/BreakingTest.php';
$testCaseFileContents = <<<PHP
<?php
use lucatume\\WPBrowser\\TestCase\\WPTestCase;
class BreakingTest extends WPTestCase
{
public static function setUpBeforeClass():void
{
global \$wpdb;
\$wpdb->close();
parent::set_up_before_class();
}
public function test_something():void{
\$this->assertTrue(true);
}
}
PHP;
if(!file_put_contents($testcaseFile, $testCaseFileContents, LOCK_EX)) {
throw new \RuntimeException('Could not write BreakingTest.php.');
}

// Run a test using the default value, strict.
$this->config = [
'wpRootFolder' => $wpRootDir,
'dbUrl' => $db->getDbUrl()
];
$wpLoader = $this->module();

$this->assertInIsolation(static function () use ($wpLoader, $testcaseFile) {
$wpLoader->_initialize();
$connectionId = WPTestCase::getWpdbConnectionId();
Assert::assertnotEmpty($connectionId);
Assert::assertTrue(WPTestCase::isStrictAboutWpdbConnectionId());

require_once $testcaseFile;

try {
\BreakingTest::setUpBeforeClass();
} catch (\Throwable $e) {
Assert::assertNotSame($connectionId, $GLOBALS['wpdb']->get_var('SELECT CONNECTION_ID()'));
Assert::assertStringContainsString(
'The database connection went away. A `setUpBeforeClassMethod` likely closed the connection',
$e->getMessage()
);
return;
}

Assert::fail('The test should have failed.');
});

// Run a test in strict mode.
$this->config = [
'wpRootFolder' => $wpRootDir,
'dbUrl' => $db->getDbUrl(),
'beStrictAboutWpdbConnectionId' => true
];
$wpLoader = $this->module();

$this->assertInIsolation(static function () use ($wpLoader, $testcaseFile) {
$wpLoader->_initialize();
$connectionId = WPTestCase::getWpdbConnectionId();
Assert::assertnotEmpty($connectionId);
Assert::assertTrue(WPTestCase::isStrictAboutWpdbConnectionId());

require_once $testcaseFile;

try {
\BreakingTest::setUpBeforeClass();
} catch (\Throwable $e) {
Assert::assertNotSame($connectionId, $GLOBALS['wpdb']->get_var('SELECT CONNECTION_ID()'));
Assert::assertStringContainsString(
'The database connection went away. A `setUpBeforeClassMethod` likely closed the connection',
$e->getMessage()
);
return;
}

Assert::fail('The test should have failed.');
});

// Run a test in non-strict mode.
$this->config = [
'wpRootFolder' => $wpRootDir,
'dbUrl' => $db->getDbUrl(),
'beStrictAboutWpdbConnectionId' => false
];
$wpLoader = $this->module();

$this->assertInIsolation(static function () use ($wpLoader, $testcaseFile) {
$wpLoader->_initialize();
$connectionId = WPTestCase::getWpdbConnectionId();
Assert::assertFalse(WPTestCase::isStrictAboutWpdbConnectionId());

require_once $testcaseFile;

\BreakingTest::setUpBeforeClass();
Assert::assertNotSame($connectionId, $GLOBALS['wpdb']->get_var('SELECT CONNECTION_ID()'));
});
}
}

0 comments on commit fc65ae6

Please sign in to comment.