From 275b9f4243136eea05625d6d3595063001ba4fe2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Vo=C5=99=C3=AD=C5=A1ek?= Date: Sun, 9 Jan 2022 16:47:20 +0100 Subject: [PATCH] Test with MySQL Server 5.6 and fix unordered export/2D assertions (#956) --- .github/workflows/test-unit.yml | 13 +++ composer.json | 2 +- src/Field.php | 1 + src/Model.php | 2 +- src/Model/ArrayAccessTrait.php | 4 +- src/Schema/TestCase.php | 100 +++++++++++++++++++- tests/ConditionSqlTest.php | 4 +- tests/ContainsManyTest.php | 10 +- tests/ExpressionSqlTest.php | 41 +++----- tests/FieldTest.php | 6 +- tests/JoinSqlTest.php | 12 +-- tests/LookupSqlTest.php | 6 +- tests/ModelWithoutIdTest.php | 14 +-- tests/Persistence/ArrayTest.php | 18 ++-- tests/Persistence/Sql/QueryTest.php | 18 ++-- tests/Persistence/Sql/WithDb/SelectTest.php | 12 +-- tests/Persistence/SqlTest.php | 10 +- tests/RandomTest.php | 52 +++++----- tests/ReadOnlyModeTest.php | 2 + tests/Schema/ModelTest.php | 13 ++- tests/Schema/TestCaseTest.php | 4 +- tests/ScopeTest.php | 10 +- tests/SmboTransferTest.php | 36 +++---- tests/SubTypesTest.php | 7 +- tests/TypecastingTest.php | 2 +- tests/Util/DeepCopyTest.php | 14 +-- tests/WithTest.php | 11 ++- 27 files changed, 271 insertions(+), 153 deletions(-) diff --git a/.github/workflows/test-unit.yml b/.github/workflows/test-unit.yml index da8e1c261..f3f2ea3e9 100644 --- a/.github/workflows/test-unit.yml +++ b/.github/workflows/test-unit.yml @@ -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 @@ -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 @@ -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" diff --git a/composer.json b/composer.json index 9a0382ec9..5d5f6c1f4 100644 --- a/composer.json +++ b/composer.json @@ -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": { diff --git a/src/Field.php b/src/Field.php index 19d17465e..0d780016b 100644 --- a/src/Field.php +++ b/src/Field.php @@ -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, ]; diff --git a/src/Model.php b/src/Model.php index 38b2b23d7..f9e748f8f 100644 --- a/src/Model.php +++ b/src/Model.php @@ -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; } diff --git a/src/Model/ArrayAccessTrait.php b/src/Model/ArrayAccessTrait.php index 5da3f6b51..0b2e76fff 100644 --- a/src/Model/ArrayAccessTrait.php +++ b/src/Model/ArrayAccessTrait.php @@ -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 diff --git a/src/Schema/TestCase.php b/src/Schema/TestCase.php index 9858ff271..f4acf933c 100644 --- a/src/Schema/TestCase.php +++ b/src/Schema/TestCase.php @@ -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 @@ -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])) { @@ -117,10 +119,16 @@ 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 @@ -128,6 +136,94 @@ protected function assertSameSql(string $expectedSqliteSql, string $actualSql, s $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); diff --git a/tests/ConditionSqlTest.php b/tests/ConditionSqlTest.php index 885db78bc..7d574252d 100644 --- a/tests/ConditionSqlTest.php +++ b/tests/ConditionSqlTest.php @@ -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()); } /** diff --git a/tests/ContainsManyTest.php b/tests/ContainsManyTest.php index c43726515..e17e1de12 100644 --- a/tests/ContainsManyTest.php +++ b/tests/ContainsManyTest.php @@ -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 @@ -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; @@ -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 } /** @@ -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, diff --git a/tests/ExpressionSqlTest.php b/tests/ExpressionSqlTest.php index e5ae61a63..2730918be 100644 --- a/tests/ExpressionSqlTest.php +++ b/tests/ExpressionSqlTest.php @@ -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; @@ -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')); @@ -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 diff --git a/tests/FieldTest.php b/tests/FieldTest.php index 83bf07659..66425fd2f 100644 --- a/tests/FieldTest.php +++ b/tests/FieldTest.php @@ -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' => [ diff --git a/tests/JoinSqlTest.php b/tests/JoinSqlTest.php index b9cf6d0aa..85be074f1 100644 --- a/tests/JoinSqlTest.php +++ b/tests/JoinSqlTest.php @@ -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) @@ -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' => 'john@foo.net'], - ['id' => 41, 'contact_id' => 10, 'address' => 'johnny@foo.net'], + $this->assertSameExportUnordered([ + ['id' => 40, 'contact_id' => '10', 'address' => 'john@foo.net'], + ['id' => 41, 'contact_id' => '10', 'address' => 'johnny@foo.net'], ], $m_u2->ref('Email')->export()); } diff --git a/tests/LookupSqlTest.php b/tests/LookupSqlTest.php index db168676e..302a30e5b 100644 --- a/tests/LookupSqlTest.php +++ b/tests/LookupSqlTest.php @@ -179,7 +179,7 @@ public function testImportCountriesBasic(): void ['name' => 'Russia', 'code' => 'RU'], ]); - $this->assertSame([ + $this->assertSameExportUnordered([ 'country' => [ 1 => [ 'id' => '1', @@ -237,7 +237,7 @@ public function testImportInternationalUsers(): void // Both lines will work quite similar $c->insert(['name' => 'Latvia', 'Users' => [['name' => 'imants'], ['name' => 'juris']]]); - $this->assertSame([ + $this->assertSameExportUnordered([ 'country' => [ 1 => [ 'id' => '1', @@ -302,7 +302,7 @@ public function testImportByLookup(): void //'name' => 'Romans', 'country_code' => 'UK'], // does not exist ]); - $this->assertSame([ + $this->assertSameExportUnordered([ 'country' => [ 1 => [ 'id' => '1', diff --git a/tests/ModelWithoutIdTest.php b/tests/ModelWithoutIdTest.php index 21b043aff..d904bbcb3 100644 --- a/tests/ModelWithoutIdTest.php +++ b/tests/ModelWithoutIdTest.php @@ -38,18 +38,20 @@ protected function setUp(): void */ public function testBasic(): void { + $this->m->setOrder('name', 'asc'); $m = $this->m->tryLoadAny(); $this->assertSame('John', $m->get('name')); + $this->m->order = []; $this->m->setOrder('name', 'desc'); $m = $this->m->tryLoadAny(); $this->assertSame('Sue', $m->get('name')); - $n = []; + $names = []; foreach ($this->m as $row) { - $n[] = $row->get('name'); + $names[] = $row->get('name'); } - $this->assertSame(['Sue', 'John'], $n); + $this->assertSame(['Sue', 'John'], $names); } public function testGetIdException(): void @@ -84,7 +86,7 @@ public function testInsert(): void } $this->m->insert(['name' => 'Joe']); - $this->assertEquals(3, $this->m->action('count')->getOne()); + $this->assertSame('3', $this->m->action('count')->getOne()); } /** @@ -99,7 +101,7 @@ public function testSave1(): void $m = $this->m->tryLoadAny(); $m->saveAndUnload(); - $this->assertEquals(3, $this->m->action('count')->getOne()); + $this->assertSame('3', $this->m->action('count')->getOne()); } /** @@ -114,7 +116,7 @@ public function testSave2(): void $m = $this->m->tryLoadAny(); $m->save(); - $this->assertEquals(3, $this->m->action('count')->getOne()); + $this->assertSame('3', $this->m->action('count')->getOne()); } /** diff --git a/tests/Persistence/ArrayTest.php b/tests/Persistence/ArrayTest.php index 3080fa344..334506f5e 100644 --- a/tests/Persistence/ArrayTest.php +++ b/tests/Persistence/ArrayTest.php @@ -123,7 +123,7 @@ public function testUpdateArray(): void $m->setMulti(['name' => 'Foo', 'surname' => 'Bar']); $m->save(); - $this->assertEquals([ + $this->assertSame([ 'user' => [ 1 => ['name' => 'Peter', 'surname' => 'Smith'], 2 => ['name' => 'Sarah', 'surname' => 'QQ'], @@ -146,7 +146,7 @@ public function testInsert(): void $m->insert(['name' => 'Foo', 'surname' => 'Bar']); - $this->assertEquals([ + $this->assertSame([ 'user' => [ 1 => ['name' => 'John', 'surname' => 'Smith'], 2 => ['name' => 'Sarah', 'surname' => 'Jones'], @@ -610,13 +610,13 @@ public function testOrder(): void $m->setOrder('f1', 'desc'); $m->setOrder('f2', 'desc'); $d = $this->_getRows($m, ['f1', 'f2', 'id']); - $this->assertEquals([ - ['f1' => 'E', 'f2' => 'A', 'id' => 5], - ['f1' => 'D', 'f2' => 'C', 'id' => 3], - ['f1' => 'D', 'f2' => 'A', 'id' => 2], - ['f1' => 'C', 'f2' => 'A', 'id' => 6], - ['f1' => 'A', 'f2' => 'C', 'id' => 4], - ['f1' => 'A', 'f2' => 'B', 'id' => 1], + $this->assertSame([ + ['id' => 5, 'f1' => 'E', 'f2' => 'A'], + ['id' => 3, 'f1' => 'D', 'f2' => 'C'], + ['id' => 2, 'f1' => 'D', 'f2' => 'A'], + ['id' => 6, 'f1' => 'C', 'f2' => 'A'], + ['id' => 4, 'f1' => 'A', 'f2' => 'C'], + ['id' => 1, 'f1' => 'A', 'f2' => 'B'], ], $d); $this->assertSame($d, array_values($m->export(['f1', 'f2', 'id']))); // array_values to get rid of keys } diff --git a/tests/Persistence/Sql/QueryTest.php b/tests/Persistence/Sql/QueryTest.php index df86cadc5..03b8fb8ad 100644 --- a/tests/Persistence/Sql/QueryTest.php +++ b/tests/Persistence/Sql/QueryTest.php @@ -496,7 +496,7 @@ public function testBasicRenderSubquery(): void /** * @covers \Atk4\Data\Persistence\Sql\Expression::getDebugQuery */ - public function testTestgetDebugQuery(): void + public function testGetDebugQuery(): void { $age = new Expression('coalesce([age], [default_age], [foo], [bar])'); $age['age'] = new Expression('year(now()) - year(birth_date)'); @@ -518,7 +518,7 @@ public function testTestgetDebugQuery(): void public function testVarDump(): void { $this->assertMatchesRegularExpression( - '/select\s+\*\s+from\s*"user".*/', + '~select\s+\*\s+from\s*"user"~', $this->q()->table('user')->__debugInfo()['R'] ); } @@ -528,8 +528,8 @@ public function testVarDump(): void */ public function testVarDump2(): void { - $this->assertMatchesRegularExpression( - '/.*Expression could not render tag.*/', + $this->assertStringContainsString( + 'Expression could not render tag', (new Expression('Hello [world]'))->__debugInfo()['R'] ); } @@ -539,8 +539,8 @@ public function testVarDump2(): void */ public function testVarDump3(): void { - $this->assertMatchesRegularExpression( - '/.*Hello \'php\'.*/', + $this->assertStringContainsString( + 'Hello \'php\'', (new Expression('Hello [world]', ['world' => 'php']))->__debugInfo()['R'] ); } @@ -551,8 +551,8 @@ public function testVarDump3(): void public function testVarDump4(): void { // should throw exception "Table cannot be Query in UPDATE, INSERT etc. query modes" - $this->assertMatchesRegularExpression( - '/.*Table cannot be Query.*/', + $this->assertStringContainsString( + 'Table cannot be Query', ($this->q() ->mode('update') ->table($this->q()->table('test'), 'foo'))->__debugInfo()['R'] @@ -1192,7 +1192,7 @@ public function testCombinedWhere(): void ); /* - $this->assertEquals( + $this->assertSame( 'select "name" from "db"."employee" where "db"."employee"."a" = :a', $this->q() ->field('name')->table('db.employee')->where('db.employee.a',1) diff --git a/tests/Persistence/Sql/WithDb/SelectTest.php b/tests/Persistence/Sql/WithDb/SelectTest.php index 6f082093b..0d16231f2 100644 --- a/tests/Persistence/Sql/WithDb/SelectTest.php +++ b/tests/Persistence/Sql/WithDb/SelectTest.php @@ -73,12 +73,12 @@ public function testBasicQueries(): void $this->assertSame( ['name' => 'Oliver', 'surname' => 'Smith'], - $this->q('employee')->field('name')->field('surname')->getRow() + $this->q('employee')->field('name')->field('surname')->order('id')->getRow() ); - $this->assertSame( - ['surname' => 'Williams'], - $this->q('employee')->field('surname')->where('retired', true)->getRow() + $this->assertSameExportUnordered( + [['surname' => 'Williams'], ['surname' => 'Taylor']], + $this->q('employee')->field('surname')->where('retired', true)->getRows() ); $this->assertSame( @@ -92,12 +92,12 @@ public function testBasicQueries(): void ); $names = []; - foreach ($this->q('employee')->where('retired', false)->getRowsIterator() as $row) { + foreach ($this->q('employee')->order('name')->where('retired', false)->getRowsIterator() as $row) { $names[] = $row['name']; } $this->assertSame( - ['Oliver', 'Charlie'], + ['Charlie', 'Oliver'], $names ); diff --git a/tests/Persistence/SqlTest.php b/tests/Persistence/SqlTest.php index 582aef030..02b51aa39 100644 --- a/tests/Persistence/SqlTest.php +++ b/tests/Persistence/SqlTest.php @@ -171,13 +171,13 @@ public function testModelInsertRows(): void $m->addField('name'); $m->addField('surname'); - $this->assertEquals(0, $m->action('exists')->getOne()); + $this->assertSame('0', $m->action('exists')->getOne()); $m->import($dbData['user']); // import data - $this->assertEquals(1, $m->action('exists')->getOne()); + $this->assertSame('1', $m->action('exists')->getOne()); - $this->assertEquals(2, $m->action('count')->getOne()); + $this->assertSame('2', $m->action('count')->getOne()); } public function testPersistenceDelete(): void @@ -226,12 +226,12 @@ public function testExport(): void $m->addField('name'); $m->addField('surname'); - $this->assertEquals([ + $this->assertSameExportUnordered([ ['id' => 1, 'name' => 'John', 'surname' => 'Smith'], ['id' => 2, 'name' => 'Sarah', 'surname' => 'Jones'], ], $m->export()); - $this->assertSame([ + $this->assertSameExportUnordered([ ['surname' => 'Smith'], ['surname' => 'Jones'], ], $m->export(['surname'])); diff --git a/tests/RandomTest.php b/tests/RandomTest.php index a38a5300f..068ebba6c 100644 --- a/tests/RandomTest.php +++ b/tests/RandomTest.php @@ -88,7 +88,7 @@ public function testRate(): void $m = new Model_Rate($this->db); - $this->assertEquals(2, $m->action('count')->getOne()); + $this->assertSame('2', $m->action('count')->getOne()); } public function testTitleImport(): void @@ -159,9 +159,9 @@ public function testAddFields2(): void $m->insert([]); - $this->assertEquals([ + $this->assertSameExportUnordered([ ['id' => 1, 'name' => 'John', 'last_name' => null, 'login' => null, 'salary' => null, 'tax' => null, 'vat' => null], - ['id' => 2, 'name' => 'anonymous', 'last_name' => null, 'login' => 'unknown', 'salary' => 100, 'tax' => 20, 'vat' => 15], + ['id' => 2, 'name' => 'anonymous', 'last_name' => null, 'login' => 'unknown', 'salary' => 100.0, 'tax' => 20.0, 'vat' => 15.0], ], $m->export()); $m = $m->load(2); @@ -234,7 +234,7 @@ public function testSameTable3(): void $m->load(2)->get() ); - $this->assertEquals(1, $m->load(2)->ref('Child', ['table_alias' => 'pp'])->action('count')->getOne()); + $this->assertSame('1', $m->load(2)->ref('Child', ['table_alias' => 'pp'])->action('count')->getOne()); $this->assertSame('John', $m->load(2)->ref('parent_item_id', ['table_alias' => 'pp'])->get('name')); } @@ -369,7 +369,7 @@ public function testGetTitle(): void $m = new Model_Item($this->db, ['table' => 'item']); - $this->assertSame([1 => 'John', 2 => 'Sue'], $m->getTitles()); // all titles + $this->assertSame([1 => 'John', 2 => 'Sue'], $m->setOrder('id')->getTitles()); // all titles $mm = $m->createEntity(); @@ -415,65 +415,65 @@ public function testExport(): void // model without id field $m1 = new Model($this->db, ['table' => 'user', 'id_field' => false]); - $m1->addField('code'); + $m1->addField('code', ['type' => 'integer']); $m1->addField('name'); // model with id field $m2 = new Model($this->db, ['table' => 'user']); - $m2->addField('code'); + $m2->addField('code', ['type' => 'integer']); $m2->addField('name'); // normal export - $this->assertEquals([ - 0 => ['code' => 10, 'name' => 'John'], - 1 => ['code' => 20, 'name' => 'Sarah'], + $this->assertSameExportUnordered([ + ['code' => 10, 'name' => 'John'], + ['code' => 20, 'name' => 'Sarah'], ], $m1->export()); - $this->assertEquals([ - 0 => ['id' => 2, 'code' => 10, 'name' => 'John'], - 1 => ['id' => 5, 'code' => 20, 'name' => 'Sarah'], + $this->assertSameExportUnordered([ + ['id' => 2, 'code' => 10, 'name' => 'John'], + ['id' => 5, 'code' => 20, 'name' => 'Sarah'], ], $m2->export()); // export fields explicitly set - $this->assertSame([ - 0 => ['name' => 'John'], - 1 => ['name' => 'Sarah'], + $this->assertSameExportUnordered([ + ['name' => 'John'], + ['name' => 'Sarah'], ], $m1->export(['name'])); - $this->assertSame([ - 0 => ['name' => 'John'], - 1 => ['name' => 'Sarah'], + $this->assertSameExportUnordered([ + ['name' => 'John'], + ['name' => 'Sarah'], ], $m2->export(['name'])); // key field explicitly set - $this->assertEquals([ + $this->assertSameExportUnordered([ 10 => ['code' => 10, 'name' => 'John'], 20 => ['code' => 20, 'name' => 'Sarah'], ], $m1->export(null, 'code')); - $this->assertEquals([ + $this->assertSameExportUnordered([ 10 => ['id' => 2, 'code' => 10, 'name' => 'John'], 20 => ['id' => 5, 'code' => 20, 'name' => 'Sarah'], ], $m2->export(null, 'code')); // field names and key field explicitly set - $this->assertSame([ + $this->assertSameExportUnordered([ 10 => ['name' => 'John'], 20 => ['name' => 'Sarah'], ], $m1->export(['name'], 'code')); - $this->assertSame([ + $this->assertSameExportUnordered([ 10 => ['name' => 'John'], 20 => ['name' => 'Sarah'], ], $m2->export(['name'], 'code')); // field names include key field - $this->assertEquals([ + $this->assertSameExportUnordered([ 10 => ['code' => 10, 'name' => 'John'], 20 => ['code' => 20, 'name' => 'Sarah'], ], $m1->export(['code', 'name'], 'code')); - $this->assertEquals([ + $this->assertSameExportUnordered([ 10 => ['code' => 10, 'name' => 'John'], 20 => ['code' => 20, 'name' => 'Sarah'], ], $m2->export(['code', 'name'], 'code')); @@ -492,7 +492,7 @@ public function testDuplicateSaveNew(): void $m->load(1)->duplicate()->save(); - $this->assertSame([ + $this->assertSameExportUnordered([ ['id' => 1, 'dat' => '18/12/12', 'bid' => 3.4, 'ask' => 9.4], ['id' => 2, 'dat' => '12/12/12', 'bid' => 8.3, 'ask' => 9.2], ['id' => 3, 'dat' => '18/12/12', 'bid' => 3.4, 'ask' => 9.4], diff --git a/tests/ReadOnlyModeTest.php b/tests/ReadOnlyModeTest.php index 625bd70f6..f3fe11242 100644 --- a/tests/ReadOnlyModeTest.php +++ b/tests/ReadOnlyModeTest.php @@ -37,9 +37,11 @@ protected function setUp(): void */ public function testBasic(): void { + $this->m->setOrder('name', 'asc'); $m = $this->m->tryLoadAny(); $this->assertSame('John', $m->get('name')); + $this->m->order = []; $this->m->setOrder('name', 'desc'); $m = $this->m->tryLoadAny(); $this->assertSame('Sue', $m->get('name')); diff --git a/tests/Schema/ModelTest.php b/tests/Schema/ModelTest.php index 3c39f47f8..a3dd3f3eb 100644 --- a/tests/Schema/ModelTest.php +++ b/tests/Schema/ModelTest.php @@ -72,8 +72,8 @@ public function testImportTable(): void $migrator2->mode('create'); - $q1 = preg_replace('/\([0-9,]*\)/i', '', $migrator->render()[0]); // remove parenthesis otherwise we can't differ money from float etc. - $q2 = preg_replace('/\([0-9,]*\)/i', '', $migrator2->render()[0]); + $q1 = preg_replace('~\([0-9,]*\)~', '', $migrator->render()[0]); // remove parenthesis otherwise we can't differ money from float etc. + $q2 = preg_replace('~\([0-9,]*\)~', '', $migrator2->render()[0]); $this->assertSame($q1, $q2); } @@ -134,7 +134,10 @@ public function testCharacterTypeFieldCaseSensitivity(string $type, bool $isBina $model->addCondition('v', 'MixedCase'); $model->setOrder($this->getDatabasePlatform() instanceof OraclePlatform && in_array($type, ['text', 'blob'], true) ? 'id' : 'v'); - $this->assertSame($isBinary ? [['id' => 3]] : [['id' => 1], ['id' => 2], ['id' => 3]], $model->export(['id'])); + $this->assertSameExportUnordered( + $isBinary ? [['id' => 3]] : [['id' => 1], ['id' => 2], ['id' => 3]], + $model->export(['id']) + ); } public function providerCharacterTypeFieldCaseSensitivityData(): array @@ -250,7 +253,7 @@ public function providerCharacterTypeFieldLongData(): array } } -class TestUser extends \Atk4\Data\Model +class TestUser extends Model { public $table = 'user'; @@ -267,7 +270,7 @@ protected function init(): void } } -class TestRole extends \Atk4\Data\Model +class TestRole extends Model { public $table = 'role'; diff --git a/tests/Schema/TestCaseTest.php b/tests/Schema/TestCaseTest.php index df437c528..a598ad434 100644 --- a/tests/Schema/TestCaseTest.php +++ b/tests/Schema/TestCaseTest.php @@ -22,8 +22,8 @@ public function testInit(): void $this->setDb($q2); $q3 = $this->getDb(['user']); - $this->assertSame($q2, $q3); + $this->assertSameExportUnordered($q2, $q3); - $this->assertSame($q, $this->getDb(['user'], true)); + $this->assertSameExportUnordered($q, $this->getDb(['user'], true)); } } diff --git a/tests/ScopeTest.php b/tests/ScopeTest.php index 1be153cbe..8283c40b2 100644 --- a/tests/ScopeTest.php +++ b/tests/ScopeTest.php @@ -204,7 +204,7 @@ public function testConditionOnReferencedRecords(): void $user->addCondition('country_id/code', 'LV'); - $this->assertEquals(1, $user->action('count')->getOne()); + $this->assertSame('1', $user->action('count')->getOne()); foreach ($user as $u) { $this->assertEquals('LV', $u->get('country_code')); @@ -215,7 +215,7 @@ public function testConditionOnReferencedRecords(): void // users that have no ticket $user->addCondition('Tickets/#', 0); - $this->assertEquals(1, $user->action('count')->getOne()); + $this->assertSame('1', $user->action('count')->getOne()); foreach ($user as $u) { $this->assertTrue(in_array($u->get('name'), ['Alain', 'Aerton', 'Rubens'], true)); @@ -253,7 +253,7 @@ public function testConditionOnReferencedRecords(): void // countries with users that have any tickets $country->addCondition('Users/Tickets/#', '>', 0); - $this->assertEquals(3, $country->action('count')->getOne()); + $this->assertSame('3', $country->action('count')->getOne()); foreach ($country as $c) { $this->assertTrue(in_array($c->get('code'), ['LV', 'CA', 'BR'], true)); @@ -264,7 +264,7 @@ public function testConditionOnReferencedRecords(): void // countries with users that have no tickets $country->addCondition('Users/Tickets/#', 0); - $this->assertEquals(1, $country->action('count')->getOne()); + $this->assertSame('1', $country->action('count')->getOne()); foreach ($country as $c) { $this->assertTrue(in_array($c->get('code'), ['FR'], true)); @@ -286,7 +286,7 @@ public function testConditionOnReferencedRecords(): void $user->addCondition('Tickets/user/country_id/Users/country_id/Users/name', '!=', null); // should be always true } - $this->assertEquals(2, $user->action('count')->getOne()); + $this->assertSame('2', $user->action('count')->getOne()); foreach ($user as $u) { $this->assertTrue(in_array($u->get('name'), ['Aerton', 'Rubens'], true)); } diff --git a/tests/SmboTransferTest.php b/tests/SmboTransferTest.php index 1dcb6d3bd..d8a798cdd 100644 --- a/tests/SmboTransferTest.php +++ b/tests/SmboTransferTest.php @@ -88,19 +88,19 @@ public function testRef(): void // Account is not loaded, will dump all Payments related to ANY Account $data = $a->ref('Payment')->export(['amount']); - $this->assertEquals([ - ['amount' => 10], - ['amount' => 20], - ['amount' => 30], - //['amount' => 40], // will not select this because it is not related to any Account + $this->assertSameExportUnordered([ + ['amount' => 10.0], + ['amount' => 20.0], + ['amount' => 30.0], + // ['amount' => 40.0], // will not select this because it is not related to any Account ], $data); // Account is loaded, will dump all Payments related to that particular Account $a = $a->load(1); $data = $a->ref('Payment')->export(['amount']); - $this->assertEquals([ - ['amount' => 10], - ['amount' => 20], + $this->assertSameExportUnordered([ + ['amount' => 10.0], + ['amount' => 20.0], ], $data); } @@ -121,7 +121,7 @@ public function testBasicEntities(): void // Create two new clients, one is sole trader, other is limited company $client = $company->ref('Client'); - list($john_id, $agile_id) = $m->insert([ + [$john_id, $agile_id] = $m->insert([ ['name' => 'John Smith Consulting', 'vat_registered' => false], 'Agile Software Limited', ]); @@ -133,8 +133,8 @@ public function testBasicEntities(): void 'ref_no' => 'INV1', 'due_date' => (new Date())->add(new DateInterval('2w')), // due in 2 weeks 'lines' => [ - ['descr' => 'Sold some sweets', 'total_gross' => 100.00], - ['descr' => 'Delivery', 'total_gross' => 10.00], + ['descr' => 'Sold some sweets', 'total_gross' => 100.0], + ['descr' => 'Delivery', 'total_gross' => 10.0], ], ]); @@ -147,15 +147,15 @@ public function testBasicEntities(): void [ 'item_id' => $john->ref('Product')->insert('Cat Food'), 'nominal' => 'Sales:Discounted', - 'total_net' => 50.00, + 'total_net' => 50.0, 'vat_rate' => 23, - // calculates total_gross at 61.50. + // calculates total_gross at 61.5. ], [ 'item_id' => $john->ref('Service')->insert('Delivery'), - 'total_net' => 10.00, + 'total_net' => 10.0, 'vat_rate' => '23%', - // calculates total_gross at 12.30 + // calculates total_gross at 12.3 ], ], ]); @@ -165,15 +165,15 @@ public function testBasicEntities(): void // And each of our invoices will have one new payment foreach ($john_invoices as $invoice) { - $invoice->ref('Payment')->insert(['amount' => 10.20, 'bank_account_id' => $hsbc]); + $invoice->ref('Payment')->insert(['amount' => 10.2, 'bank_account_id' => $hsbc]); } // Now let's execute report $debt = $john->add(new Model_Report_Debtors()); // This should give us total amount owed by all clients: - // (100.00+10.00) + (61.50 + 12.30) - 10.20*2 - $this->assertEquals(163.40, $debt->sum('amount')); + // (100.0 + 10.0) + (61.5 + 12.3) - 10.2 * 2 + $this->assertSame(163.4, $debt->sum('amount')); } */ } diff --git a/tests/SubTypesTest.php b/tests/SubTypesTest.php index 0ca9a3e7a..8dafc0d72 100644 --- a/tests/SubTypesTest.php +++ b/tests/SubTypesTest.php @@ -171,11 +171,12 @@ public function testBasic(): void $this->assertInstanceOf(StTransaction_TransferIn::class, $current->ref('Transactions')->load(3)); $this->assertInstanceOf(StTransaction_Withdrawal::class, $current->ref('Transactions')->load(4)); - $cl = []; + $classes = []; foreach ($current->ref('Transactions') as $tr) { - $cl[] = get_class($tr); + $classes[] = get_class($tr); } + sort($classes); - $this->assertSame([StTransaction_TransferIn::class, StTransaction_Withdrawal::class], $cl); + $this->assertSame([StTransaction_TransferIn::class, StTransaction_Withdrawal::class], $classes); } } diff --git a/tests/TypecastingTest.php b/tests/TypecastingTest.php index 0e8783d3a..1e9119202 100644 --- a/tests/TypecastingTest.php +++ b/tests/TypecastingTest.php @@ -83,7 +83,7 @@ public function testType(): void 'time' => '12:00:50.000000', 'boolean' => 1, 'integer' => 2940, - 'money' => 8.20, + 'money' => 8.2, 'float' => 8.202343, 'json' => '[1,2,3]', ], diff --git a/tests/Util/DeepCopyTest.php b/tests/Util/DeepCopyTest.php index 007a705cd..f00d64cb5 100644 --- a/tests/Util/DeepCopyTest.php +++ b/tests/Util/DeepCopyTest.php @@ -166,7 +166,7 @@ public function testBasic(): void $quote = $quote->loadOne(); // total price should match - $this->assertEquals(90.00, $quote->get('total')); + $this->assertEquals(90.0, $quote->get('total')); $dc = new DeepCopy(); $invoice = $dc @@ -177,7 +177,7 @@ public function testBasic(): void // price now will be with VAT $this->assertSame('q1', $invoice->get('ref')); - $this->assertEquals(108.90, $invoice->get('total')); + $this->assertEquals(108.9, $invoice->get('total')); $this->assertEquals(1, $invoice->getId()); // Note that we did not specify that 'client_id' should be copied, so same value here @@ -243,9 +243,9 @@ public function testBasic(): void $this->assertEquals(3, $client3->getId()); // We should have one of each records for this new client - $this->assertEquals(1, $client3->ref('Invoices')->action('count')->getOne()); - $this->assertEquals(1, $client3->ref('Quotes')->action('count')->getOne()); - $this->assertEquals(1, $client3->ref('Payments')->action('count')->getOne()); + $this->assertSame('1', $client3->ref('Invoices')->action('count')->getOne()); + $this->assertSame('1', $client3->ref('Quotes')->action('count')->getOne()); + $this->assertSame('1', $client3->ref('Payments')->action('count')->getOne()); if ($this->getDatabasePlatform() instanceof SQLServerPlatform) { $this->markTestIncomplete('TODO MSSQL: Cannot perform an aggregate function on an expression containing an aggregate or a subquery.'); @@ -286,7 +286,7 @@ public function testError(): void }); // total price should match - $this->assertEquals(90.00, $quote->get('total')); + $this->assertEquals(90.0, $quote->get('total')); $dc = new DeepCopy(); @@ -327,7 +327,7 @@ public function testDeepError(): void }); // total price should match - $this->assertEquals(90.00, $quote->get('total')); + $this->assertEquals(90.0, $quote->get('total')); $dc = new DeepCopy(); diff --git a/tests/WithTest.php b/tests/WithTest.php index 3d8897362..bcc285ea1 100644 --- a/tests/WithTest.php +++ b/tests/WithTest.php @@ -7,6 +7,7 @@ use Atk4\Data\Exception; use Atk4\Data\Model; use Atk4\Data\Schema\TestCase; +use Doctrine\DBAL\Platforms\MySQLPlatform; class WithTest extends TestCase { @@ -46,7 +47,15 @@ public function testWith(): void . 'select "user"."id", "user"."name", "user"."salary", "_i"."invoiced" from "user" inner join "i" "_i" on "_i"."user_id" = "user"."id"', $m->action('select')->render()[0] ); - $this->assertSame([ + + if ($this->getDatabasePlatform() instanceof MySQLPlatform) { + $serverVersion = $this->db->connection->connection()->getWrappedConnection()->getServerVersion(); + if (str_starts_with($serverVersion, '5.')) { + $this->markTestIncomplete('MySQL Server 5.x does not support WITH clause'); + } + } + + $this->assertSameExportUnordered([ ['id' => 10, 'name' => 'John', 'salary' => 2500, 'invoiced' => 500], ['id' => 20, 'name' => 'Peter', 'salary' => 4000, 'invoiced' => 200], ['id' => 20, 'name' => 'Peter', 'salary' => 4000, 'invoiced' => 400],