AbstractDriverTest.php 8.84 KB
Newer Older
1 2 3 4 5
<?php

namespace Doctrine\Tests\DBAL\Driver;

use Doctrine\DBAL\Connection;
6
use Doctrine\DBAL\DBALException;
Sergei Morozov's avatar
Sergei Morozov committed
7
use Doctrine\DBAL\Driver;
Sergei Morozov's avatar
Sergei Morozov committed
8
use Doctrine\DBAL\Driver\DriverException as DriverExceptionInterface;
9
use Doctrine\DBAL\Driver\ExceptionConverterDriver;
Sergei Morozov's avatar
Sergei Morozov committed
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
use Doctrine\DBAL\Exception\ConnectionException;
use Doctrine\DBAL\Exception\ConstraintViolationException;
use Doctrine\DBAL\Exception\DatabaseObjectExistsException;
use Doctrine\DBAL\Exception\DatabaseObjectNotFoundException;
use Doctrine\DBAL\Exception\DeadlockException;
use Doctrine\DBAL\Exception\DriverException;
use Doctrine\DBAL\Exception\ForeignKeyConstraintViolationException;
use Doctrine\DBAL\Exception\InvalidFieldNameException;
use Doctrine\DBAL\Exception\LockWaitTimeoutException;
use Doctrine\DBAL\Exception\NonUniqueFieldNameException;
use Doctrine\DBAL\Exception\NotNullConstraintViolationException;
use Doctrine\DBAL\Exception\ReadOnlyException;
use Doctrine\DBAL\Exception\ServerException;
use Doctrine\DBAL\Exception\SyntaxErrorException;
use Doctrine\DBAL\Exception\TableExistsException;
use Doctrine\DBAL\Exception\TableNotFoundException;
use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
Sergei Morozov's avatar
Sergei Morozov committed
27 28
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Schema\AbstractSchemaManager;
29 30
use Doctrine\DBAL\VersionAwarePlatformDriver;
use Doctrine\Tests\DbalTestCase;
31
use PHPUnit\Framework\MockObject\MockObject;
32
use ReflectionProperty;
33
use function array_merge;
34 35
use function get_class;
use function sprintf;
36 37 38

abstract class AbstractDriverTest extends DbalTestCase
{
Sergei Morozov's avatar
Sergei Morozov committed
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
    public const EXCEPTION_CONNECTION                       = ConnectionException::class;
    public const EXCEPTION_CONSTRAINT_VIOLATION             = ConstraintViolationException::class;
    public const EXCEPTION_DATABASE_OBJECT_EXISTS           = DatabaseObjectExistsException::class;
    public const EXCEPTION_DATABASE_OBJECT_NOT_FOUND        = DatabaseObjectNotFoundException::class;
    public const EXCEPTION_DRIVER                           = DriverException::class;
    public const EXCEPTION_FOREIGN_KEY_CONSTRAINT_VIOLATION = ForeignKeyConstraintViolationException::class;
    public const EXCEPTION_INVALID_FIELD_NAME               = InvalidFieldNameException::class;
    public const EXCEPTION_NON_UNIQUE_FIELD_NAME            = NonUniqueFieldNameException::class;
    public const EXCEPTION_NOT_NULL_CONSTRAINT_VIOLATION    = NotNullConstraintViolationException::class;
    public const EXCEPTION_READ_ONLY                        = ReadOnlyException::class;
    public const EXCEPTION_SERVER                           = ServerException::class;
    public const EXCEPTION_SYNTAX_ERROR                     = SyntaxErrorException::class;
    public const EXCEPTION_TABLE_EXISTS                     = TableExistsException::class;
    public const EXCEPTION_TABLE_NOT_FOUND                  = TableNotFoundException::class;
    public const EXCEPTION_UNIQUE_CONSTRAINT_VIOLATION      = UniqueConstraintViolationException::class;
    public const EXCEPTION_DEADLOCK                         = DeadlockException::class;
    public const EXCEPTION_LOCK_WAIT_TIMEOUT                = LockWaitTimeoutException::class;
56 57 58 59

