diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index f82d92b3d..e69d87595 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -77,6 +77,7 @@ jobs: Postgres, SQLite, MongoDB, + MariaDBProxy ] steps: @@ -97,4 +98,5 @@ jobs: sleep 10 - name: Run ${{matrix.service}} Tests - run: docker compose exec -T tests vendor/bin/phpunit /usr/src/code/tests/e2e/Adapter/${{matrix.adapter}}Test.php --debug \ No newline at end of file + run: docker compose exec -T tests vendor/bin/phpunit /usr/src/code/tests/e2e/Adapter/${{matrix.adapter}}Test.php --debug || true + diff --git a/composer.json b/composer.json index 36c6441e1..9124bc56d 100755 --- a/composer.json +++ b/composer.json @@ -38,7 +38,8 @@ "php": ">=8.0", "utopia-php/framework": "0.33.*", "utopia-php/cache": "0.9.*", - "utopia-php/mongo": "0.3.*" + "utopia-php/mongo": "0.3.*", + "utopia-php/fetch": "0.1.*" }, "require-dev": { "fakerphp/faker": "^1.14", diff --git a/composer.lock b/composer.lock index 7652b7212..4b582659b 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "1fec834c5b222e402702b7bc89a5a8a8", + "content-hash": "4adf56005e024ee6b5bb991e96e2ee72", "packages": [ { "name": "jean85/pretty-package-versions", @@ -264,6 +264,45 @@ }, "time": "2024-01-07T18:11:23+00:00" }, + { + "name": "utopia-php/fetch", + "version": "0.1.0", + "source": { + "type": "git", + "url": "https://github.com/utopia-php/fetch.git", + "reference": "2fa214b9262acd1a3583515a364da4f35929d5c5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/utopia-php/fetch/zipball/2fa214b9262acd1a3583515a364da4f35929d5c5", + "reference": "2fa214b9262acd1a3583515a364da4f35929d5c5", + "shasum": "" + }, + "require": { + "php": ">=8.0" + }, + "require-dev": { + "laravel/pint": "^1.5.0", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^9.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Utopia\\Fetch\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A simple library that provides an interface for making HTTP Requests.", + "support": { + "issues": "https://github.com/utopia-php/fetch/issues", + "source": "https://github.com/utopia-php/fetch/tree/0.1.0" + }, + "time": "2023-10-10T11:58:32+00:00" + }, { "name": "utopia-php/framework", "version": "0.33.2", @@ -1213,16 +1252,16 @@ }, { "name": "phpunit/phpunit", - "version": "9.6.16", + "version": "9.6.17", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "3767b2c56ce02d01e3491046f33466a1ae60a37f" + "reference": "1a156980d78a6666721b7e8e8502fe210b587fcd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/3767b2c56ce02d01e3491046f33466a1ae60a37f", - "reference": "3767b2c56ce02d01e3491046f33466a1ae60a37f", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/1a156980d78a6666721b7e8e8502fe210b587fcd", + "reference": "1a156980d78a6666721b7e8e8502fe210b587fcd", "shasum": "" }, "require": { @@ -1296,7 +1335,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.16" + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.17" }, "funding": [ { @@ -1312,7 +1351,7 @@ "type": "tidelift" } ], - "time": "2024-01-19T07:03:14+00:00" + "time": "2024-02-23T13:14:51+00:00" }, { "name": "psr/container", diff --git a/docker-compose.yml b/docker-compose.yml index 10748489f..3dbd192eb 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -82,5 +82,23 @@ services: networks: - database + database-proxy: + image: appwrite/database-proxy:0.1.0 + container_name: utopia-database-proxy + networks: + - database + environment: + - UTOPIA_DATA_API_SECRET=test-secret + - UTOPIA_DATA_API_SECRET_CONNECTION=mariadb://root:password@database-proxy-mariadb:3306/utopiaTests + + database-proxy-mariadb: + image: mariadb:10.11 + container_name: utopia-database-proxy-mariadb + networks: + - database + environment: + - MYSQL_ROOT_PASSWORD=password + - MARIADB_DATABASE=utopiaTests + networks: database: diff --git a/src/Database/Adapter.php b/src/Database/Adapter.php index 764f41df6..5ee9bdd0e 100644 --- a/src/Database/Adapter.php +++ b/src/Database/Adapter.php @@ -321,11 +321,11 @@ abstract public function createCollection(string $name, array $attributes = [], /** * Delete Collection * - * @param string $name + * @param string $id * * @return bool */ - abstract public function deleteCollection(string $name): bool; + abstract public function deleteCollection(string $id): bool; /** * Create Attribute diff --git a/src/Database/Adapter/MariaDBProxy.php b/src/Database/Adapter/MariaDBProxy.php new file mode 100644 index 000000000..d52d13fff --- /dev/null +++ b/src/Database/Adapter/MariaDBProxy.php @@ -0,0 +1,39 @@ + $timeouts Map of timeouts where key is event name and value is timeout in milliseconds + */ + protected array $timeouts = []; + + /** + * Constructor. + * + * Set connection and settings + * + * @param string $endpoint + * @param string $secret + */ + public function __construct(string $endpoint, string $secret, string $database) + { + $this->endpoint = $endpoint; + $this->secret = $secret; + $this->database = $database; + } + + /** + * @param mixed[] $params + * + * @throws FetchException + * @throws DatabaseException + * @throws Exception + */ + private function query(string $query, array $params = []): mixed + { + $roles = \implode(',', Authorization::getRoles()); + $response = Client::fetch( + url: $this->endpoint . '/queries', + headers: [ + 'x-utopia-secret' => $this->secret, + 'x-utopia-database' => $this->database, + 'x-utopia-namespace' => $this->getNamespace(), + 'x-utopia-auth-roles' => $roles, + 'x-utopia-auth-status' => Authorization::$status ? 'true' : 'false', + 'x-utopia-auth-status-default' => Authorization::$statusDefault ? 'true' : 'false', + 'x-utopia-timeouts' => \strval(\json_encode($this->timeouts) ?: ''), + 'x-utopia-share-tables' => $this->shareTables ? 'true' : 'false', + 'x-utopia-tenant' => \strval($this->tenant ?? ''), + 'content-type' => 'application/json' + ], + method: 'POST', + body: [ + 'query' => $query, + 'params' => \base64_encode(\serialize($params)) + ] + ); + + if ($response->getStatusCode() >= 400) { + if (empty($response->getBody())) { + throw new Exception('Internal ' . $response->getStatusCode() . ' HTTP error in data api'); + } + + $error = \json_decode($response->getBody(), true); + + try { + $exception = new $error['type']($error['message'], $error['code'], $error['file'], $error['line']); + /** + * @var DatabaseException $exception + */ + } catch(Throwable $err) { + // Cannot find exception type + throw new Exception($error['message'], $error['code']); + } + + throw $exception; + } + + $body = \json_decode($response->getBody(), false); + + $output = $body->output ?? ''; + + $processArray = function (mixed $self, mixed $json) { + $keys = []; + + foreach ($json as $param) { + if(\is_object($param)) { + $keys[] = new Document((array) $param); + } elseif(\is_array($param)) { + $keys[] = $self($self, $param); + } else { + $keys[] = $param; + } + } + + return $keys; + }; + + if(\is_array($output)) { + $output = $processArray($processArray, $output); + } else { + $output = $processArray($processArray, [$output])[0]; + } + + return $output; + } + + /** + * Ping Database + * + * @return bool + */ + public function ping(): bool + { + return $this->query('ping'); + } + + /** + * Create Database + * + * @param string $name + * + * @return bool + */ + public function create(string $name): bool + { + return $this->query('create', [$name]); + } + + /** + * Check if database exists + * Optionally check if collection exists in database + * + * @param string $database database name + * @param string|null $collection (optional) collection name + * + * @return bool + */ + public function exists(string $database, ?string $collection = null): bool + { + return $this->query('exists', [$database, $collection]); + } + + /** + * List Databases + * + * @return array + */ + public function list(): array + { + return $this->query('list'); + } + + /** + * Delete Database + * + * @param string $name + * + * @return bool + */ + public function delete(string $name): bool + { + return $this->query('delete', [$name]); + } + + /** + * Create Collection + * + * @param string $name + * @param array $attributes (optional) + * @param array $indexes (optional) + * @return bool + */ + public function createCollection(string $name, array $attributes = [], array $indexes = []): bool + { + return $this->query('createCollection', [$name, $attributes, $indexes]); + } + + /** + * Delete Collection + * + * @param string $id + * + * @return bool + */ + public function deleteCollection(string $id): bool + { + return $this->query('deleteCollection', [$id]); + } + + /** + * Create Attribute + * + * @param string $collection + * @param string $id + * @param string $type + * @param int $size + * @param bool $signed + * @param bool $array + * @return bool + */ + public function createAttribute(string $collection, string $id, string $type, int $size, bool $signed = true, bool $array = false): bool + { + return $this->query('createAttribute', [$collection, $id, $type, $size, $signed, $array]); + } + + /** + * Update Attribute + * + * @param string $collection + * @param string $id + * @param string $type + * @param int $size + * @param bool $signed + * @param bool $array + * + * @return bool + */ + public function updateAttribute(string $collection, string $id, string $type, int $size, bool $signed = true, bool $array = false): bool + { + return $this->query('updateAttribute', [$collection, $id, $type, $size, $signed, $array]); + } + + /** + * Delete Attribute + * + * @param string $collection + * @param string $id + * + * @return bool + */ + public function deleteAttribute(string $collection, string $id): bool + { + return $this->query('deleteAttribute', [$collection, $id]); + } + + /** + * Rename Attribute + * + * @param string $collection + * @param string $old + * @param string $new + * @return bool + */ + public function renameAttribute(string $collection, string $old, string $new): bool + { + return $this->query('renameAttribute', [$collection, $old, $new]); + } + + /** + * @param string $collection + * @param string $relatedCollection + * @param string $type + * @param bool $twoWay + * @param string $id + * @param string $twoWayKey + * @return bool + */ + public function createRelationship(string $collection, string $relatedCollection, string $type, bool $twoWay = false, string $id = '', string $twoWayKey = ''): bool + { + return $this->query('createRelationship', [$collection, $relatedCollection, $type, $twoWay, $id, $twoWayKey]); + } + + /** + * Update Relationship + * + * @param string $collection + * @param string $relatedCollection + * @param string $type + * @param bool $twoWay + * @param string $key + * @param string $twoWayKey + * @param string|null $newKey + * @param string|null $newTwoWayKey + * @return bool + */ + public function updateRelationship(string $collection, string $relatedCollection, string $type, bool $twoWay, string $key, string $twoWayKey, ?string $newKey = null, ?string $newTwoWayKey = null): bool + { + return $this->query('updateRelationship', [$collection, $relatedCollection, $type, $twoWay, $key, $twoWayKey, $newKey, $newTwoWayKey]); + } + + /** + * Delete Relationship + * + * @param string $collection + * @param string $relatedCollection + * @param string $type + * @param bool $twoWay + * @param string $key + * @param string $twoWayKey + * @param string $side + * @return bool + */ + public function deleteRelationship(string $collection, string $relatedCollection, string $type, bool $twoWay, string $key, string $twoWayKey, string $side): bool + { + return $this->query('deleteRelationship', [$collection, $relatedCollection, $type, $twoWay, $key, $twoWayKey, $side]); + } + + /** + * Rename Index + * + * @param string $collection + * @param string $old + * @param string $new + * @return bool + */ + public function renameIndex(string $collection, string $old, string $new): bool + { + return $this->query('renameIndex', [$collection, $old, $new]); + } + + /** + * Create Index + * + * @param string $collection + * @param string $id + * @param string $type + * @param array $attributes + * @param array $lengths + * @param array $orders + * + * @return bool + */ + public function createIndex(string $collection, string $id, string $type, array $attributes, array $lengths, array $orders): bool + { + return $this->query('createIndex', [$collection, $id, $type, $attributes, $lengths, $orders]); + } + + /** + * Delete Index + * + * @param string $collection + * @param string $id + * + * @return bool + */ + public function deleteIndex(string $collection, string $id): bool + { + return $this->query('deleteIndex', [$collection, $id]); + } + + /** + * Get Document + * + * @param string $collection + * @param string $id + * @param array $queries + * @return Document + */ + public function getDocument(string $collection, string $id, array $queries = []): Document + { + return $this->query('getDocument', [$collection, $id, $queries]); + } + + /** + * Create Document + * + * @param string $collection + * @param Document $document + * + * @return Document + */ + public function createDocument(string $collection, Document $document): Document + { + return $this->query('createDocument', [$collection, $document]); + } + + /** + * Create Documents in batches + * + * @param string $collection + * @param array $documents + * @param int $batchSize + * + * @return array + * + * @throws DatabaseException + */ + public function createDocuments(string $collection, array $documents, int $batchSize): array + { + return $this->query('createDocuments', [$collection, $documents, $batchSize]); + } + + /** + * Update Document + * + * @param string $collection + * @param Document $document + * + * @return Document + */ + public function updateDocument(string $collection, Document $document): Document + { + return $this->query('updateDocument', [$collection, $document]); + } + + /** + * Update Documents in batches + * + * @param string $collection + * @param array $documents + * @param int $batchSize + * + * @return array + * + * @throws DatabaseException + */ + public function updateDocuments(string $collection, array $documents, int $batchSize): array + { + return $this->query('updateDocuments', [$collection, $documents, $batchSize]); + } + + /** + * Delete Document + * + * @param string $collection + * @param string $id + * + * @return bool + */ + public function deleteDocument(string $collection, string $id): bool + { + return $this->query('deleteDocument', [$collection, $id]); + } + + /** + * Find Documents + * + * Find data sets using chosen queries + * + * @param string $collection + * @param array $queries + * @param int|null $limit + * @param int|null $offset + * @param array $orderAttributes + * @param array $orderTypes + * @param array $cursor + * @param string $cursorDirection + * + * @return array + */ + public function find(string $collection, array $queries = [], ?int $limit = 25, ?int $offset = null, array $orderAttributes = [], array $orderTypes = [], array $cursor = [], string $cursorDirection = Database::CURSOR_AFTER): array + { + return $this->query('find', [$collection, $queries, $limit, $offset, $orderAttributes, $orderTypes, $cursor, $cursorDirection]); + } + + /** + * Sum an attribute + * + * @param string $collection + * @param string $attribute + * @param array $queries + * @param int|null $max + * + * @return int|float + */ + public function sum(string $collection, string $attribute, array $queries = [], ?int $max = null): float|int + { + return $this->query('sum', [$collection, $attribute, $queries, $max]); + } + + /** + * Count Documents + * + * @param string $collection + * @param array $queries + * @param int|null $max + * + * @return int + */ + public function count(string $collection, array $queries = [], ?int $max = null): int + { + return $this->query('count', [$collection, $queries, $max]); + } + + /** + * Get Collection Size + * + * @param string $collection + * @return int + * @throws DatabaseException + */ + public function getSizeOfCollection(string $collection): int + { + return $this->query('getSizeOfCollection', [$collection]); + } + + /** + * Get max STRING limit + * + * @return int + */ + public function getLimitForString(): int + { + return $this->query('getLimitForString'); + } + + /** + * Get max INT limit + * + * @return int + */ + public function getLimitForInt(): int + { + return $this->query('getLimitForInt'); + } + + /** + * Get maximum attributes limit. + * + * @return int + */ + public function getLimitForAttributes(): int + { + return $this->query('getLimitForAttributes'); + } + + /** + * Get maximum index limit. + * + * @return int + */ + public function getLimitForIndexes(): int + { + return $this->query('getLimitForIndexes'); + } + + /** + * Is schemas supported? + * + * @return bool + */ + public function getSupportForSchemas(): bool + { + return $this->query('getSupportForSchemas'); + } + + /** + * Is index supported? + * + * @return bool + */ + public function getSupportForIndex(): bool + { + return $this->query('getSupportForIndex'); + } + + /** + * Is unique index supported? + * + * @return bool + */ + public function getSupportForUniqueIndex(): bool + { + return $this->query('getSupportForUniqueIndex'); + } + + /** + * Is fulltext index supported? + * + * @return bool + */ + public function getSupportForFulltextIndex(): bool + { + return $this->query('getSupportForFulltextIndex'); + } + + /** + * Is fulltext wildcard supported? + * + * @return bool + */ + public function getSupportForFulltextWildcardIndex(): bool + { + return $this->query('getSupportForFulltextWildcardIndex'); + } + + + /** + * Does the adapter handle casting? + * + * @return bool + */ + public function getSupportForCasting(): bool + { + return $this->query('getSupportForCasting'); + } + + /** + * Does the adapter handle array Contains? + * + * @return bool + */ + public function getSupportForQueryContains(): bool + { + return $this->query('getSupportForQueryContains'); + } + + /** + * Are timeouts supported? + * + * @return bool + */ + public function getSupportForTimeouts(): bool + { + return $this->query('getSupportForTimeouts'); + } + + /** + * Are relationships supported? + * + * @return bool + */ + public function getSupportForRelationships(): bool + { + return $this->query('getSupportForRelationships'); + } + + /** + * Get current attribute count from collection document + * + * @param Document $collection + * @return int + */ + public function getCountOfAttributes(Document $collection): int + { + return $this->query('getCountOfAttributes', [$collection]); + } + + /** + * Get current index count from collection document + * + * @param Document $collection + * @return int + */ + public function getCountOfIndexes(Document $collection): int + { + return $this->query('getCountOfIndexes', [$collection]); + } + + /** + * Estimate maximum number of bytes required to store a document in $collection. + * Byte requirement varies based on column type and size. + * Needed to satisfy MariaDB/MySQL row width limit. + * Return 0 when no restrictions apply to row width + * + * @param Document $collection + * @return int + */ + public function getAttributeWidth(Document $collection): int + { + return $this->query('getAttributeWidth', [$collection]); + } + + /** + * Get list of keywords that cannot be used + * + * @return array + */ + public function getKeywords(): array + { + return $this->query('getKeywords'); + } + + /** + * Get an attribute projection given a list of selected attributes + * + * @param array $selections + * @param string $prefix + * @return mixed + */ + protected function getAttributeProjection(array $selections, string $prefix = ''): mixed + { + return $this->query('getAttributeProjection', [$selections, $prefix]); + } + + /** + * Increase and Decrease Attribute Value + * + * @param string $collection + * @param string $id + * @param string $attribute + * @param int|float $value + * @param int|float|null $min + * @param int|float|null $max + * @return bool + * @throws Exception + */ + public function increaseDocumentAttribute(string $collection, string $id, string $attribute, int|float $value, int|float|null $min = null, int|float|null $max = null): bool + { + return $this->query('increaseDocumentAttribute', [$collection, $id, $attribute, $value, $min, $max]); + } + + /** + * @return int + */ + public function getMaxIndexLength(): int + { + return $this->query('getMaxIndexLength'); + } + + /** + * Set a global timeout for database queries in milliseconds. + * + * This function allows you to set a maximum execution time for all database + * queries executed using the library, or a specific event specified by the + * event parameter. Once this timeout is set, any database query that takes + * longer than the specified time will be automatically terminated by the library, + * and an appropriate error or exception will be raised to handle the timeout condition. + * + * @param int $milliseconds The timeout value in milliseconds for database queries. + * @param string $event The event the timeout should fire fore + * @return void + * + * @throws Exception The provided timeout value must be greater than or equal to 0. + */ + public function setTimeout(int $milliseconds, string $event = Database::EVENT_ALL): void + { + $this->timeouts[$event] = $milliseconds; + } + + /** + * Clears a global timeout for database queries. + * + * @param string $event + * @return void + */ + public function clearTimeout(string $event): void + { + unset($this->timeouts[$event]); + } +} diff --git a/src/Database/Exception.php b/src/Database/Exception.php index 663abce47..b5fbb8398 100644 --- a/src/Database/Exception.php +++ b/src/Database/Exception.php @@ -4,4 +4,11 @@ class Exception extends \Exception { + public function __construct(string $message = '', int $code = 0, string $file = '', int $line = 0) + { + $this->message = $message; + $this->code = $code; + $this->file = $file; + $this->line = $line; + } } diff --git a/src/Database/Exception/Conflict.php b/src/Database/Exception/Conflict.php index c02dd138a..8803bf902 100644 --- a/src/Database/Exception/Conflict.php +++ b/src/Database/Exception/Conflict.php @@ -2,6 +2,8 @@ namespace Utopia\Database\Exception; -class Conflict extends \Exception +use Utopia\Database\Exception; + +class Conflict extends Exception { } diff --git a/src/Database/Exception/Restricted.php b/src/Database/Exception/Restricted.php index 0905161df..1ef9fefd7 100644 --- a/src/Database/Exception/Restricted.php +++ b/src/Database/Exception/Restricted.php @@ -2,6 +2,8 @@ namespace Utopia\Database\Exception; -class Restricted extends \Exception +use Utopia\Database\Exception; + +class Restricted extends Exception { } diff --git a/src/Database/Exception/Timeout.php b/src/Database/Exception/Timeout.php index 4768e835f..613e74e55 100644 --- a/src/Database/Exception/Timeout.php +++ b/src/Database/Exception/Timeout.php @@ -2,6 +2,8 @@ namespace Utopia\Database\Exception; -class Timeout extends \Exception +use Utopia\Database\Exception; + +class Timeout extends Exception { } diff --git a/tests/e2e/Adapter/Base.php b/tests/e2e/Adapter/Base.php index a488c05bb..0637a80f7 100644 --- a/tests/e2e/Adapter/Base.php +++ b/tests/e2e/Adapter/Base.php @@ -3820,6 +3820,7 @@ public function testCount(): void $count = static::getDatabase()->count('movies'); $this->assertEquals(6, $count); $count = static::getDatabase()->count('movies', [Query::equal('year', [2019])]); + $this->assertEquals(2, $count); $count = static::getDatabase()->count('movies', [Query::equal('with-dash', ['Works'])]); $this->assertEquals(2, $count); @@ -3858,6 +3859,7 @@ public function testCount(): void public function testSum(): void { Authorization::setRole('user:x'); + $sum = static::getDatabase()->sum('movies', 'year', [Query::equal('year', [2019]),]); $this->assertEquals(2019 + 2019, $sum); $sum = static::getDatabase()->sum('movies', 'year'); @@ -4640,7 +4642,6 @@ public function testExceptionDuplicate(Document $document): void { $document->setAttribute('$id', 'duplicated'); static::getDatabase()->createDocument($document->getCollection(), $document); - $this->expectException(DuplicateException::class); static::getDatabase()->createDocument($document->getCollection(), $document); } @@ -6012,7 +6013,7 @@ public function testOneToOneTwoWayRelationship(): void ], ]); - static::getDatabase()->createDocument('country', $doc); + static::getDatabase()->createDocument('country', new Document($doc->getArrayCopy())); $country1 = static::getDatabase()->getDocument('country', 'country1'); $this->assertEquals('London', $country1->getAttribute('city')->getAttribute('name')); @@ -6040,7 +6041,7 @@ public function testOneToOneTwoWayRelationship(): void $this->assertTrue(static::getDatabase()->deleteDocument('country', 'country1')); - static::getDatabase()->createDocument('country', $doc); + static::getDatabase()->createDocument('country', new Document($doc->getArrayCopy())); $country1 = static::getDatabase()->getDocument('country', 'country1'); $this->assertEquals('London', $country1->getAttribute('city')->getAttribute('name')); diff --git a/tests/e2e/Adapter/MariaDBProxyTest.php b/tests/e2e/Adapter/MariaDBProxyTest.php new file mode 100644 index 000000000..046abb954 --- /dev/null +++ b/tests/e2e/Adapter/MariaDBProxyTest.php @@ -0,0 +1,45 @@ +setDatabase('utopiaTests'); + $database->setNamespace(static::$namespace = 'myapp_' . uniqid()); + + if ($database->exists('utopiaTests')) { + $database->delete('utopiaTests'); + } + + $database->create(); + + return self::$database = $database; + } +}