mirror of
https://github.com/danog/AsyncOrm.git
synced 2024-11-26 12:24:59 +01:00
Fixes
This commit is contained in:
parent
6fd7fd854f
commit
21ad5239e1
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,3 +1,4 @@
|
||||
.phpunit.cache
|
||||
/vendor/
|
||||
*.cache
|
||||
composer.lock
|
||||
|
14
.vscode/launch.json
vendored
Normal file
14
.vscode/launch.json
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Listen for Xdebug",
|
||||
"type": "php",
|
||||
"request": "launch",
|
||||
"port": 9003
|
||||
}
|
||||
]
|
||||
}
|
@ -8,6 +8,11 @@
|
||||
"danog\\AsyncOrm\\": "src/"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"danog\\AsyncOrm\\Test\\": "tests/"
|
||||
}
|
||||
},
|
||||
"authors": [
|
||||
{
|
||||
"name": "Alexander Pankratov",
|
||||
@ -29,7 +34,8 @@
|
||||
"vimeo/psalm": "dev-master",
|
||||
"phpunit/phpunit": "^11.0.6",
|
||||
"amphp/php-cs-fixer-config": "^2.0.1",
|
||||
"friendsofphp/php-cs-fixer": "^3.51"
|
||||
"friendsofphp/php-cs-fixer": "^3.51",
|
||||
"amphp/process": "^2.0"
|
||||
},
|
||||
"scripts": {
|
||||
"cs-fix": "PHP_CS_FIXER_IGNORE_ENV=1 php -d pcre.jit=0 vendor/bin/php-cs-fixer fix -v"
|
||||
|
23
phpunit.xml
Normal file
23
phpunit.xml
Normal file
@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/11.0/phpunit.xsd"
|
||||
bootstrap="vendor/autoload.php"
|
||||
cacheDirectory=".phpunit.cache"
|
||||
executionOrder="depends,defects"
|
||||
requireCoverageMetadata="true"
|
||||
beStrictAboutCoverageMetadata="true"
|
||||
beStrictAboutOutputDuringTests="true"
|
||||
failOnRisky="true"
|
||||
failOnWarning="true">
|
||||
<testsuites>
|
||||
<testsuite name="default">
|
||||
<directory>tests</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
|
||||
<source restrictDeprecations="true" restrictNotices="true" restrictWarnings="true">
|
||||
<include>
|
||||
<directory>src</directory>
|
||||
</include>
|
||||
</source>
|
||||
</phpunit>
|
@ -25,6 +25,8 @@ use AssertionError;
|
||||
use danog\AsyncOrm\Driver\Mysql;
|
||||
use danog\AsyncOrm\Driver\SqlArray;
|
||||
use danog\AsyncOrm\FieldConfig;
|
||||
use danog\AsyncOrm\Internal\Serializer\IntString;
|
||||
use danog\AsyncOrm\Internal\Serializer\Passthrough;
|
||||
use danog\AsyncOrm\KeyType;
|
||||
use danog\AsyncOrm\Serializer;
|
||||
use danog\AsyncOrm\ValueType;
|
||||
@ -56,6 +58,12 @@ final class MysqlArray extends SqlArray
|
||||
*/
|
||||
public function __construct(FieldConfig $config, Serializer $serializer)
|
||||
{
|
||||
/** @var Serializer<TValue> */
|
||||
$serializer = match ($config->valueType) {
|
||||
ValueType::INT => new IntString,
|
||||
ValueType::STRING => new Passthrough,
|
||||
default => $serializer
|
||||
};
|
||||
$settings = $config->settings;
|
||||
\assert($settings instanceof \danog\AsyncOrm\Settings\Mysql);
|
||||
|
||||
|
@ -22,9 +22,9 @@ use Amp\Postgres\PostgresConnectionPool;
|
||||
use Amp\Sync\LocalKeyedMutex;
|
||||
use danog\AsyncOrm\Driver\SqlArray;
|
||||
use danog\AsyncOrm\FieldConfig;
|
||||
use danog\AsyncOrm\Internal\Serializer\ByteaSerializer;
|
||||
use danog\AsyncOrm\KeyType;
|
||||
use danog\AsyncOrm\Serializer;
|
||||
use danog\AsyncOrm\Serializer\ByteaSerializer;
|
||||
use danog\AsyncOrm\Settings\Postgres;
|
||||
use danog\AsyncOrm\ValueType;
|
||||
use Revolt\EventLoop;
|
||||
@ -104,7 +104,7 @@ class PostgresArray extends SqlArray
|
||||
);
|
||||
");
|
||||
|
||||
$result = $this->db->query("DESCRIBE \"bytea_{$config->table}\"");
|
||||
$result = $connection->query("DESCRIBE \"bytea_{$config->table}\"");
|
||||
while ($column = $result->fetchRow()) {
|
||||
['Field' => $key, 'Type' => $type, 'Null' => $null] = $column;
|
||||
$type = \strtoupper($type);
|
||||
@ -116,11 +116,11 @@ class PostgresArray extends SqlArray
|
||||
} elseif ($key === 'value') {
|
||||
$expected = $valueType;
|
||||
} else {
|
||||
$this->db->query("ALTER TABLE \"bytea_{$config->table}\" DROP \"$key\"");
|
||||
$connection->query("ALTER TABLE \"bytea_{$config->table}\" DROP \"$key\"");
|
||||
continue;
|
||||
}
|
||||
if ($expected !== $type || $null !== 'NO') {
|
||||
$this->db->query("ALTER TABLE \"bytea_{$config->table}\" MODIFY \"$key\" $expected NOT NULL");
|
||||
$connection->query("ALTER TABLE \"bytea_{$config->table}\" MODIFY \"$key\" $expected NOT NULL");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -23,9 +23,9 @@ use Amp\Redis\RedisClient;
|
||||
use Amp\Sync\LocalKeyedMutex;
|
||||
use danog\AsyncOrm\Driver\DriverArray;
|
||||
use danog\AsyncOrm\FieldConfig;
|
||||
use danog\AsyncOrm\Internal\Serializer\IntString;
|
||||
use danog\AsyncOrm\Internal\Serializer\Passthrough;
|
||||
use danog\AsyncOrm\Serializer;
|
||||
use danog\AsyncOrm\Serializer\IntString;
|
||||
use danog\AsyncOrm\Serializer\Passthrough;
|
||||
use danog\AsyncOrm\Settings\Redis;
|
||||
use danog\AsyncOrm\ValueType;
|
||||
use Revolt\EventLoop;
|
||||
@ -54,9 +54,12 @@ final class RedisArray extends DriverArray
|
||||
*/
|
||||
public function __construct(FieldConfig $config, Serializer $serializer)
|
||||
{
|
||||
if ($serializer instanceof Passthrough && $config->valueType === ValueType::INT) {
|
||||
$serializer = new IntString;
|
||||
}
|
||||
/** @var Serializer<TValue> */
|
||||
$serializer = match ($config->valueType) {
|
||||
ValueType::INT => new IntString,
|
||||
ValueType::STRING => new Passthrough,
|
||||
default => $serializer
|
||||
};
|
||||
parent::__construct($config, $serializer);
|
||||
|
||||
self::$mutex ??= new LocalKeyedMutex;
|
||||
|
@ -16,7 +16,7 @@
|
||||
* @link https://daniil.it/AsyncOrm AsyncOrm documentation
|
||||
*/
|
||||
|
||||
namespace danog\AsyncOrm\Serializer;
|
||||
namespace danog\AsyncOrm\Internal\Serializer;
|
||||
|
||||
use Amp\Postgres\PostgresByteA;
|
||||
use danog\AsyncOrm\Serializer;
|
@ -16,14 +16,14 @@
|
||||
* @link https://daniil.it/AsyncOrm AsyncOrm documentation
|
||||
*/
|
||||
|
||||
namespace danog\AsyncOrm\Serializer;
|
||||
namespace danog\AsyncOrm\Internal\Serializer;
|
||||
|
||||
use danog\AsyncOrm\Serializer;
|
||||
|
||||
/**
|
||||
* Integer casting serializer.
|
||||
*
|
||||
* @api
|
||||
* @internal
|
||||
*
|
||||
* @implements Serializer<int>
|
||||
*/
|
@ -16,14 +16,14 @@
|
||||
* @link https://daniil.it/AsyncOrm AsyncOrm documentation
|
||||
*/
|
||||
|
||||
namespace danog\AsyncOrm\Serializer;
|
||||
namespace danog\AsyncOrm\Internal\Serializer;
|
||||
|
||||
use danog\AsyncOrm\Serializer;
|
||||
|
||||
/**
|
||||
* Passthrough serializer.
|
||||
*
|
||||
* @api
|
||||
* @internal
|
||||
*
|
||||
* @template TValue
|
||||
* @implements Serializer<TValue>
|
@ -50,7 +50,7 @@ abstract readonly class SqlSettings extends DriverSettings
|
||||
public function __construct(
|
||||
/** @var T */
|
||||
public readonly SqlConfig $config,
|
||||
Serializer $serializer,
|
||||
?Serializer $serializer,
|
||||
int $cacheTtl = self::DEFAULT_CACHE_TTL,
|
||||
int $maxConnections = self::DEFAULT_SQL_MAX_CONNECTIONS,
|
||||
int $idleTimeout = self::DEFAULT_SQL_IDLE_TIMEOUT,
|
||||
|
174
tests/OrmTest.php
Normal file
174
tests/OrmTest.php
Normal file
@ -0,0 +1,174 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* This file is part of AsyncOrm.
|
||||
* AsyncOrm is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
* AsyncOrm is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU Affero General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License along with AsyncOrm.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
* @copyright 2016-2024 Daniil Gentili <daniil@daniil.it>
|
||||
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
|
||||
* @link https://daniil.it/AsyncOrm AsyncOrm documentation
|
||||
*/
|
||||
|
||||
namespace danog\AsyncOrm\Test;
|
||||
|
||||
use Amp\ByteStream\ReadableStream;
|
||||
use Amp\Mysql\MysqlConfig;
|
||||
use Amp\Postgres\PostgresConfig;
|
||||
use Amp\Process\Process;
|
||||
use Amp\Redis\RedisConfig;
|
||||
use AssertionError;
|
||||
use danog\AsyncOrm\FieldConfig;
|
||||
use danog\AsyncOrm\KeyType;
|
||||
use danog\AsyncOrm\Serializer\Igbinary;
|
||||
use danog\AsyncOrm\Serializer\Json;
|
||||
use danog\AsyncOrm\Serializer\Native;
|
||||
use danog\AsyncOrm\Settings;
|
||||
use danog\AsyncOrm\Settings\Mysql;
|
||||
use danog\AsyncOrm\Settings\Postgres;
|
||||
use danog\AsyncOrm\Settings\Redis;
|
||||
use danog\AsyncOrm\ValueType;
|
||||
use PHPUnit\Framework\Attributes\CoversNothing;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
use function Amp\async;
|
||||
use function Amp\ByteStream\buffer;
|
||||
use function Amp\ByteStream\getStderr;
|
||||
use function Amp\ByteStream\getStdout;
|
||||
use function Amp\ByteStream\pipe;
|
||||
use function Amp\ByteStream\splitLines;
|
||||
use function Amp\Future\await;
|
||||
use function Amp\Future\awaitAny;
|
||||
|
||||
#[CoversNothing]
|
||||
final class OrmTest extends TestCase
|
||||
{
|
||||
/** @var array<string, Process> */
|
||||
private static array $processes = [];
|
||||
private static function shellExec(string $cmd): void
|
||||
{
|
||||
$process = Process::start($cmd);
|
||||
async(pipe(...), $process->getStderr(), getStderr());
|
||||
async(pipe(...), $process->getStdout(), getStdout());
|
||||
$process->join();
|
||||
}
|
||||
public static function setUpBeforeClass(): void
|
||||
{
|
||||
$f = [];
|
||||
foreach (['redis' => 6379, 'mariadb' => 3306, 'postgres' => 5432] as $image => $port) {
|
||||
$f []= async(function () use ($image, $port) {
|
||||
await([
|
||||
async(self::shellExec(...), "docker pull $image"),
|
||||
async(self::shellExec(...), "docker rm -f test_$image 2>/dev/null")
|
||||
]);
|
||||
|
||||
$args = match ($image) {
|
||||
'postgres' => '-e POSTGRES_HOST_AUTH_METHOD=trust',
|
||||
'mariadb' => '-e MARIADB_ALLOW_EMPTY_ROOT_PASSWORD=1',
|
||||
default => ''
|
||||
};
|
||||
$process = Process::start(
|
||||
"docker run --rm -p $port:$port $args --name test_$image $image"
|
||||
);
|
||||
self::$processes[$image] = $process;
|
||||
});
|
||||
}
|
||||
await($f);
|
||||
if (!self::$processes) {
|
||||
throw new AssertionError("No processes!");
|
||||
}
|
||||
foreach (self::$processes as $name => $process) {
|
||||
$ok = awaitAny([
|
||||
async(self::waitForStartup(...), $process->getStdout()),
|
||||
async(self::waitForStartup(...), $process->getStderr()),
|
||||
]);
|
||||
if (!$ok) {
|
||||
throw new AssertionError("Could not start $name!");
|
||||
}
|
||||
}
|
||||
}
|
||||
private static function waitForStartup(ReadableStream $f): bool
|
||||
{
|
||||
foreach (splitLines($f) as $line) {
|
||||
if (\stripos($line, 'ready to ') !== false
|
||||
|| \stripos($line, "socket: '/run/mysqld/mysqld.sock' port: 3306") !== false
|
||||
) {
|
||||
async(buffer(...), $f);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
public static function tearDownAfterClass(): void
|
||||
{
|
||||
foreach (self::$processes as $process) {
|
||||
$process->signal(15);
|
||||
}
|
||||
}
|
||||
|
||||
#[DataProvider('provideSettings')]
|
||||
public function testAll(Settings $settings, KeyType $keyType, string|int $key): void
|
||||
{
|
||||
$field = new FieldConfig(
|
||||
"test",
|
||||
$settings,
|
||||
$keyType,
|
||||
ValueType::INT
|
||||
);
|
||||
$orm = $field->build();
|
||||
$orm[$key] = 123;
|
||||
|
||||
$this->assertEquals(123, $orm[$key]);
|
||||
$this->assertTrue(isset($orm[$key]));
|
||||
unset($orm[$key]);
|
||||
|
||||
$this->assertNull($orm[$key]);
|
||||
$this->assertFalse(isset($orm[$key]));
|
||||
}
|
||||
|
||||
public static function provideSettings(): \Generator
|
||||
{
|
||||
foreach ([new Native, new Igbinary, new Json] as $serializer) {
|
||||
foreach ([
|
||||
new Redis(
|
||||
RedisConfig::fromUri('redis://127.0.0.1'),
|
||||
$serializer
|
||||
),
|
||||
new Postgres(
|
||||
PostgresConfig::fromString('host=127.0.0.1:5432 user=postgres db=test'),
|
||||
$serializer
|
||||
),
|
||||
new Mysql(
|
||||
MysqlConfig::fromString('host=127.0.0.1:3306 user=root db=test'),
|
||||
$serializer
|
||||
),
|
||||
] as $settings) {
|
||||
yield [
|
||||
$settings,
|
||||
KeyType::INT,
|
||||
123
|
||||
];
|
||||
yield [
|
||||
$settings,
|
||||
KeyType::STRING,
|
||||
'test'
|
||||
];
|
||||
yield [
|
||||
$settings,
|
||||
KeyType::STRING_OR_INT,
|
||||
'test'
|
||||
];
|
||||
yield [
|
||||
$settings,
|
||||
KeyType::STRING_OR_INT,
|
||||
'test'
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user