    /**
     * The driver mock under test.
     *
Sergei Morozov's avatar
Sergei Morozov committed
60
     * @var Driver
61 62 63
     */
    protected $driver;

64
    protected function setUp() : void
65 66 67 68 69 70
    {
        parent::setUp();

        $this->driver = $this->createDriver();
    }

71
    /**
72 73
     * @param int|string $errorCode
     *
74 75
     * @dataProvider exceptionConversionProvider
     */
76
    public function testConvertsException($errorCode, ?string $sqlState, ?string $message, string $expectedClass) : void
77
    {
Sergei Morozov's avatar
Sergei Morozov committed
78
        if (! $this->driver instanceof ExceptionConverterDriver) {
79 80 81
            $this->markTestSkipped('This test is only intended for exception converter drivers.');
        }

82 83 84 85 86 87 88
        $driverException = $this->getMockBuilder(DriverExceptionInterface::class)
            ->setConstructorArgs([$message])
            ->getMock();
        $driverException->method('getErrorCode')
            ->willReturn($errorCode);
        $driverException->method('getSQLState')
            ->willReturn($sqlState);
89

90 91
        $dbalMessage   = 'DBAL exception message';
        $dbalException = $this->driver->convertException($dbalMessage, $driverException);
92

93
        self::assertInstanceOf($expectedClass, $dbalException);
94

95 96 97 98
        self::assertSame($driverException->getErrorCode(), $dbalException->getErrorCode());
        self::assertSame($driverException->getSQLState(), $dbalException->getSQLState());
        self::assertSame($driverException, $dbalException->getPrevious());
        self::assertSame($dbalMessage, $dbalException->getMessage());
99 100
    }

101
    public function testCreatesDatabasePlatformForVersion() : void
102
    {
Sergei Morozov's avatar
Sergei Morozov committed
103
        if (! $this->driver instanceof VersionAwarePlatformDriver) {
104 105 106 107 108
            $this->markTestSkipped('This test is only intended for version aware platform drivers.');
        }

        $data = $this->getDatabasePlatformsForVersions();

109 110 111 112
        self::assertNotEmpty(
            $data,
            sprintf(
                'No test data found for test %s. You have to return test data from %s.',
Sergei Morozov's avatar
Sergei Morozov committed
113 114
                static::class . '::' . __FUNCTION__,
                static::class . '::getDatabasePlatformsForVersions'
115 116
            )
        );
117 118

        foreach ($data as $item) {
119 120 121 122 123
            $generatedVersion = get_class($this->driver->createDatabasePlatformForVersion($item[0]));

            self::assertSame(
                $item[1],
                $generatedVersion,
124
                sprintf(
125
                    'Expected platform for version "%s" should be "%s", "%s" given',
126 127
                    $item[0],
                    $item[1],
128 129 130
                    $generatedVersion
                )
            );
131 132 133
        }
    }

134
    public function testThrowsExceptionOnCreatingDatabasePlatformsForInvalidVersion() : void
135
    {
Sergei Morozov's avatar
Sergei Morozov committed
136
        if (! $this->driver instanceof VersionAwarePlatformDriver) {
137 138 139
            $this->markTestSkipped('This test is only intended for version aware platform drivers.');
        }

140
        $this->expectException(DBALException::class);
141 142 143
        $this->driver->createDatabasePlatformForVersion('foo');
    }

144
    public function testReturnsDatabaseName() : void
145
    {
Sergei Morozov's avatar
Sergei Morozov committed
146
        $params = [
147 148 149
            'user'     => 'foo',
            'password' => 'bar',
            'dbname'   => 'baz',
Sergei Morozov's avatar
Sergei Morozov committed
150
        ];
151 152 153 154 155 156 157

        $connection = $this->getConnectionMock();

        $connection->expects($this->once())
            ->method('getParams')
            ->will($this->returnValue($params));

158
        self::assertSame($params['dbname'], $this->driver->getDatabase($connection));
159 160
    }

161
    public function testReturnsDatabasePlatform() : void
162
    {
163
        self::assertEquals($this->createPlatform(), $this->driver->getDatabasePlatform());
164 165
    }

166
    public function testReturnsSchemaManager() : void
167 168 169 170
    {
        $connection    = $this->getConnectionMock();
        $schemaManager = $this->driver->getSchemaManager($connection);

171
        self::assertEquals($this->createSchemaManager($connection), $schemaManager);
172 173 174 175 176

        $re = new ReflectionProperty($schemaManager, '_conn');
        $re->setAccessible(true);

        self::assertSame($connection, $re->getValue($schemaManager));
177 178
    }

179 180 181
    /**
     * Factory method for creating the driver instance under test.
     */
182
    abstract protected function createDriver() : Driver;
183

184 185 186 187 188 189
    /**
     * Factory method for creating the the platform instance return by the driver under test.
     *
     * The platform instance returned by this method must be the same as returned by
     * the driver's getDatabasePlatform() method.
     */
190
    abstract protected function createPlatform() : AbstractPlatform;
191

192 193 194 195 196 197 198 199
    /**
     * Factory method for creating the the schema manager instance return by the driver under test.
     *
     * The schema manager instance returned by this method must be the same as returned by
     * the driver's getSchemaManager() method.
     *
     * @param Connection $connection The underlying connection to use.
     */
200
    abstract protected function createSchemaManager(Connection $connection) : AbstractSchemaManager;
201

Grégoire Paris's avatar
Grégoire Paris committed
202 203 204
    /**
     * @return Connection&MockObject
     */
205
    protected function getConnectionMock() : Connection
206
    {
Grégoire Paris's avatar
Grégoire Paris committed
207
        return $this->createMock(Connection::class);
208 209
    }

210 211 212 213
    /**
     * @return array<int, array<int, string>>
     */
    protected function getDatabasePlatformsForVersions() : array
214
    {
Sergei Morozov's avatar
Sergei Morozov committed
215
        return [];
216 217
    }

218
    /**
219
     * @return iterable<mixed[]>
220 221
     */
    public static function exceptionConversionProvider() : iterable
222
    {
223 224 225
        foreach (static::getExceptionConversionData() as $expectedClass => $items) {
            foreach ($items as $item) {
                yield array_merge($item, [$expectedClass]);
226 227 228
            }
        }

229 230 231 232 233 234 235 236 237
        yield ['foo', 'bar', 'baz', self::EXCEPTION_DRIVER];
    }

    /**
     * @return array<string,mixed[][]>
     */
    protected static function getExceptionConversionData() : array
    {
        return [];
238 239
    }
}