Completed
Push — master ( f37a88...f7f156 )
by Sergei
92:58 queued 31:02
created

ExceptionTest::tearDownForeignKeyConstraintViolationExceptionTest()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 3
dl 0
loc 6
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Doctrine\Tests\DBAL\Functional;
6
7
use Doctrine\DBAL\Driver\ExceptionConverterDriver;
8
use Doctrine\DBAL\Driver\ServerInfoAwareConnection;
9
use Doctrine\DBAL\DriverManager;
10
use Doctrine\DBAL\Exception;
11
use Doctrine\DBAL\Platforms\MySqlPlatform;
12
use Doctrine\DBAL\Platforms\PostgreSqlPlatform;
13
use Doctrine\DBAL\Platforms\SqlitePlatform;
14
use Doctrine\DBAL\Schema\Schema;
15
use Doctrine\DBAL\Schema\Table;
16
use Doctrine\Tests\DbalFunctionalTestCase;
17
use const PHP_OS;
18
use function array_merge;
19
use function assert;
20
use function chmod;
21
use function exec;
22
use function file_exists;
23
use function posix_geteuid;
24
use function posix_getpwuid;
25
use function sprintf;
26
use function sys_get_temp_dir;
27
use function touch;
28
use function unlink;
29
use function version_compare;
30
31
class ExceptionTest extends DbalFunctionalTestCase
32
{
33
    protected function setUp() : void
34
    {
35
        parent::setUp();
36
37
        if ($this->connection->getDriver() instanceof ExceptionConverterDriver) {
38
            return;
39
        }
40
41
        $this->markTestSkipped('Driver does not support special exception handling.');
42
    }
43
44
    public function testPrimaryConstraintViolationException() : void
45
    {
46
        $table = new Table('duplicatekey_table');
47
        $table->addColumn('id', 'integer', []);
48
        $table->setPrimaryKey(['id']);
49
50
        $this->connection->getSchemaManager()->createTable($table);
51
52
        $this->connection->insert('duplicatekey_table', ['id' => 1]);
53
54
        $this->expectException(Exception\UniqueConstraintViolationException::class);
55
        $this->connection->insert('duplicatekey_table', ['id' => 1]);
56
    }
57
58
    public function testTableNotFoundException() : void
59
    {
60
        $sql = 'SELECT * FROM unknown_table';
61
62
        $this->expectException(Exception\TableNotFoundException::class);
63
        $this->connection->executeQuery($sql);
64
    }
65
66
    public function testTableExistsException() : void
67
    {
68
        $schemaManager = $this->connection->getSchemaManager();
69
        $table         = new Table('alreadyexist_table');
70
        $table->addColumn('id', 'integer', []);
71
        $table->setPrimaryKey(['id']);
72
73
        $this->expectException(Exception\TableExistsException::class);
74
        $schemaManager->createTable($table);
75
        $schemaManager->createTable($table);
76
    }
77
78
    public function testNotNullConstraintViolationException() : void
79
    {
80
        $schema = new Schema();
81
82
        $table = $schema->createTable('notnull_table');
83
        $table->addColumn('id', 'integer', []);
84
        $table->addColumn('value', 'integer', ['notnull' => true]);
85
        $table->setPrimaryKey(['id']);
86
87
        foreach ($schema->toSql($this->connection->getDatabasePlatform()) as $sql) {
88
            $this->connection->exec($sql);
89
        }
90
91
        $this->expectException(Exception\NotNullConstraintViolationException::class);
92
        $this->connection->insert('notnull_table', ['id' => 1, 'value' => null]);
93
    }
94
95
    public function testInvalidFieldNameException() : void
96
    {
97
        $schema = new Schema();
98
99
        $table = $schema->createTable('bad_fieldname_table');
100
        $table->addColumn('id', 'integer', []);
101
102
        foreach ($schema->toSql($this->connection->getDatabasePlatform()) as $sql) {
103
            $this->connection->exec($sql);
104
        }
105
106
        $this->expectException(Exception\InvalidFieldNameException::class);
107
        $this->connection->insert('bad_fieldname_table', ['name' => 5]);
108
    }
109
110
    public function testNonUniqueFieldNameException() : void
111
    {
112
        $schema = new Schema();
113
114
        $table = $schema->createTable('ambiguous_list_table');
115
        $table->addColumn('id', 'integer');
116
117
        $table2 = $schema->createTable('ambiguous_list_table_2');
118
        $table2->addColumn('id', 'integer');
119
120
        foreach ($schema->toSql($this->connection->getDatabasePlatform()) as $sql) {
121
            $this->connection->exec($sql);
122
        }
123
124
        $sql = 'SELECT id FROM ambiguous_list_table, ambiguous_list_table_2';
125
        $this->expectException(Exception\NonUniqueFieldNameException::class);
126
        $this->connection->executeQuery($sql);
127
    }
128
129
    public function testUniqueConstraintViolationException() : void
130
    {
131
        $schema = new Schema();
132
133
        $table = $schema->createTable('unique_field_table');
134
        $table->addColumn('id', 'integer');
135
        $table->addUniqueIndex(['id']);
136
137
        foreach ($schema->toSql($this->connection->getDatabasePlatform()) as $sql) {
138
            $this->connection->exec($sql);
139
        }
140
141
        $this->connection->insert('unique_field_table', ['id' => 5]);
142
        $this->expectException(Exception\UniqueConstraintViolationException::class);
143
        $this->connection->insert('unique_field_table', ['id' => 5]);
144
    }
145
146
    public function testSyntaxErrorException() : void
147
    {
148
        $table = new Table('syntax_error_table');
149
        $table->addColumn('id', 'integer', []);
150
        $table->setPrimaryKey(['id']);
151
152
        $this->connection->getSchemaManager()->createTable($table);
153
154
        $sql = 'SELECT id FRO syntax_error_table';
155
        $this->expectException(Exception\SyntaxErrorException::class);
156
        $this->connection->executeQuery($sql);
157
    }
158
159
    public function testConnectionExceptionSqLite() : void
160
    {
161
        if (! ($this->connection->getDatabasePlatform() instanceof SqlitePlatform)) {
162
            $this->markTestSkipped('Only fails this way on sqlite');
163
        }
164
165
        // mode 0 is considered read-only on Windows
166
        $mode = PHP_OS === 'Linux' ? 0444 : 0000;
167
168
        $filename = sprintf('%s/%s', sys_get_temp_dir(), 'doctrine_failed_connection_' . $mode . '.db');
169
170
        if (file_exists($filename)) {
171
            $this->cleanupReadOnlyFile($filename);
172
        }
173
174
        touch($filename);
175
        chmod($filename, $mode);
176
177
        if ($this->isLinuxRoot()) {
178
            exec(sprintf('chattr +i %s', $filename));
179
        }
180
181
        $params = [
182
            'driver' => 'pdo_sqlite',
183
            'path'   => $filename,
184
        ];
185
        $conn   = DriverManager::getConnection($params);
186
187
        $schema = new Schema();
188
        $table  = $schema->createTable('no_connection');
189
        $table->addColumn('id', 'integer');
190
191
        $this->expectException(Exception\ReadOnlyException::class);
192
        $this->expectExceptionMessage(
193
            <<<EOT
194
An exception occurred while executing "CREATE TABLE no_connection (id INTEGER NOT NULL)":
195
196
SQLSTATE[HY000]: General error: 8 attempt to write a readonly database
197
EOT
198
        );
199
200
        try {
201
            foreach ($schema->toSql($conn->getDatabasePlatform()) as $sql) {
202
                $conn->exec($sql);
203
            }
204
        } finally {
205
            $this->cleanupReadOnlyFile($filename);
206
        }
207
    }
208
209
    /**
210
     * @param array<string, mixed> $params
211
     *
212
     * @dataProvider getConnectionParams
213
     */
214
    public function testConnectionException(array $params) : void
215
    {
216
        $platform = $this->connection->getDatabasePlatform();
217
218
        if ($platform instanceof SqlitePlatform) {
219
            $this->markTestSkipped('Only skipped if platform is not sqlite');
220
        }
221
222
        if ($platform instanceof PostgreSqlPlatform && isset($params['password'])) {
223
            $this->markTestSkipped('Does not work on Travis');
224
        }
225
226
        if ($platform instanceof MySqlPlatform && isset($params['user'])) {
227
            $wrappedConnection = $this->connection->getWrappedConnection();
228
            assert($wrappedConnection instanceof ServerInfoAwareConnection);
229
230
            if (version_compare($wrappedConnection->getServerVersion(), '8', '>=')) {
231
                $this->markTestIncomplete('PHP currently does not completely support MySQL 8');
232
            }
233
        }
234
235
        $defaultParams = $this->connection->getParams();
236
        $params        = array_merge($defaultParams, $params);
237
238
        $conn = DriverManager::getConnection($params);
239
240
        $schema = new Schema();
241
        $table  = $schema->createTable('no_connection');
242
        $table->addColumn('id', 'integer');
243
244
        $this->expectException(Exception\ConnectionException::class);
245
246
        foreach ($schema->toSql($conn->getDatabasePlatform()) as $sql) {
247
            $conn->exec($sql);
248
        }
249
    }
250
251
    /**
252
     * @return array<int, array<int, mixed>>
253
     */
254
    public static function getConnectionParams() : iterable
255
    {
256
        return [
257
            [['user' => 'not_existing']],
258
            [['password' => 'really_not']],
259
            [['host' => 'localnope']],
260
        ];
261
    }
262
263
    private function isLinuxRoot() : bool
264
    {
265
        return PHP_OS === 'Linux' && posix_getpwuid(posix_geteuid())['name'] === 'root';
266
    }
267
268
    private function cleanupReadOnlyFile(string $filename) : void
269
    {
270
        if ($this->isLinuxRoot()) {
271
            exec(sprintf('chattr -i %s', $filename));
272
        }
273
274
        chmod($filename, 0200); // make the file writable again, so it can be removed on Windows
275
        unlink($filename);
276
    }
277
}
278