From 5e00cf15db227b0eb8c54111fce47caa255e669b Mon Sep 17 00:00:00 2001 From: Roman Konz Date: Sun, 3 May 2026 21:06:53 +0200 Subject: [PATCH 1/2] test: add dbsafeString coverage Adds a table-driven test covering scalar/null/bool passthrough, htmlspecialchars/addslashes flag toggles, and the BackedEnum and EscapeableParameterInterface unwrap branches added in #96. Test fixtures live under tests/Fixtures/. Co-Authored-By: Claude Opus 4.7 (1M context) --- tests/ConnectionTest.php | 103 ++++++++++++++++++++++++++++ tests/Fixtures/EscapeableValue.php | 37 ++++++++++ tests/Fixtures/IntBackedEnum.php | 19 +++++ tests/Fixtures/StringBackedEnum.php | 21 ++++++ 4 files changed, 180 insertions(+) create mode 100644 tests/Fixtures/EscapeableValue.php create mode 100644 tests/Fixtures/IntBackedEnum.php create mode 100644 tests/Fixtures/StringBackedEnum.php diff --git a/tests/ConnectionTest.php b/tests/ConnectionTest.php index e4e93493..bd328ea5 100644 --- a/tests/ConnectionTest.php +++ b/tests/ConnectionTest.php @@ -20,6 +20,9 @@ use Artemeon\Database\Exception\QueryException; use Artemeon\Database\Exception\RemoveColumnException; use Artemeon\Database\Schema\DataType; +use Artemeon\Database\Tests\Fixtures\EscapeableValue; +use Artemeon\Database\Tests\Fixtures\IntBackedEnum; +use Artemeon\Database\Tests\Fixtures\StringBackedEnum; use DateInterval; use DateTime; use PHPUnit\Framework\Attributes\CoversClass; @@ -962,4 +965,104 @@ public function testHasTable(): void $this->assertTrue($connection->hasTable($tableName)); $this->assertFalse($connection->hasTable('table_does_not_exist')); } + + #[DataProvider('dbsafeStringProvider')] + public function testDbsafeString( + mixed $input, + mixed $expected, + bool $htmlSpecialChars = true, + bool $addSlashes = true, + ): void { + $this->assertSame( + $expected, + $this->getConnection()->dbsafeString($input, $htmlSpecialChars, $addSlashes), + ); + } + + /** + * @return array + */ + public static function dbsafeStringProvider(): array + { + return [ + 'null' => [ + 'input' => null, + 'expected' => null, + ], + 'int' => [ + 'input' => 42, + 'expected' => 42, + ], + 'float' => [ + 'input' => 1.5, + 'expected' => 1.5, + ], + 'bool true is cast to 1' => [ + 'input' => true, + 'expected' => 1, + ], + 'bool false is cast to 0' => [ + 'input' => false, + 'expected' => 0, + ], + 'plain string passthrough' => [ + 'input' => 'plain', + 'expected' => 'plain', + ], + 'string with html chars is escaped' => [ + 'input' => '', + 'expected' => '<a>', + ], + 'string with quote is escaped' => [ + 'input' => "O'Brien", + 'expected' => "O\\'Brien", + ], + 'htmlSpecialChars=false skips html escape' => [ + 'input' => '', + 'expected' => '', + 'htmlSpecialChars' => false, + ], + 'addSlashes=false skips slash escape' => [ + 'input' => "O'Brien", + 'expected' => "O'Brien", + 'addSlashes' => false, + ], + 'BackedEnum (string)' => [ + 'input' => StringBackedEnum::Foo, + 'expected' => 'foo', + ], + 'BackedEnum (int)' => [ + 'input' => IntBackedEnum::Forty, + 'expected' => 40, + ], + 'BackedEnum (string with html chars is escaped)' => [ + 'input' => StringBackedEnum::WithHtml, + 'expected' => '<a>', + ], + 'BackedEnum (string with quote is escaped)' => [ + 'input' => StringBackedEnum::WithQuote, + 'expected' => "O\\'Brien", + ], + 'EscapeableParameterInterface (string)' => [ + 'input' => new EscapeableValue('hello'), + 'expected' => 'hello', + ], + 'EscapeableParameterInterface (int)' => [ + 'input' => new EscapeableValue(123), + 'expected' => 123, + ], + 'EscapeableParameterInterface (null)' => [ + 'input' => new EscapeableValue(null), + 'expected' => null, + ], + 'EscapeableParameterInterface (string with html chars is escaped)' => [ + 'input' => new EscapeableValue(''), + 'expected' => '<a>', + ], + 'EscapeableParameterInterface (string with quote is escaped)' => [ + 'input' => new EscapeableValue("O'Brien"), + 'expected' => "O\\'Brien", + ], + ]; + } } diff --git a/tests/Fixtures/EscapeableValue.php b/tests/Fixtures/EscapeableValue.php new file mode 100644 index 00000000..bd00ae47 --- /dev/null +++ b/tests/Fixtures/EscapeableValue.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Artemeon\Database\Tests\Fixtures; + +use Artemeon\Database\EscapeableParameterInterface; +use Stringable; + +final class EscapeableValue implements EscapeableParameterInterface +{ + /** + * @param scalar|Stringable|null $value + */ + public function __construct(private readonly mixed $value) + { + } + + public function isEscape(): bool + { + return true; + } + + public function getValue(): mixed + { + return $this->value; + } +} diff --git a/tests/Fixtures/IntBackedEnum.php b/tests/Fixtures/IntBackedEnum.php new file mode 100644 index 00000000..4debf61b --- /dev/null +++ b/tests/Fixtures/IntBackedEnum.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Artemeon\Database\Tests\Fixtures; + +enum IntBackedEnum: int +{ + case Forty = 40; +} diff --git a/tests/Fixtures/StringBackedEnum.php b/tests/Fixtures/StringBackedEnum.php new file mode 100644 index 00000000..ec09c158 --- /dev/null +++ b/tests/Fixtures/StringBackedEnum.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Artemeon\Database\Tests\Fixtures; + +enum StringBackedEnum: string +{ + case Foo = 'foo'; + case WithHtml = ''; + case WithQuote = "O'Brien"; +} From 587fa694aa9d2110bdb4d032030f18d68b83e397 Mon Sep 17 00:00:00 2001 From: Roman Konz Date: Sun, 3 May 2026 21:12:35 +0200 Subject: [PATCH 2/2] test: assert dbsafeString flag toggles after enum/escapeable unwrap Adds four rows verifying that htmlSpecialChars=false / addSlashes=false are still honored when the value arrives via a BackedEnum or EscapeableParameterInterface, without relying on the implementation joining the post-unwrap path with the plain-string path. Co-Authored-By: Claude Opus 4.7 (1M context) --- tests/ConnectionTest.php | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/tests/ConnectionTest.php b/tests/ConnectionTest.php index bd328ea5..cacb938b 100644 --- a/tests/ConnectionTest.php +++ b/tests/ConnectionTest.php @@ -1043,6 +1043,16 @@ public static function dbsafeStringProvider(): array 'input' => StringBackedEnum::WithQuote, 'expected' => "O\\'Brien", ], + 'BackedEnum (htmlSpecialChars=false skips html escape)' => [ + 'input' => StringBackedEnum::WithHtml, + 'expected' => '', + 'htmlSpecialChars' => false, + ], + 'BackedEnum (addSlashes=false skips slash escape)' => [ + 'input' => StringBackedEnum::WithQuote, + 'expected' => "O'Brien", + 'addSlashes' => false, + ], 'EscapeableParameterInterface (string)' => [ 'input' => new EscapeableValue('hello'), 'expected' => 'hello', @@ -1063,6 +1073,16 @@ public static function dbsafeStringProvider(): array 'input' => new EscapeableValue("O'Brien"), 'expected' => "O\\'Brien", ], + 'EscapeableParameterInterface (htmlSpecialChars=false skips html escape)' => [ + 'input' => new EscapeableValue(''), + 'expected' => '', + 'htmlSpecialChars' => false, + ], + 'EscapeableParameterInterface (addSlashes=false skips slash escape)' => [ + 'input' => new EscapeableValue("O'Brien"), + 'expected' => "O'Brien", + 'addSlashes' => false, + ], ]; } }