diff --git a/composer.json b/composer.json index a792038..3650240 100644 --- a/composer.json +++ b/composer.json @@ -17,7 +17,8 @@ "autoload-dev": { "psr-4": { "Test\\Unit\\": "./tests/unit", - "Test\\Integration\\": "./tests/integration" + "Test\\Integration\\": "./tests/integration", + "Test\\Integration\\SQLite\\": "./tests/integration/sqlite" } } } \ No newline at end of file diff --git a/composer.lock b/composer.lock index 50d47e2..3f0b880 100644 --- a/composer.lock +++ b/composer.lock @@ -619,16 +619,16 @@ }, { "name": "phpunit/phpunit", - "version": "11.1.3", + "version": "11.2.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "d475be032238173ca3b0a516f5cc291d174708ae" + "reference": "705eba0190afe04bc057f565ad843267717cf109" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/d475be032238173ca3b0a516f5cc291d174708ae", - "reference": "d475be032238173ca3b0a516f5cc291d174708ae", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/705eba0190afe04bc057f565ad843267717cf109", + "reference": "705eba0190afe04bc057f565ad843267717cf109", "shasum": "" }, "require": { @@ -667,7 +667,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "11.1-dev" + "dev-main": "11.2-dev" } }, "autoload": { @@ -699,7 +699,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/11.1.3" + "source": "https://github.com/sebastianbergmann/phpunit/tree/11.2.0" }, "funding": [ { @@ -715,7 +715,7 @@ "type": "tidelift" } ], - "time": "2024-04-24T06:34:25+00:00" + "time": "2024-06-07T04:48:50+00:00" }, { "name": "sebastian/cli-parser", diff --git a/src/Configuration.php b/src/Configuration.php index b562e7b..c12a6ae 100644 --- a/src/Configuration.php +++ b/src/Configuration.php @@ -58,4 +58,10 @@ class Configuration return self::$_pdo; } + + + public static function resetPDO(): void + { + self::$_pdo = null; + } } diff --git a/src/Custom.php b/src/Custom.php index 5ba6663..740868b 100644 --- a/src/Custom.php +++ b/src/Custom.php @@ -4,6 +4,7 @@ namespace BitBadger\PDODocument; use BitBadger\PDODocument\Mapper\Mapper; use PDO; +use PDOException; use PDOStatement; /** @@ -22,7 +23,13 @@ class Custom public static function &runQuery(string $query, array $parameters): PDOStatement { $debug = defined('PDO_DOC_DEBUG_SQL'); - $stmt = Configuration::dbConn()->prepare($query); + try { + $stmt = Configuration::dbConn()->prepare($query); + } catch (PDOException $ex) { + $keyword = explode(' ', $query, 2)[0]; + throw new DocumentException("Error executing $keyword statement: " . Configuration::dbConn()->errorCode(), + previous: $ex); + } foreach ($parameters as $key => $value) { if ($debug) echo "
Binding $value to $key\n
"; $dataType = match (true) { @@ -115,7 +122,7 @@ class Custom * @return mixed|false|T The scalar value if found, false if not * @throws DocumentException If any is encountered */ - public static function scalar(string $query, array $parameters, Mapper $mapper, ?PDO $pdo = null): mixed + public static function scalar(string $query, array $parameters, Mapper $mapper): mixed { try { $stmt = &self::runQuery($query, $parameters); diff --git a/src/Mapper/StringMapper.php b/src/Mapper/StringMapper.php index 3ed781f..c923fc5 100644 --- a/src/Mapper/StringMapper.php +++ b/src/Mapper/StringMapper.php @@ -3,7 +3,7 @@ namespace BitBadger\PDODocument\Mapper; /** - * Map a string result from the given + * Map a string result from the * * @implements Mapper */ diff --git a/tests/integration/.gitkeep b/tests/integration/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/tests/integration/SubDocument.php b/tests/integration/SubDocument.php new file mode 100644 index 0000000..3c5eb58 --- /dev/null +++ b/tests/integration/SubDocument.php @@ -0,0 +1,11 @@ +dbName = ThrowawayDb::create(); + } + + public function tearDown(): void + { + ThrowawayDb::destroy($this->dbName); + } + + public function testRunQuerySucceedsWithAValidQuery() + { + $stmt = &Custom::runQuery('SELECT data FROM ' . ThrowawayDb::TABLE . ' LIMIT 1', []); + try { + $this->assertNotNull($stmt, 'The statement should not have been null'); + } finally { + $stmt = null; + } + } + + public function testRunQueryFailsWithAnInvalidQuery() + { + $this->expectException(DocumentException::class); + $stmt = &Custom::runQuery('GRAB stuff FROM over_there UNTIL done', []); + try { + $this->assertTrue(false, 'This code should not be reached'); + } finally { + $stmt = null; + } + } + + public function testListSucceedsWhenDataIsFound() + { + $list = Custom::list(Query::selectFromTable(ThrowawayDb::TABLE), [], new DocumentMapper(TestDocument::class)); + $this->assertNotNull($list, 'The document list should not be null'); + $count = 0; + foreach ($list->items() as $ignored) $count++; + $this->assertEquals(5, $count, 'There should have been 5 documents in the list'); + } + + public function testListSucceedsWhenNoDataIsFound() + { + $list = Custom::list(Query::selectFromTable(ThrowawayDb::TABLE) . " WHERE data->>'num_value' > :value", + [':value' => 100], new DocumentMapper(TestDocument::class)); + $this->assertNotNull($list, 'The document list should not be null'); + $count = 0; + foreach ($list->items() as $ignored) $count++; + $this->assertEquals(0, $count, 'There should have been no documents in the list'); + } + + public function testArraySucceedsWhenDataIsFound() + { + $array = Custom::array(Query::selectFromTable(ThrowawayDb::TABLE) . " WHERE data->>'sub' IS NOT NULL", [], + new DocumentMapper(TestDocument::class)); + $this->assertNotNull($array, 'The document array should not be null'); + $this->assertCount(2, $array, 'There should have been 2 documents in the array'); + } + + public function testArraySucceedsWhenNoDataIsFound() + { + $array = Custom::array(Query::selectFromTable(ThrowawayDb::TABLE) . " WHERE data->>'value' = :value", + [':value' => 'not there'], new DocumentMapper(TestDocument::class)); + $this->assertNotNull($array, 'The document array should not be null'); + $this->assertCount(0, $array, 'There should have been no documents in the array'); + } + + public function testSingleSucceedsWhenARowIsFound(): void + { + $doc = Custom::single('SELECT data FROM ' . ThrowawayDb::TABLE . " WHERE data->>'id' = :id", [':id' => 'one'], + new DocumentMapper(TestDocument::class)); + $this->assertNotNull($doc, 'There should have been a document returned'); + $this->assertEquals('one', $doc->id, 'The incorrect document was returned'); + } + + public function testSingleSucceedsWhenARowIsNotFound(): void + { + $doc = Custom::single('SELECT data FROM ' . ThrowawayDb::TABLE . " WHERE data->>'id' = :id", + [':id' => 'eighty'], new DocumentMapper(TestDocument::class)); + $this->assertFalse($doc, 'There should not have been a document returned'); + } + + public function testNonQuerySucceedsWhenOperatingOnData() + { + Custom::nonQuery('DELETE FROM ' . ThrowawayDb::TABLE, []); + try { + $remaining = Count::all(ThrowawayDb::TABLE); + $this->assertEquals(0, $remaining, 'There should be no documents remaining in the table'); + } finally { + $this->dbName = ThrowawayDb::exchange($this->dbName); + } + } + + public function testNonQuerySucceedsWhenNoDataMatchesWhereClause() + { + Custom::nonQuery('DELETE FROM ' . ThrowawayDb::TABLE . " WHERE data->>'num_value' > :value", [':value' => 100]); + $remaining = Count::all(ThrowawayDb::TABLE); + $this->assertEquals(5, $remaining, 'There should be 5 documents remaining in the table'); + } + + public function testScalarSucceeds() + { + $value = Custom::scalar("SELECT 5 AS it", [], new CountMapper()); + $this->assertEquals(5, $value, 'The scalar value was not returned correctly'); + } +} diff --git a/tests/integration/sqlite/ThrowawayDb.php b/tests/integration/sqlite/ThrowawayDb.php new file mode 100644 index 0000000..5a3ab64 --- /dev/null +++ b/tests/integration/sqlite/ThrowawayDb.php @@ -0,0 +1,71 @@ +