Skip to content

Commit e37c93f

Browse files
feat: add MySQL, PostgreSQL, and SQLServer CI workflow and corresponding test classes for improoved database testing. (#61)
1 parent ae1c5bc commit e37c93f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+774
-22
lines changed

.github/workflows/build-mssql.yml

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
on:
2+
pull_request:
3+
paths-ignore:
4+
- 'docs/**'
5+
- 'README.md'
6+
- 'CHANGELOG.md'
7+
- '.gitignore'
8+
- '.gitattributes'
9+
10+
push:
11+
paths-ignore:
12+
- 'docs/**'
13+
- 'README.md'
14+
- 'CHANGELOG.md'
15+
- '.gitignore'
16+
- '.gitattributes'
17+
18+
name: build-mssql
19+
20+
jobs:
21+
mssql:
22+
name: SQL Server tests.
23+
uses: php-forge/actions/.github/workflows/phpunit-database.yml@main
24+
secrets:
25+
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
26+
with:
27+
concurrency-group: mssql-${{ github.ref }}
28+
database-env: |
29+
{
30+
"ACCEPT_EULA": "Y",
31+
"SA_PASSWORD": "YourStrong!Passw0rd",
32+
"MSSQL_PID": "Developer"
33+
}
34+
database-health-cmd: "/opt/mssql-tools18/bin/sqlcmd -C -S localhost -U SA -P 'YourStrong!Passw0rd' -Q 'SELECT 1'"
35+
database-health-retries: 5
36+
database-image: mcr.microsoft.com/mssql/server
37+
database-port: 1433
38+
database-type: mssql
39+
database-versions: '["2022-latest"]'
40+
enable-concurrency: true
41+
extensions: pdo, pdo_sqlsrv, sqlsrv
42+
os: '["ubuntu-latest"]'
43+
php-version: '["8.4"]'
44+
phpunit-group: mssql
45+
setup-commands: |
46+
# Install Microsoft ODBC Driver for SQL Server
47+
sudo ACCEPT_EULA=Y apt-get install -y msodbcsql18
48+
49+
# Wait for SQL Server to be fully ready
50+
sleep 15
51+
52+
# Create test database
53+
docker exec -i database /opt/mssql-tools18/bin/sqlcmd -C -S localhost -U SA -P 'YourStrong!Passw0rd' -Q "
54+
IF NOT EXISTS (SELECT name FROM sys.databases WHERE name = 'yiitest')
55+
BEGIN
56+
CREATE DATABASE yiitest;
57+
END
58+
"

.github/workflows/build-mysql.yml

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
on:
2+
pull_request:
3+
paths-ignore:
4+
- 'docs/**'
5+
- 'README.md'
6+
- 'CHANGELOG.md'
7+
- '.gitignore'
8+
- '.gitattributes'
9+
10+
push:
11+
paths-ignore:
12+
- 'docs/**'
13+
- 'README.md'
14+
- 'CHANGELOG.md'
15+
- '.gitignore'
16+
- '.gitattributes'
17+
18+
name: build-mysql
19+
20+
jobs:
21+
mysql:
22+
name: MySQL tests.
23+
uses: php-forge/actions/.github/workflows/phpunit-database.yml@main
24+
secrets:
25+
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
26+
with:
27+
concurrency-group: mysql-${{ github.ref }}
28+
database-env: |
29+
{
30+
"MYSQL_DATABASE": "yiitest",
31+
"MYSQL_ROOT_PASSWORD": "root",
32+
}
33+
database-health-cmd: "mysqladmin ping"
34+
database-health-retries: 3
35+
database-image: mysql
36+
database-port: 3306
37+
database-type: mysql
38+
database-versions: '["8.0", "8.4", "latest"]'
39+
enable-concurrency: true
40+
extensions: pdo, pdo_mysql
41+
os: '["ubuntu-latest"]'
42+
php-version: '["8.4"]'
43+
phpunit-group: mysql

.github/workflows/build-pgsql.yml

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
on:
2+
pull_request:
3+
paths-ignore:
4+
- 'docs/**'
5+
- 'README.md'
6+
- 'CHANGELOG.md'
7+
- '.gitignore'
8+
- '.gitattributes'
9+
10+
push:
11+
paths-ignore:
12+
- 'docs/**'
13+
- 'README.md'
14+
- 'CHANGELOG.md'
15+
- '.gitignore'
16+
- '.gitattributes'
17+
18+
name: build-pgsql
19+
20+
jobs:
21+
pgsql:
22+
name: PostgreSQL tests.
23+
uses: php-forge/actions/.github/workflows/phpunit-database.yml@main
24+
secrets:
25+
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
26+
with:
27+
concurrency-group: pgsql-${{ github.ref }}
28+
database-env: |
29+
{
30+
"POSTGRES_DB": "yiitest",
31+
"POSTGRES_USER": "root",
32+
"POSTGRES_PASSWORD": "root"
33+
}
34+
database-health-cmd: "pg_isready -U postgres"
35+
database-health-retries: 3
36+
database-image: postgres
37+
database-port: 5432
38+
database-type: pgsql
39+
database-versions: '["15", "16", "17"]'
40+
enable-concurrency: true
41+
extensions: pdo, pdo_pgsql
42+
os: '["ubuntu-latest"]'
43+
php-version: '["8.4"]'
44+
phpunit-group: pgsql

.github/workflows/build.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,12 @@ jobs:
2727
composer require yiisoft/yii2:22.0.x-dev --prefer-dist --no-progress --no-interaction --no-scripts --ansi
2828
concurrency-group: phpunit-${{ github.workflow }}-${{ github.ref }}
2929
extensions: pdo, pdo_sqlite
30+
phpunit-group: sqlite
3031
phpunit-compatibility:
3132
uses: php-forge/actions/.github/workflows/phpunit.yml@main
3233
secrets:
3334
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
3435
with:
3536
concurrency-group: compatibility-${{ github.workflow }}-${{ github.ref }}
3637
extensions: pdo, pdo_sqlite
38+
phpunit-group: sqlite

.github/workflows/mutation.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,6 @@ jobs:
2222
uses: php-forge/actions/.github/workflows/infection.yml@main
2323
with:
2424
phpstan: true
25+
framework-options: --test-framework-options="--group=sqlite"
2526
secrets:
2627
STRYKER_DASHBOARD_API_KEY: ${{ secrets.STRYKER_DASHBOARD_API_KEY }}

composer.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,8 @@
5151
"scripts": {
5252
"check-dependencies": "./vendor/bin/composer-require-checker check",
5353
"ecs": "./vendor/bin/ecs --fix",
54-
"mutation": "./vendor/bin/infection --threads=4 --ignore-msi-with-no-mutations --only-covered --min-msi=100 --min-covered-msi=100",
55-
"mutation-static": "./vendor/bin/infection --threads=4 --ignore-msi-with-no-mutations --only-covered --min-msi=100 --min-covered-msi=100 --static-analysis-tool=phpstan",
54+
"mutation": "./vendor/bin/infection --threads=4 --ignore-msi-with-no-mutations --only-covered --min-msi=100 --min-covered-msi=100 --test-framework-options=--group=sqlite",
55+
"mutation-static": "./vendor/bin/infection --threads=4 --ignore-msi-with-no-mutations --only-covered --min-msi=100 --min-covered-msi=100 --static-analysis-tool=phpstan --test-framework-options=--group=sqlite",
5656
"rector": "./vendor/bin/rector process src",
5757
"static": "./vendor/bin/phpstan --memory-limit=512M",
5858
"tests": "./vendor/bin/phpunit"

src/NestedSetsBehavior.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1070,7 +1070,10 @@ protected function beforeInsertNode(int $value, int $depth): void
10701070
*/
10711071
protected function beforeInsertRootNode(): void
10721072
{
1073-
if ($this->treeAttribute === false && $this->getOwner()::find()->roots()->exists()) {
1073+
if (
1074+
$this->treeAttribute === false &&
1075+
$this->getOwner()::find()->andWhere([$this->leftAttribute => 1])->exists()
1076+
) {
10741077
throw new Exception('Can not create more than one root when "treeAttribute" is false.');
10751078
}
10761079

tests/TestCase.php

Lines changed: 47 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use function array_values;
1717
use function dom_import_simplexml;
1818
use function file_get_contents;
19+
use function preg_replace;
1920
use function simplexml_load_string;
2021
use function str_replace;
2122

@@ -38,9 +39,12 @@
3839
class TestCase extends \PHPUnit\Framework\TestCase
3940
{
4041
use SchemaBuilderTrait;
42+
protected string $driverName = 'sqlite';
4143

4244
protected string|null $dsn = null;
4345
protected string $fixtureDirectory = __DIR__ . '/support/data/';
46+
protected string $password = '';
47+
protected string $username = '';
4448

4549
protected function setUp(): void
4650
{
@@ -103,12 +107,12 @@ protected function assertQueryHasOrderBy(ActiveQuery $query, string $methodName)
103107

104108
self::assertStringContainsString(
105109
'ORDER BY',
106-
$sql,
110+
$this->replaceQuotes($sql),
107111
"'{$methodName}' query should include 'ORDER BY' clause for deterministic results.",
108112
);
109113

110114
self::assertStringContainsString(
111-
'`lft`',
115+
$this->replaceQuotes('[[lft]]'),
112116
$sql,
113117
"'{$methodName}' query should order by 'left' attribute for consistent ordering.",
114118
);
@@ -167,11 +171,11 @@ protected function createDatabase(): void
167171
$command = $this->getDb()->createCommand();
168172

169173
if ($this->getDb()->getTableSchema('tree', true) !== null) {
170-
$command->dropTable('tree');
174+
$command->dropTable('tree')->execute();
171175
}
172176

173177
if ($this->getDb()->getTableSchema('multiple_tree', true) !== null) {
174-
$command->dropTable('multiple_tree');
178+
$command->dropTable('multiple_tree')->execute();
175179
}
176180

177181
$command->createTable(
@@ -291,14 +295,14 @@ protected function generateFixtureTree(): void
291295
*/
292296
protected function getDataSet(): array
293297
{
294-
$dataSetTree = Tree::find()->asArray()->all();
298+
$dataSetTree = Tree::find()->orderBy(['id' => SORT_ASC])->asArray()->all();
295299

296300
foreach ($dataSetTree as $key => $value) {
297301
$dataSetTree[$key]['type'] = 'tree';
298302
$dataSetTree[$key]['tree'] = 0;
299303
}
300304

301-
$dataSetMultipleTree = MultipleTree::find()->asArray()->all();
305+
$dataSetMultipleTree = MultipleTree::find()->orderBy(['id' => SORT_ASC])->asArray()->all();
302306

303307
foreach ($dataSetMultipleTree as $key => $value) {
304308
$dataSetMultipleTree[$key]['type'] = 'multiple_tree';
@@ -314,7 +318,7 @@ protected function getDataSet(): array
314318
*/
315319
protected function getDataSetMultipleTree(): array
316320
{
317-
$dataSetMultipleTree = MultipleTree::find()->asArray()->all();
321+
$dataSetMultipleTree = MultipleTree::find()->orderBy(['id' => SORT_ASC])->asArray()->all();
318322

319323
foreach ($dataSetMultipleTree as $key => $value) {
320324
$dataSetMultipleTree[$key]['type'] = 'multiple_tree';
@@ -352,12 +356,48 @@ protected function mockConsoleApplication(): void
352356
'db' => [
353357
'class' => Connection::class,
354358
'dsn' => $this->dsn !== null ? $this->dsn : 'sqlite::memory:',
359+
'password' => $this->password,
360+
'username' => $this->username,
355361
],
356362
],
357363
],
358364
);
359365
}
360366

367+
/**
368+
* Adjust dbms specific escaping.
369+
*
370+
* @param string $sql SQL to adjust.
371+
*
372+
* @return string Adjusted SQL.
373+
*/
374+
protected function replaceQuotes(string $sql): string
375+
{
376+
return match ($this->driverName) {
377+
'mysql', 'sqlite' => str_replace(
378+
['[[', ']]'],
379+
'`',
380+
$sql,
381+
),
382+
'oci' => str_replace(
383+
['[[', ']]'],
384+
'"',
385+
$sql,
386+
),
387+
'pgsql' => str_replace(
388+
['\\[', '\\]'],
389+
['[', ']'],
390+
preg_replace('/(\[\[)|((?<!(\[))\]\])/', '"', $sql) ?? $sql,
391+
),
392+
'sqlsrv' => str_replace(
393+
['[[', ']]'],
394+
['[', ']'],
395+
$sql,
396+
),
397+
default => $sql,
398+
};
399+
}
400+
361401
/**
362402
* Applies database updates to tree nodes.
363403
*

tests/base/AbstractQueryBehavior.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ public function testRootsMethodRequiresLeftAttributeOrderingWhenTreeAttributeIsD
128128
"'roots()' query should include 'ORDER BY' clause for consistent results.",
129129
);
130130
self::assertStringContainsString(
131-
'`lft`',
131+
$this->replaceQuotes('[[lft]]'),
132132
$sql,
133133
"'roots()' query should order by 'left' attribute for deterministic ordering.",
134134
);
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace yii2\extensions\nestedsets\tests\mssql;
6+
7+
use PHPUnit\Framework\Attributes\Group;
8+
use yii2\extensions\nestedsets\tests\base\AbstractCacheManagement;
9+
10+
#[Group('mssql')]
11+
final class CacheManagementTest extends AbstractCacheManagement
12+
{
13+
protected string $driverName = 'sqlsrv';
14+
protected string|null $dsn = 'sqlsrv:Server=127.0.0.1,1433;Database=yiitest;Encrypt=no';
15+
protected string $password = 'YourStrong!Passw0rd';
16+
protected string $username = 'SA';
17+
}

0 commit comments

Comments
 (0)