diff --git a/.gitignore b/.gitignore index eed7ff6..9b6cffc 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,5 @@ # PHPStorm **/.idea/ + +**/.cache/ diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..3e14266 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,22 @@ +FROM composer:lts as deps +WORKDIR /app +RUN --mount=type=bind,source=./composer.json,target=composer.json \ + --mount=type=bind,source=./composer.lock,target=composer.lock \ + --mount=type=cache,target=/tmp/cache \ + composer install --no-interaction + +FROM php:8-cli as base +WORKDIR /app +RUN apt-get update && \ + apt-get install -yq --no-install-recommends libsqlite3-dev && \ + rm -rf /var/lib/apt/lists/* && \ + docker-php-ext-install pdo pdo_sqlite +COPY ./src /app/src + +FROM base as dev +COPY ./tests /app/tests +RUN mv "$PHP_INI_DIR/php.ini-development" "$PHP_INI_DIR/php.ini" +COPY --from=deps /app/vendor/ /app/vendor + +FROM dev as test +ENTRYPOINT [ "./vendor/bin/phpunit" ] diff --git a/compose.yml b/compose.yml new file mode 100644 index 0000000..97f3091 --- /dev/null +++ b/compose.yml @@ -0,0 +1,5 @@ +services: + database: + build: . + volumes: + - ./:/app diff --git a/composer.json b/composer.json index 85a3f71..e415bf2 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,7 @@ { "name": "provm/database", "type": "library", + "version": "1.2.0", "authors": [ { "name": "Aldarien", @@ -12,8 +13,7 @@ "ext-pdo": "*" }, "require-dev": { - "phpunit/phpunit": "^10.0", - "kint-php/kint": "^5.0" + "phpunit/phpunit": "^10.0" }, "autoload": { "psr-4": { diff --git a/phpunit.xml b/phpunit.xml new file mode 100644 index 0000000..fcc4184 --- /dev/null +++ b/phpunit.xml @@ -0,0 +1,28 @@ + + + + + tests + + + + + + src + + + diff --git a/src/Database/Connection.php b/src/Database/Connection.php index 934eba4..ab67de3 100644 --- a/src/Database/Connection.php +++ b/src/Database/Connection.php @@ -3,6 +3,7 @@ namespace ProVM\Database; use PDO; use ProVM\Concept\Database; +use ProVM\Exception\Database\InvalidQuery; class Connection implements Database\Connection { @@ -50,11 +51,19 @@ class Connection implements Database\Connection public function query(string $query): Database\ResultSet { - return new ResultSet($this->connect()->query($query)); + $statement = $this->connect()->query($query); + if ($statement === false) { + throw new InvalidQuery($query); + } + return new ResultSet($statement); } public function prepare(string $query): Database\ResultSet { - return new ResultSet($this->connect()->prepare($query)); + $statement = $this->connect()->prepare($query); + if ($statement === false) { + throw new InvalidQuery($query); + } + return new ResultSet($statement); } public function execute(string $query, ?array $data = null): Database\ResultSet { @@ -65,4 +74,13 @@ class Connection implements Database\Connection } return $this->query($query); } + + public function fetchOne(string $query, ?array $data = null): array + { + return $this->execute($query, $data)->fetchFirst(); + } + public function fetchMany(string $query, ?array $data = null): array + { + return $this->execute($query, $data)->fetchAll(); + } } diff --git a/src/Exception/Database/InvalidQuery.php b/src/Exception/Database/InvalidQuery.php new file mode 100644 index 0000000..4f9b48c --- /dev/null +++ b/src/Exception/Database/InvalidQuery.php @@ -0,0 +1,14 @@ +pdo = new PDO('sqlite::memory:'); + $query = "CREATE TABLE IF NOT EXISTS test_table (id INTEGER PRIMARY KEY, test TEXT)"; + $this->pdo->query($query); + } + protected function tearDown(): void + { + unset($this->pdo); + } + + public function testConnection() + { + $host = "memory"; + + $database = $this->getMockBuilder(Concept\Database::class)->getMock(); + $database->method('getHost')->willReturn($host); + $database->method('getDsn')->willReturn("sqlite::{$host}"); + $database->method('needsUser')->willReturn(false); + + $connection = new Connection($database); + $this->assertEquals($this->pdo, $connection->connect()); + } + public function testQuery() + { + $host = "memory"; + + $database = $this->getMockBuilder(Concept\Database::class)->getMock(); + $database->method('getHost')->willReturn($host); + $database->method('getDsn')->willReturn("sqlite::{$host}"); + $database->method('needsUser')->willReturn(false); + + $connection = new Connection($database); + $query = "CREATE TABLE IF NOT EXISTS test_table (id INTEGER PRIMARY KEY, test TEXT)"; + $connection->query($query); + $query = "SELECT * FROM test_table"; + $result = $connection->query($query); + $this->assertInstanceOf(Concept\Database\ResultSet::class, $result); + } + public function testPrepare() + { + $host = "memory"; + + $database = $this->getMockBuilder(Concept\Database::class)->getMock(); + $database->method('getHost')->willReturn($host); + $database->method('getDsn')->willReturn("sqlite::{$host}"); + $database->method('needsUser')->willReturn(false); + + $connection = new Connection($database); + $query = "CREATE TABLE IF NOT EXISTS test_table (id INTEGER PRIMARY KEY, test TEXT)"; + $connection->query($query); + $query = "SELECT * FROM test_table"; + $result = $connection->prepare($query); + $this->assertInstanceOf(Concept\Database\ResultSet::class, $result); + } +} diff --git a/tests/MySQLTest.php b/tests/MySQLTest.php new file mode 100644 index 0000000..c02ede0 --- /dev/null +++ b/tests/MySQLTest.php @@ -0,0 +1,32 @@ +setHost($host); + $database->setPort($port); + $database->setName($name); + $database->setUser($user); + $database->setPassword($pass); + + $this->assertEquals($host, $database->getHost()); + $this->assertEquals($port, $database->getPort()); + $this->assertEquals($name, $database->getName()); + $this->assertEquals($user, $database->getUser()); + $this->assertEquals($pass, $database->getPassword()); + $this->assertTrue($database->needsUser()); + $this->assertEquals($dsn, $database->getDsn()); + } +} diff --git a/tests/PostgreSQLTest.php b/tests/PostgreSQLTest.php new file mode 100644 index 0000000..993005c --- /dev/null +++ b/tests/PostgreSQLTest.php @@ -0,0 +1,32 @@ +setHost($host); + $database->setPort($port); + $database->setName($name); + $database->setUser($user); + $database->setPassword($pass); + + $this->assertEquals($host, $database->getHost()); + $this->assertEquals($port, $database->getPort()); + $this->assertEquals($name, $database->getName()); + $this->assertEquals($user, $database->getUser()); + $this->assertEquals($pass, $database->getPassword()); + $this->assertFalse($database->needsUser()); + $this->assertEquals($dsn, $database->getDsn()); + } +} diff --git a/tests/ResultSetTest.php b/tests/ResultSetTest.php new file mode 100644 index 0000000..b64b4bf --- /dev/null +++ b/tests/ResultSetTest.php @@ -0,0 +1,26 @@ +getMockBuilder(PDOStatement::class)->getMock(); + $statement->method('execute')->willReturn(true); + $statement->method('fetch')->willReturn($result1); + $statement->method('fetchAll')->willReturn($result2); + $statement->method('rowCount')->willReturn(2); + + $resultSet = new ResultSet($statement); + + $resultSet->execute(['foo' => 'bar']); + $this->assertTrue(true); + $this->assertEquals($result1, $resultSet->fetchFirst()); + $this->assertEquals($result2, $resultSet->fetchAll()); + } +} diff --git a/tests/SQLiteTest.php b/tests/SQLiteTest.php new file mode 100644 index 0000000..b1d5820 --- /dev/null +++ b/tests/SQLiteTest.php @@ -0,0 +1,20 @@ +setHost($host); + + $this->assertEquals($host, $database->getHost()); + $this->assertFalse($database->needsUser()); + $this->assertEquals($dsn, $database->getDsn()); + } +} diff --git a/tests/TransactionTest.php b/tests/TransactionTest.php new file mode 100644 index 0000000..ec2ff0c --- /dev/null +++ b/tests/TransactionTest.php @@ -0,0 +1,19 @@ +createMock(ProVM\Concept\Database\Connection::class); + $transaction = new Transaction($connection); + $transaction->begin(); + $this->assertTrue(true); + $transaction->commit(); + $this->assertTrue(true); + $transaction->begin(); + $transaction->rollback(); + $this->assertTrue(true); + } +}