Issues (201)

tests/Driver/AbstractDriverTest.php (2 issues)

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Doctrine\DBAL\Tests\Driver;
6
7
use Doctrine\DBAL\Connection;
8
use Doctrine\DBAL\DBALException;
9
use Doctrine\DBAL\Driver;
10
use Doctrine\DBAL\Driver\DriverException as DriverExceptionInterface;
11
use Doctrine\DBAL\Driver\ExceptionConverterDriver;
12
use Doctrine\DBAL\Exception\ConnectionException;
13
use Doctrine\DBAL\Exception\ConstraintViolationException;
14
use Doctrine\DBAL\Exception\DatabaseObjectExistsException;
15
use Doctrine\DBAL\Exception\DatabaseObjectNotFoundException;
16
use Doctrine\DBAL\Exception\DeadlockException;
17
use Doctrine\DBAL\Exception\DriverException;
18
use Doctrine\DBAL\Exception\ForeignKeyConstraintViolationException;
19
use Doctrine\DBAL\Exception\InvalidFieldNameException;
20
use Doctrine\DBAL\Exception\LockWaitTimeoutException;
21
use Doctrine\DBAL\Exception\NonUniqueFieldNameException;
22
use Doctrine\DBAL\Exception\NotNullConstraintViolationException;
23
use Doctrine\DBAL\Exception\ReadOnlyException;
24
use Doctrine\DBAL\Exception\ServerException;
25
use Doctrine\DBAL\Exception\SyntaxErrorException;
26
use Doctrine\DBAL\Exception\TableExistsException;
27
use Doctrine\DBAL\Exception\TableNotFoundException;
28
use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
29
use Doctrine\DBAL\Platforms\AbstractPlatform;
30
use Doctrine\DBAL\Schema\AbstractSchemaManager;
31
use Doctrine\DBAL\VersionAwarePlatformDriver;
32
use PHPUnit\Framework\MockObject\MockObject;
33
use PHPUnit\Framework\TestCase;
34
use ReflectionProperty;
35
use function array_merge;
36
use function get_class;
37
use function sprintf;
38
39
abstract class AbstractDriverTest extends TestCase
40
{
41
    public const EXCEPTION_CONNECTION                       = ConnectionException::class;
42
    public const EXCEPTION_CONSTRAINT_VIOLATION             = ConstraintViolationException::class;
43
    public const EXCEPTION_DATABASE_OBJECT_EXISTS           = DatabaseObjectExistsException::class;
44
    public const EXCEPTION_DATABASE_OBJECT_NOT_FOUND        = DatabaseObjectNotFoundException::class;
45
    public const EXCEPTION_DRIVER                           = DriverException::class;
46
    public const EXCEPTION_FOREIGN_KEY_CONSTRAINT_VIOLATION = ForeignKeyConstraintViolationException::class;
47
    public const EXCEPTION_INVALID_FIELD_NAME               = InvalidFieldNameException::class;
48
    public const EXCEPTION_NON_UNIQUE_FIELD_NAME            = NonUniqueFieldNameException::class;
49
    public const EXCEPTION_NOT_NULL_CONSTRAINT_VIOLATION    = NotNullConstraintViolationException::class;
50
    public const EXCEPTION_READ_ONLY                        = ReadOnlyException::class;
51
    public const EXCEPTION_SERVER                           = ServerException::class;
52
    public const EXCEPTION_SYNTAX_ERROR                     = SyntaxErrorException::class;
53
    public const EXCEPTION_TABLE_EXISTS                     = TableExistsException::class;
54
    public const EXCEPTION_TABLE_NOT_FOUND                  = TableNotFoundException::class;
55
    public const EXCEPTION_UNIQUE_CONSTRAINT_VIOLATION      = UniqueConstraintViolationException::class;
56
    public const EXCEPTION_DEADLOCK                         = DeadlockException::class;
57
    public const EXCEPTION_LOCK_WAIT_TIMEOUT                = LockWaitTimeoutException::class;
58
59
    /**
60
     * The driver mock under test.
61
     *
62
     * @var Driver
63
     */
64
    protected $driver;
65
66
    protected function setUp() : void
67
    {
68
        parent::setUp();
69
70
        $this->driver = $this->createDriver();
71
    }
72
73
    /**
74
     * @dataProvider exceptionConversionProvider
75
     */
76
    public function testConvertsException(string $expectedClass, int $errorCode, ?string $sqlState = null, string $message = '') : void
77
    {
78
        if (! $this->driver instanceof ExceptionConverterDriver) {
79
            self::markTestSkipped('This test is only intended for exception converter drivers.');
80
        }
81
82
        $driverException = $this->getMockBuilder(DriverExceptionInterface::class)
83
            ->setConstructorArgs([$message, $errorCode])
84
            ->getMock();
85
        $driverException->method('getSQLState')
86
            ->willReturn($sqlState);
87
88
        $dbalMessage   = 'DBAL exception message';
89
        $dbalException = $this->driver->convertException($dbalMessage, $driverException);
90
91
        self::assertInstanceOf($expectedClass, $dbalException);
92
93
        self::assertSame($driverException->getCode(), $dbalException->getCode());
94
        self::assertSame($driverException->getSQLState(), $dbalException->getSQLState());
95
        self::assertSame($driverException, $dbalException->getPrevious());
96
        self::assertSame($dbalMessage, $dbalException->getMessage());
97
    }
98
99
    public function testCreatesDatabasePlatformForVersion() : void
100
    {
101
        if (! $this->driver instanceof VersionAwarePlatformDriver) {
102
            self::markTestSkipped('This test is only intended for version aware platform drivers.');
103
        }
104
105
        $data = $this->getDatabasePlatformsForVersions();
106
107
        self::assertNotEmpty(
108
            $data,
109
            sprintf(
110
                'No test data found for test %s. You have to return test data from %s.',
111
                static::class . '::' . __FUNCTION__,
112
                static::class . '::getDatabasePlatformsForVersions'
113
            )
114
        );
115
116
        foreach ($data as $item) {
117
            $generatedVersion = get_class($this->driver->createDatabasePlatformForVersion($item[0]));
118
119
            self::assertSame(
120
                $item[1],
121
                $generatedVersion,
122
                sprintf(
123
                    'Expected platform for version "%s" should be "%s", "%s" given',
124
                    $item[0],
125
                    $item[1],
126
                    $generatedVersion
127
                )
128
            );
129
        }
130
    }
131
132
    public function testThrowsExceptionOnCreatingDatabasePlatformsForInvalidVersion() : void
133
    {
134
        if (! $this->driver instanceof VersionAwarePlatformDriver) {
135
            self::markTestSkipped('This test is only intended for version aware platform drivers.');
136
        }
137
138
        $this->expectException(DBALException::class);
139
        $this->driver->createDatabasePlatformForVersion('foo');
140
    }
141
142
    public function testReturnsDatabasePlatform() : void
143
    {
144
        self::assertEquals($this->createPlatform(), $this->driver->getDatabasePlatform());
145
    }
146
147
    public function testReturnsSchemaManager() : void
148
    {
149
        $connection    = $this->getConnectionMock();
150
        $schemaManager = $this->driver->getSchemaManager($connection);
151
152
        self::assertEquals($this->createSchemaManager($connection), $schemaManager);
153
154
        $re = new ReflectionProperty($schemaManager, '_conn');
155
        $re->setAccessible(true);
156
157
        self::assertSame($connection, $re->getValue($schemaManager));
158
    }
159
160
    /**
161
     * Factory method for creating the driver instance under test.
162
     */
163
    abstract protected function createDriver() : Driver;
164
165
    /**
166
     * Factory method for creating the the platform instance return by the driver under test.
167
     *
168
     * The platform instance returned by this method must be the same as returned by
169
     * the driver's getDatabasePlatform() method.
170
     */
171
    abstract protected function createPlatform() : AbstractPlatform;
172
173
    /**
174
     * Factory method for creating the the schema manager instance return by the driver under test.
175
     *
176
     * The schema manager instance returned by this method must be the same as returned by
177
     * the driver's getSchemaManager() method.
178
     *
179
     * @param Connection $connection The underlying connection to use.
180
     */
181
    abstract protected function createSchemaManager(Connection $connection) : AbstractSchemaManager;
182
183
    /**
184
     * @return Connection&MockObject
185
     */
186
    protected function getConnectionMock() : Connection
187
    {
188
        return $this->getMockBuilder(Connection::class)
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->getMockBui...onstructor()->getMock() returns the type PHPUnit\Framework\MockObject\MockObject which is incompatible with the type-hinted return Doctrine\DBAL\Connection.
Loading history...
189
            ->disableOriginalConstructor()
190
            ->getMock();
191
    }
192
193
    /**
194
     * @return array<int, array<int, string>>
195
     */
196
    protected function getDatabasePlatformsForVersions() : array
197
    {
198
        return [];
199
    }
200
201
    /**
202
     * @return mixed[][]
203
     */
204
    public static function exceptionConversionProvider() : iterable
205
    {
206
        foreach (static::getExceptionConversionData() as $expectedClass => $items) {
207
            foreach ($items as $item) {
208
                yield array_merge([$expectedClass], $item);
0 ignored issues
show
Bug Best Practice introduced by
The expression yield array_merge(array($expectedClass), $item) returns the type Generator which is incompatible with the documented return type array<mixed,array<mixed,mixed>>.
Loading history...
209
            }
210
        }
211
212
        yield [self::EXCEPTION_DRIVER, 1, 'HY000', 'The message'];
213
    }
214
215
    /**
216
     * @return array<string,mixed[][]>
217
     */
218
    protected static function getExceptionConversionData() : array
219
    {
220
        return [];
221
    }
222
}
223