diff --git a/phpunit.xml.dist b/phpunit.xml.dist index c5f21759..cc3153f7 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -18,10 +18,6 @@ ./test/unit - ./test/unit/Adapter/AdapterServiceFactoryTest.php - ./test/unit/Adapter/Driver/Pdo/ConnectionIntegrationTest.php - ./test/unit/Adapter/Driver/Pdo/StatementIntegrationTest.php - ./test/unit/Adapter/AdapterAwareTraitTest.php ./test/integration diff --git a/src/Adapter/Driver/Pdo/Statement.php b/src/Adapter/Driver/Pdo/Statement.php index d2d23222..cb0de27d 100644 --- a/src/Adapter/Driver/Pdo/Statement.php +++ b/src/Adapter/Driver/Pdo/Statement.php @@ -157,8 +157,8 @@ public function execute(null|array|ParameterContainer $parameters = null): ?Resu /** START Standard ParameterContainer Merging Block */ if ($parameters instanceof ParameterContainer) { - $this->parameterContainer = $parameters; - $parameters = null; + $this->parameterContainer = $parameters; + $parameters = null; } if (is_array($parameters)) { diff --git a/test/integration/Adapter/Driver/Pdo/AbstractAdapterTestCase.php b/test/integration/Adapter/Driver/Pdo/AbstractAdapterTestCase.php index 63dbe38f..2b087df3 100644 --- a/test/integration/Adapter/Driver/Pdo/AbstractAdapterTestCase.php +++ b/test/integration/Adapter/Driver/Pdo/AbstractAdapterTestCase.php @@ -9,7 +9,8 @@ use function getmypid; use function shell_exec; -#[CoversMethod(AdapterInterface::class, '__construct()')] +#[CoversMethod(AdapterInterface::class, 'getDriver')] +#[CoversMethod(AdapterInterface::class, 'getPlatform')] abstract class AbstractAdapterTestCase extends TestCase { use AdapterTrait; diff --git a/test/unit/Adapter/Container/AdapterInterfaceDelegatorTest.php b/test/unit/Adapter/Container/AdapterInterfaceDelegatorTest.php index ddb935e4..942fdfbb 100644 --- a/test/unit/Adapter/Container/AdapterInterfaceDelegatorTest.php +++ b/test/unit/Adapter/Container/AdapterInterfaceDelegatorTest.php @@ -217,46 +217,6 @@ public function testDelegatorWithServiceManagerAndCustomAdapterName(): void ); } - public function testSetStateWithDefaultAdapterName(): void - { - $delegator = AdapterInterfaceDelegator::__set_state([]); - - self::assertInstanceOf(AdapterInterfaceDelegator::class, $delegator); - } - - public function testSetStateWithCustomAdapterName(): void - { - $delegator = AdapterInterfaceDelegator::__set_state(['adapterName' => 'custom']); - - self::assertInstanceOf(AdapterInterfaceDelegator::class, $delegator); - } - - public function testInvokeReturnsInstanceWhenAdapterIsNotAdapterInterface(): void - { - $container = $this->createMock(ContainerInterface::class); - $container - ->expects(self::once()) - ->method('has') - ->with(AdapterInterface::class) - ->willReturn(true); - $container - ->expects(self::once()) - ->method('get') - ->with(AdapterInterface::class) - ->willReturn(new stdClass()); - - $callback = static fn(): ConcreteAdapterAwareObject => new ConcreteAdapterAwareObject(); - - $result = (new AdapterInterfaceDelegator())( - $container, - ConcreteAdapterAwareObject::class, - $callback - ); - - self::assertInstanceOf(ConcreteAdapterAwareObject::class, $result); - self::assertNull($result->getAdapter()); - } - public function testDelegatorWithPluginManager(): void { $databaseAdapter = new Adapter( @@ -305,4 +265,44 @@ public function validate(mixed $instance): void ); $this->assertSame($options, $result->getOptions()); } + + public function testSetStateWithDefaultAdapterName(): void + { + $delegator = AdapterInterfaceDelegator::__set_state([]); + + self::assertInstanceOf(AdapterInterfaceDelegator::class, $delegator); + } + + public function testSetStateWithCustomAdapterName(): void + { + $delegator = AdapterInterfaceDelegator::__set_state(['adapterName' => 'custom']); + + self::assertInstanceOf(AdapterInterfaceDelegator::class, $delegator); + } + + public function testInvokeReturnsInstanceWhenAdapterIsNotAdapterInterface(): void + { + $container = $this->createMock(ContainerInterface::class); + $container + ->expects(self::once()) + ->method('has') + ->with(AdapterInterface::class) + ->willReturn(true); + $container + ->expects(self::once()) + ->method('get') + ->with(AdapterInterface::class) + ->willReturn(new stdClass()); + + $callback = static fn(): ConcreteAdapterAwareObject => new ConcreteAdapterAwareObject(); + + $result = (new AdapterInterfaceDelegator())( + $container, + ConcreteAdapterAwareObject::class, + $callback + ); + + self::assertInstanceOf(ConcreteAdapterAwareObject::class, $result); + self::assertNull($result->getAdapter()); + } } diff --git a/test/unit/Adapter/Driver/AbstractConnectionTest.php b/test/unit/Adapter/Driver/AbstractConnectionTest.php index 07ff65bf..af7fa388 100644 --- a/test/unit/Adapter/Driver/AbstractConnectionTest.php +++ b/test/unit/Adapter/Driver/AbstractConnectionTest.php @@ -6,8 +6,7 @@ use PhpDb\Adapter\Driver\AbstractConnection; use PhpDb\Adapter\Profiler\ProfilerInterface; -use PhpDbTest\TestAsset\ConnectionWrapper; -use PhpDbTest\TestAsset\PdoStubDriver; +use PhpDbTest\Adapter\Driver\TestAsset\TestConnection; use PHPUnit\Framework\Attributes\CoversMethod; use PHPUnit\Framework\Attributes\Group; use PHPUnit\Framework\TestCase; @@ -25,7 +24,8 @@ final class AbstractConnectionTest extends TestCase { public function testDisconnectNullsResourceWhenConnected(): void { - $connection = new ConnectionWrapper(new PdoStubDriver()); + $connection = new TestConnection(); + $connection->connect(); self::assertTrue($connection->isConnected()); @@ -36,8 +36,7 @@ public function testDisconnectNullsResourceWhenConnected(): void public function testDisconnectIsNoOpWhenNotConnected(): void { - $connection = new ConnectionWrapper(); - $connection->disconnect(); + $connection = new TestConnection(); $result = $connection->disconnect(); @@ -46,28 +45,35 @@ public function testDisconnectIsNoOpWhenNotConnected(): void public function testGetConnectionParametersReturnsEmptyByDefault(): void { - $connection = new ConnectionWrapper(); + $connection = new TestConnection(); self::assertSame([], $connection->getConnectionParameters()); } - public function testGetDriverNameReturnsDriverAttribute(): void + public function testGetDriverNameReturnsValueWhenSet(): void { - $connection = new ConnectionWrapper(new PdoStubDriver()); + $connection = new TestConnection('sqlite'); self::assertSame('sqlite', $connection->getDriverName()); } + public function testGetDriverNameReturnsNullByDefault(): void + { + $connection = new TestConnection(); + + self::assertNull($connection->getDriverName()); + } + public function testGetProfilerReturnsNullByDefault(): void { - $connection = new ConnectionWrapper(); + $connection = new TestConnection(); self::assertNull($connection->getProfiler()); } public function testSetProfilerStoresAndReturnsProfiler(): void { - $connection = new ConnectionWrapper(); + $connection = new TestConnection(); $profiler = $this->createMock(ProfilerInterface::class); $result = $connection->setProfiler($profiler); @@ -78,16 +84,19 @@ public function testSetProfilerStoresAndReturnsProfiler(): void public function testGetResourceAutoConnectsWhenNotConnected(): void { - $connection = new ConnectionWrapper(); + $connection = new TestConnection(); + + self::assertFalse($connection->isConnected()); $resource = $connection->getResource(); - self::assertNotNull($resource); + self::assertTrue($connection->isConnected()); + self::assertSame('fake-resource', $resource); } public function testSetConnectionParametersStoresAndReturnsConnection(): void { - $connection = new ConnectionWrapper(); + $connection = new TestConnection(); $params = ['host' => 'localhost', 'port' => 3306]; $result = $connection->setConnectionParameters($params); @@ -98,7 +107,7 @@ public function testSetConnectionParametersStoresAndReturnsConnection(): void public function testInTransactionReturnsFalseByDefault(): void { - $connection = new ConnectionWrapper(); + $connection = new TestConnection(); self::assertFalse($connection->inTransaction()); } diff --git a/test/unit/Adapter/Driver/Pdo/ConnectionIntegrationTest.php b/test/unit/Adapter/Driver/Pdo/ConnectionIntegrationTest.php deleted file mode 100644 index aafd52a9..00000000 --- a/test/unit/Adapter/Driver/Pdo/ConnectionIntegrationTest.php +++ /dev/null @@ -1,163 +0,0 @@ - */ - protected array $variables = ['pdodriver' => 'sqlite', 'database' => ':memory:']; - - public function testGetCurrentSchema(): void - { - $connection = new TestConnection($this->variables); - self::assertIsString($connection->getCurrentSchema()); - } - - public function testSetResource(): void - { - $resource = new TestAsset\SqliteMemoryPdo(); - $connection = new TestConnection([]); - self::assertSame($connection, $connection->setResource($resource)); - - $connection->disconnect(); - unset($connection); - unset($resource); - } - - public function testGetResource(): void - { - $connection = new TestConnection($this->variables); - $connection->connect(); - self::assertInstanceOf('PDO', $connection->getResource()); - - $connection->disconnect(); - unset($connection); - } - - public function testConnect(): void - { - $connection = new TestConnection($this->variables); - self::assertSame($connection, $connection->connect()); - self::assertTrue($connection->isConnected()); - - $connection->disconnect(); - unset($connection); - } - - public function testIsConnected(): void - { - $connection = new TestConnection($this->variables); - self::assertFalse($connection->isConnected()); - self::assertSame($connection, $connection->connect()); - self::assertTrue($connection->isConnected()); - - $connection->disconnect(); - unset($connection); - } - - public function testDisconnect(): void - { - $connection = new TestConnection($this->variables); - $connection->connect(); - self::assertTrue($connection->isConnected()); - $connection->disconnect(); - self::assertFalse($connection->isConnected()); - } - - /** - * @todo Implement testBeginTransaction(). - */ - public function testBeginTransaction(): never - { - // Remove the following lines when you implement this test. - $this->markTestIncomplete( - 'This test has not been implemented yet.' - ); - } - - /** - * @todo Implement testCommit(). - */ - public function testCommit(): never - { - // Remove the following lines when you implement this test. - $this->markTestIncomplete( - 'This test has not been implemented yet.' - ); - } - - /** - * @todo Implement testRollback(). - */ - public function testRollback(): never - { - // Remove the following lines when you implement this test. - $this->markTestIncomplete( - 'This test has not been implemented yet.' - ); - } - - public function testExecute(): void - { - $sqlsrv = new TestPdo($this->variables); - $connection = $sqlsrv->getConnection(); - - $result = $connection->execute('SELECT \'foo\''); - self::assertInstanceOf(Result::class, $result); - } - - public function testPrepare(): void - { - $sqlsrv = new TestPdo($this->variables); - /** @var AbstractPdoConnection $connection */ - $connection = $sqlsrv->getConnection(); - - $statement = $connection->prepare('SELECT \'foo\''); - self::assertInstanceOf(Statement::class, $statement); - } - - public function testGetLastGeneratedValue(): never - { - $this->markTestIncomplete('Need to create a temporary sequence.'); - //$connection = new Connection($this->variables); - //$connection->getLastGeneratedValue(); - } - - #[Group('laminas3469')] - public function testConnectReturnsConnectionWhenResourceSet(): void - { - $resource = new TestAsset\SqliteMemoryPdo(); - $connection = new TestConnection([]); - $connection->setResource($resource); - self::assertSame($connection, $connection->connect()); - - $connection->disconnect(); - unset($connection); - unset($resource); - } -} diff --git a/test/unit/Adapter/Driver/Pdo/ConnectionTest.php b/test/unit/Adapter/Driver/Pdo/ConnectionTest.php index 14127966..fef08966 100644 --- a/test/unit/Adapter/Driver/Pdo/ConnectionTest.php +++ b/test/unit/Adapter/Driver/Pdo/ConnectionTest.php @@ -23,7 +23,6 @@ #[CoversMethod(AbstractPdoConnection::class, 'getResource')] #[CoversMethod(AbstractPdoConnection::class, 'getDsn')] -#[CoversMethod(AbstractPdoConnection::class, '__construct')] #[CoversMethod(AbstractPdoConnection::class, 'setDriver')] #[CoversMethod(AbstractPdoConnection::class, 'setConnectionParameters')] #[CoversMethod(AbstractPdoConnection::class, 'isConnected')] diff --git a/test/unit/Adapter/Driver/Pdo/PdoTest.php b/test/unit/Adapter/Driver/Pdo/PdoTest.php index 99b4f2c9..e144ab87 100644 --- a/test/unit/Adapter/Driver/Pdo/PdoTest.php +++ b/test/unit/Adapter/Driver/Pdo/PdoTest.php @@ -24,7 +24,6 @@ use PHPUnit\Framework\TestCase; #[CoversMethod(AbstractPdo::class, 'getResultPrototype')] -#[CoversMethod(AbstractPdo::class, '__construct')] #[CoversMethod(AbstractPdo::class, 'checkEnvironment')] #[CoversMethod(AbstractPdo::class, 'getConnection')] #[CoversMethod(AbstractPdo::class, 'createStatement')] @@ -32,6 +31,7 @@ #[CoversMethod(AbstractPdo::class, 'getLastGeneratedValue')] #[CoversMethod(AbstractPdo::class, 'setProfiler')] #[CoversMethod(AbstractPdo::class, 'getProfiler')] +#[CoversMethod(AbstractPdo::class, 'formatParameterName')] #[Group('unit')] final class PdoTest extends TestCase { diff --git a/test/unit/Adapter/Driver/Pdo/StatementTest.php b/test/unit/Adapter/Driver/Pdo/StatementTest.php index 8f38adcb..9534b1a9 100644 --- a/test/unit/Adapter/Driver/Pdo/StatementTest.php +++ b/test/unit/Adapter/Driver/Pdo/StatementTest.php @@ -488,4 +488,30 @@ public function testExecuteAutoPrepares(): void self::assertInstanceOf(Result::class, $result); self::assertTrue($this->statement->isPrepared()); } + + public function testSecondExecuteSkipsBindingWhenAlreadyBound(): void + { + $pdo = new SqliteMemoryPdo(); + $this->statement->setDriver(new TestPdo(new TestConnection($pdo))); + $this->statement->initialize($pdo); + $this->statement->setSql('SELECT :val'); + $this->statement->setParameterContainer(new ParameterContainer(['val' => 'test'])); + + $result1 = $this->statement->execute(); + self::assertInstanceOf(Result::class, $result1); + + $result2 = $this->statement->execute(); + self::assertInstanceOf(Result::class, $result2); + } + + public function testCloneClonesParameterContainerWhenSet(): void + { + $container = new ParameterContainer(['key' => 'value']); + $statement = new Statement($container); + + $clone = clone $statement; + + self::assertNotSame($container, $clone->getParameterContainer()); + self::assertSame('value', $clone->getParameterContainer()->offsetGet('key')); + } } diff --git a/test/unit/Adapter/Driver/TestAsset/TestConnection.php b/test/unit/Adapter/Driver/TestAsset/TestConnection.php new file mode 100644 index 00000000..117a7673 --- /dev/null +++ b/test/unit/Adapter/Driver/TestAsset/TestConnection.php @@ -0,0 +1,58 @@ +resource = 'fake-resource'; + + return $this; + } + + public function execute(string $sql): ?ResultInterface + { + return null; + } + + public function getCurrentSchema(): string|false + { + return false; + } + + public function getLastGeneratedValue(?string $name = null): string|int|false|null + { + return false; + } + + public function isConnected(): bool + { + return $this->resource !== null; + } + + public function rollback(): ConnectionInterface + { + return $this; + } +} diff --git a/test/unit/Adapter/Platform/Sql92Test.php b/test/unit/Adapter/Platform/Sql92Test.php index f1820b15..aaa4a7e6 100644 --- a/test/unit/Adapter/Platform/Sql92Test.php +++ b/test/unit/Adapter/Platform/Sql92Test.php @@ -9,6 +9,7 @@ use PhpDb\Adapter\Exception\VunerablePlatformQuoteException; use PhpDb\Adapter\Platform\AbstractPlatform; use PhpDb\Adapter\Platform\Sql92; +use PhpDbTest\Adapter\Platform\TestAsset\TestPlatform; use PhpDbTest\TestAsset\TestSql92Platform; use PHPUnit\Framework\Attributes\CoversMethod; use PHPUnit\Framework\Attributes\Group; @@ -177,4 +178,19 @@ public function testQuoteValueEscapesSpecialCharacters(): void self::assertStringStartsWith("'", $quoted); self::assertStringEndsWith("'", $quoted); } + + public function testAbstractPlatformQuoteValueThrowsWithoutDriver(): void + { + $platform = new TestPlatform(); + + $this->expectException(VunerablePlatformQuoteException::class); + $platform->quoteValue('value'); + } + + public function testAbstractPlatformQuoteValueEscapesWithDriver(): void + { + $platform = new TestPlatform($this->createStub(DriverInterface::class)); + + self::assertSame("'test\\'value'", $platform->quoteValue("test'value")); + } } diff --git a/test/unit/Adapter/Platform/TestAsset/TestPlatform.php b/test/unit/Adapter/Platform/TestAsset/TestPlatform.php new file mode 100644 index 00000000..9b87d9d5 --- /dev/null +++ b/test/unit/Adapter/Platform/TestAsset/TestPlatform.php @@ -0,0 +1,31 @@ +profiler = new Profiler(); } - public function testFluentProfilerStartWithStringAndContainer(): void + public function testProfilerStartWithString(): void { $ret = $this->profiler->profilerStart('SELECT * FROM FOO'); self::assertSame($this->profiler, $ret); + } + + public function testProfilerStartWithStatementContainer(): void + { $ret = $this->profiler->profilerStart(new StatementContainer()); self::assertSame($this->profiler, $ret); - - $this->expectException(TypeError::class); - $this->profiler->profilerStart(5); } public function testProfilerFinishThrowsWithoutStart(): void