Skip to content

Commit

Permalink
Test with MySQL Server 5.6 and fix unordered export/2D assertions (#956)
Browse files Browse the repository at this point in the history
  • Loading branch information
mvorisek authored Jan 9, 2022
1 parent 6ac5c4a commit 275b9f4
Show file tree
Hide file tree
Showing 27 changed files with 271 additions and 153 deletions.
13 changes: 13 additions & 0 deletions .github/workflows/test-unit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,9 @@ jobs:
mysql:
image: mysql:8
options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=5 -e MYSQL_ROOT_PASSWORD=atk4_pass_root -e MYSQL_USER=atk4_test_user -e MYSQL_PASSWORD=atk4_pass -e MYSQL_DATABASE=atk4_test --entrypoint sh mysql:8 -c "exec docker-entrypoint.sh mysqld --default-authentication-plugin=mysql_native_password"
mysql56:
image: mysql:5.6
options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=5 -e MYSQL_ROOT_PASSWORD=atk4_pass_root -e MYSQL_USER=atk4_test_user -e MYSQL_PASSWORD=atk4_pass -e MYSQL_DATABASE=atk4_test
mariadb:
image: mariadb
options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=5 -e MYSQL_ROOT_PASSWORD=atk4_pass_root -e MYSQL_USER=atk4_test_user -e MYSQL_PASSWORD=atk4_pass -e MYSQL_DATABASE=atk4_test
Expand Down Expand Up @@ -154,6 +157,7 @@ jobs:
- name: Init
run: |
php -r '(new PDO("mysql:host=mysql", "root", "atk4_pass_root"))->exec("ALTER USER '"'"'atk4_test_user'"'"'@'"'"'%'"'"' WITH MAX_USER_CONNECTIONS 5");'
php -r '(new PDO("mysql:host=mysql56", "root", "atk4_pass_root"))->exec("GRANT USAGE ON *.* TO '"'"'atk4_test_user'"'"'@'"'"'%'"'"' WITH MAX_USER_CONNECTIONS 5");'
php -r '(new PDO("mysql:host=mariadb", "root", "atk4_pass_root"))->exec("ALTER USER '"'"'atk4_test_user'"'"'@'"'"'%'"'"' WITH MAX_USER_CONNECTIONS 5");'
php -r '(new PDO("pgsql:host=postgres;dbname=atk4_test", "atk4_test_user", "atk4_pass"))->exec("ALTER ROLE atk4_test_user CONNECTION LIMIT 1");'
if [ -n "$LOG_COVERAGE" ]; then mkdir coverage; fi
Expand Down Expand Up @@ -181,6 +185,15 @@ jobs:
php -d opcache.enable_cli=1 vendor/bin/phpunit --exclude-group none $(if [ -n "$LOG_COVERAGE" ]; then echo --coverage-text; else echo --no-coverage; fi) -v
if [ -n "$LOG_COVERAGE" ]; then mv coverage/phpunit.cov coverage/phpunit-mysql-mysqli.cov; fi
- name: "Run tests: MySQL 5.6"
env:
DB_DSN: "mysql:host=mysql56;dbname=atk4_test"
DB_USER: atk4_test_user
DB_PASSWORD: atk4_pass
run: |
php -d opcache.enable_cli=1 vendor/bin/phpunit --exclude-group none $(if [ -n "$LOG_COVERAGE" ]; then echo --coverage-text; else echo --no-coverage; fi) -v
if [ -n "$LOG_COVERAGE" ]; then mv coverage/phpunit.cov coverage/phpunit-mysql56.cov; fi
- name: "Run tests: MariaDB"
env:
DB_DSN: "mysql:host=mariadb;dbname=atk4_test"
Expand Down
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
"ext-intl": "*",
"ext-pdo": "*",
"atk4/core": "~3.2.0",
"doctrine/dbal": "^2.13.3 || ^3.2",
"doctrine/dbal": "^2.13.5 || ^3.2",
"mvorisek/atk4-hintable": "~1.7.1"
},
"require-dev": {
Expand Down
1 change: 1 addition & 0 deletions src/Field.php
Original file line number Diff line number Diff line change
Expand Up @@ -493,6 +493,7 @@ public function getDsqlExpression(Expression $expression): Expression
public function __debugInfo(): array
{
$arr = [
'owner_class' => get_class($this->getOwner()),
'short_name' => $this->short_name,
'type' => $this->type,
];
Expand Down
2 changes: 1 addition & 1 deletion src/Model.php
Original file line number Diff line number Diff line change
Expand Up @@ -1551,7 +1551,7 @@ public function save(array $data = [])
}

// No save needed, nothing was changed
if (!$data && !$dirty_join) {
if (count($data) === 0 && !$dirty_join) {
return $this;
}

Expand Down
4 changes: 2 additions & 2 deletions src/Model/ArrayAccessTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@

/**
* Trait to add array like support to Model, example usage:
* class CustomModel extends \Atk4\Data\Model implements \ArrayAccess
* class CustomModel extends Model implements \ArrayAccess
* {
* use \Atk4\Data\Model\ArrayAccessTrait;
* use Model\ArrayAccessTrait;
* }.
*/
trait ArrayAccessTrait
Expand Down
100 changes: 98 additions & 2 deletions src/Schema/TestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Platforms\MySQLPlatform;
use Doctrine\DBAL\Platforms\OraclePlatform;
use Doctrine\DBAL\Platforms\SqlitePlatform;
use Doctrine\DBAL\Platforms\SQLServerPlatform;
use Doctrine\DBAL\Schema\AbstractSchemaManager;

abstract class TestCase extends BaseTestCase
Expand Down Expand Up @@ -105,7 +107,7 @@ private function convertSqlFromSqlite(string $sql): string
{
$platform = $this->getDatabasePlatform();

return preg_replace_callback(
$convertedSql = preg_replace_callback(
'~\'(?:[^\'\\\\]+|\\\\.)*\'|"(?:[^"\\\\]+|\\\\.)*"|:(\w+)~s',
function ($matches) use ($platform) {
if (isset($matches[1])) {
Expand All @@ -117,17 +119,111 @@ function ($matches) use ($platform) {
return $platform->quoteSingleIdentifier($str);
}

return $platform->quoteStringLiteral($str);
return ($platform instanceof SQLServerPlatform ? 'N' : '') . $platform->quoteStringLiteral($str);
},
$sql
);

if ($platform instanceof SqlitePlatform && $convertedSql !== $sql) {
$this->assertSame($sql, $convertedSql);
}

return $convertedSql;
}

protected function assertSameSql(string $expectedSqliteSql, string $actualSql, string $message = ''): void
{
$this->assertSame($this->convertSqlFromSqlite($expectedSqliteSql), $actualSql, $message);
}

/**
* @param mixed $a
* @param mixed $b
*/
private function compareExportUnorderedValue($a, $b): int
{
if ($a === $b) {
return 0;
}

$cmp = gettype($a) <=> gettype($b);
if ($cmp !== 0) {
return $cmp;
}

if (is_object($a)) {
$cmp = gettype($a) <=> gettype($b);
if ($cmp !== 0) {
return $cmp;
}

if ($a instanceof \DateTimeInterface) {
$format = 'Y-m-d H:i:s.u e I Z';

return $a->format($format) <=> $b->format($format);
}
}

if (is_array($a) && count($a) === count($b)) {
$is2d = true;
foreach ($a as $v) {
if (!is_array($v)) {
$is2d = false;

break;
}
}
if ($is2d) {
foreach ($b as $v) {
if (!is_array($v)) {
$is2d = false;

break;
}
}
}

if ($is2d) {
if (array_is_list($a) && array_is_list($b)) {
usort($a, fn ($a, $b) => $this->compareExportUnorderedValue($a, $b));
usort($b, fn ($a, $b) => $this->compareExportUnorderedValue($a, $b));
} else {
uasort($a, fn ($a, $b) => $this->compareExportUnorderedValue($a, $b));
uasort($b, fn ($a, $b) => $this->compareExportUnorderedValue($a, $b));
}
}

if (array_keys($a) === array_keys($b)) {
foreach ($a as $k => $v) {
$cmp = $this->compareExportUnorderedValue($v, $b[$k]);
if ($cmp !== 0) {
return $cmp;
}
}

return 0;
}
}

return $a <=> $b;
}

/**
* Same as self::assertSame() except:
* - 2D arrays (rows) are recursively compared without any order
* - objects implementing DateTimeInterface are compared by formatted output.
*/
protected function assertSameExportUnordered(array $expected, array $actual, string $message = ''): void
{
if ($this->compareExportUnorderedValue($expected, $actual) === 0) {
$this->assertTrue(true);

return;
}

$this->assertSame($expected, $actual, $message);
}

public function createMigrator(Model $model = null): Migrator
{
$migrator = new Migrator($model ?: $this->db);
Expand Down
4 changes: 2 additions & 2 deletions tests/ConditionSqlTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -383,13 +383,13 @@ public function testOrConditions(): void
['name', 'Peter'],
));

$this->assertEquals(2, $u->action('count')->getOne());
$this->assertSame('2', $u->action('count')->getOne());

$u->addCondition(Model\Scope::createOr(
['name', 'Peter'],
['name', 'Joe'],
));
$this->assertEquals(1, $u->action('count')->getOne());
$this->assertSame('1', $u->action('count')->getOne());
}

/**
Expand Down
10 changes: 5 additions & 5 deletions tests/ContainsManyTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -117,9 +117,9 @@ public function testContainsMany(): void
}

// reload invoice just in case
$this->assertEquals($rows, $i->lines->export());
$this->assertSameExportUnordered($rows, $i->lines->export());
$i->reload();
$this->assertEquals($rows, $i->lines->export());
$this->assertSameExportUnordered($rows, $i->lines->export());

// now let's delete line with id=2 and add one more line
$i->lines
Expand Down Expand Up @@ -157,7 +157,7 @@ public function testContainsMany(): void
$l->fieldName()->add_date => new \DateTime('2019-01-01'),
],
];
$this->assertEquals($rows, $i->lines->export());
$this->assertSameExportUnordered($rows, $i->lines->export());

// try hasOne reference
$v = $i->lines->load(4)->vat_rate_id;
Expand All @@ -169,7 +169,7 @@ public function testContainsMany(): void

// and what about calculated field?
$i->reload(); // we need to reload invoice for changes in lines to be recalculated
$this->assertSame(10 * 2 * (1 + 21 / 100) + 40 * 1 * (1 + 21 / 100) + 50 * 3 * (1 + 15 / 100), $i->total_gross); // =245.10
$this->assertSame(10 * 2 * (1 + 21 / 100) + 40 * 1 * (1 + 21 / 100) + 50 * 3 * (1 + 15 / 100), $i->total_gross); // = 245.1
}

/**
Expand Down Expand Up @@ -236,7 +236,7 @@ public function testNestedContainsMany(): void
$i->reload();

// ok, so now let's test
$this->assertEquals([
$this->assertSameExportUnordered([
1 => [
$l->discounts->fieldName()->id => 1,
$l->discounts->fieldName()->percent => 5,
Expand Down
41 changes: 15 additions & 26 deletions tests/ExpressionSqlTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

use Atk4\Data\Model;
use Atk4\Data\Schema\TestCase;
use Doctrine\DBAL\Platforms\MySQLPlatform;
use Doctrine\DBAL\Platforms\OraclePlatform;
use Doctrine\DBAL\Platforms\SqlitePlatform;

Expand Down Expand Up @@ -135,31 +134,21 @@ public function testExpressions(): void
$m->addFields(['name', 'surname', 'cached_name']);

if ($this->getDatabasePlatform() instanceof SqlitePlatform) {
$m->addExpression('full_name', '[name] || " " || [surname]');
$concatExpr = '[name] || " " || [surname]';
} elseif ($this->getDatabasePlatform() instanceof OraclePlatform) {
$m->addExpression('full_name', '[name] || \' \' || [surname]');
$concatExpr = '[name] || \' \' || [surname]';
} else {
$m->addExpression('full_name', 'CONCAT([name], \' \', [surname])');
$concatExpr = 'CONCAT([name], \' \', [surname])';
}
$m->addExpression('full_name', $concatExpr);

$m->addCondition($m->expr('[full_name] != [cached_name]'));

if ($this->getDatabasePlatform() instanceof SqlitePlatform) {
$this->assertSame(
'select "id", "name", "surname", "cached_name", ("name" || " " || "surname") "full_name" from "user" where (("name" || " " || "surname") != "cached_name")',
$m->action('select')->render()[0]
);
} elseif ($this->getDatabasePlatform() instanceof OraclePlatform) {
$this->assertSame(
'select "id", "name", "surname", "cached_name", ("name" || \' \' || "surname") "full_name" from "user" where (("name" || \' \' || "surname") != "cached_name")',
$m->action('select')->render()[0]
);
} elseif ($this->getDatabasePlatform() instanceof MySQLPlatform) {
$this->assertSame(
'select `id`, `name`, `surname`, `cached_name`, (CONCAT(`name`, \' \', `surname`)) `full_name` from `user` where ((CONCAT(`name`, \' \', `surname`)) != `cached_name`)',
$m->action('select')->render()[0]
);
}
$concatSql = preg_replace('~\[(\w+)\]~', '"$1"', $concatExpr);
$this->assertSameSql(
'select "id", "name", "surname", "cached_name", (' . $concatSql . ') "full_name" from "user" where ((' . $concatSql . ') != "cached_name")',
$m->action('select')->render()[0]
);

$mm = $m->tryLoad(1);
$this->assertNull($mm->get('name'));
Expand Down Expand Up @@ -210,24 +199,24 @@ public function testExpressionActionAlias(): void

// use alias as array key if it is set
$q = $m->action('field', ['x', 'alias' => 'foo']);
$this->assertEquals([0 => ['foo' => 5]], $q->getRows());
$this->assertEquals([['foo' => 5]], $q->getRows());

// if alias is not set, then use field name as key
$q = $m->action('field', ['x']);
$this->assertEquals([0 => ['x' => 5]], $q->getRows());
$this->assertEquals([['x' => 5]], $q->getRows());

// FX actions
$q = $m->action('fx', ['sum', 'x', 'alias' => 'foo']);
$this->assertEquals([0 => ['foo' => 5]], $q->getRows());
$this->assertEquals([['foo' => 5]], $q->getRows());

$q = $m->action('fx', ['sum', 'x']);
$this->assertEquals([0 => ['sum_x' => 5]], $q->getRows());
$this->assertEquals([['sum_x' => 5]], $q->getRows());

$q = $m->action('fx0', ['sum', 'x', 'alias' => 'foo']);
$this->assertEquals([0 => ['foo' => 5]], $q->getRows());
$this->assertEquals([['foo' => 5]], $q->getRows());

$q = $m->action('fx0', ['sum', 'x']);
$this->assertEquals([0 => ['sum_x' => 5]], $q->getRows());
$this->assertEquals([['sum_x' => 5]], $q->getRows());
}

public function testNeverSaveNeverPersist(): void
Expand Down
6 changes: 4 additions & 2 deletions tests/FieldTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -419,8 +419,10 @@ public function testActual(): void
$mm = $m->loadBy('first_name', 'John');
$this->assertSame('John', $mm->get('first_name'));

$d = $m->export();
$this->assertSame('John', $d[0]['first_name']);
$this->assertSameExportUnordered([
['id' => 1, 'first_name' => 'John', 'surname' => 'Smith'],
['id' => 2, 'first_name' => 'Peter', 'surname' => 'qq'],
], $m->export());

$this->assertEquals([
'user' => [
Expand Down
12 changes: 6 additions & 6 deletions tests/JoinSqlTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -541,9 +541,9 @@ public function testJoinHasOneHasMany(): void
$ref_many = $j->hasMany('Token', ['model' => $m_t]); // hasMany on JOIN (use default our_field, their_field)

$m_u2 = $m_u->load(1);
$this->assertEquals([
['id' => 30, 'user_id' => 1, 'token' => 'ABC'],
['id' => 31, 'user_id' => 1, 'token' => 'DEF'],
$this->assertSameExportUnordered([
['id' => 30, 'user_id' => '1', 'token' => 'ABC'],
['id' => 31, 'user_id' => '1', 'token' => 'DEF'],
], $m_u2->ref('Token')->export());

// hasMany email model (uses custom our_field, their_field)
Expand All @@ -553,9 +553,9 @@ public function testJoinHasOneHasMany(): void
$ref_many = $j->hasMany('Email', ['model' => $m_e, 'our_field' => 'contact_id', 'their_field' => 'contact_id']); // hasMany on JOIN (use custom our_field, their_field)

$m_u2 = $m_u->load(1);
$this->assertEquals([
['id' => 40, 'contact_id' => 10, 'address' => '[email protected]'],
['id' => 41, 'contact_id' => 10, 'address' => '[email protected]'],
$this->assertSameExportUnordered([
['id' => 40, 'contact_id' => '10', 'address' => '[email protected]'],
['id' => 41, 'contact_id' => '10', 'address' => '[email protected]'],
], $m_u2->ref('Email')->export());
}

Expand Down
Loading

0 comments on commit 275b9f4

Please sign in to comment.