3 declare(strict_types
=1);
5 namespace PhpMyAdmin\Tests
;
9 use PhpMyAdmin\ConfigStorage\Relation
;
10 use PhpMyAdmin\Current
;
11 use PhpMyAdmin\Dbal\ConnectionType
;
12 use PhpMyAdmin\Dbal\DatabaseInterface
;
13 use PhpMyAdmin\Message
;
14 use PhpMyAdmin\Normalization
;
15 use PhpMyAdmin\Template
;
16 use PhpMyAdmin\Tests\Stubs\DbiDummy
;
17 use PhpMyAdmin\Tests\Stubs\DummyResult
;
18 use PhpMyAdmin\Transformations
;
20 use PhpMyAdmin\UserPrivileges
;
21 use PHPUnit\Framework\Attributes\CoversClass
;
25 use function _pgettext
;
26 use function json_encode
;
28 #[CoversClass(Normalization::class)]
29 class NormalizationTest
extends AbstractTestCase
31 protected DatabaseInterface
$dbi;
33 protected DbiDummy
$dummyDbi;
35 private Normalization
$normalization;
38 * prepares environment for tests
40 protected function setUp(): void
44 $this->dummyDbi
= $this->createDbiDummy();
45 $this->dbi
= $this->createDatabaseInterface($this->dummyDbi
);
46 DatabaseInterface
::$instance = $this->dbi
;
47 $config = Config
::getInstance();
48 $config->settings
['LimitChars'] = 50;
49 $config->settings
['ServerDefault'] = 'PMA_server';
50 $config->settings
['ShowHint'] = true;
51 $config->settings
['CharEditing'] = '';
52 $config->settings
['ActionLinksMode'] = 'icons';
53 Current
::$database = 'PMA_db';
54 Current
::$table = 'PMA_table';
55 $config->selectedServer
['DisableIS'] = false;
56 $_POST['change_column'] = null;
61 $dbi = $this->getMockBuilder(DatabaseInterface
::class)
62 ->disableOriginalConstructor()
64 $dbi->types
= new Types($dbi);
65 DatabaseInterface
::$instance = $dbi;
67 $dbi->expects(self
::any())
70 $dbi->expects(self
::any())
71 ->method('getColumns')
73 'id' => new Column('id', 'integer', null, false, '', null, '', '', ''),
74 'col1' => new Column('col1', 'varchar(100)', null, false, '', null, '', '', ''),
75 'col2' => new Column('col2', 'DATETIME', null, false, '', null, '', '', ''),
77 $dbi->expects(self
::any())
78 ->method('getColumnNames')
79 ->willReturn(['id', 'col1', 'col2']);
81 ['PMA_db', 'PMA_table1', ConnectionType
::User
, []],
82 ['PMA_db', 'PMA_table', ConnectionType
::User
, [['Key_name' => 'PRIMARY', 'Column_name' => 'id']]],
87 [['Key_name' => 'PRIMARY','Column_name' => 'id'], ['Key_name' => 'PRIMARY','Column_name' => 'col1']],
90 $dbi->expects(self
::any())
91 ->method('getTableIndexes')
92 ->willReturnMap($map);
93 $dbi->expects(self
::any())
95 ->willReturn(self
::createStub(DummyResult
::class));
96 $dbi->expects(self
::any())
97 ->method('fetchSingleRow')
98 ->willReturn(['`id`_cnt' => 0, '`col1`_cnt' => 0, '`col2`_cnt' => 0]);
100 $this->normalization
= new Normalization($dbi, new Relation($dbi), new Transformations(), new Template());
104 * Test for getHtmlForColumnsList
106 public function testGetHtmlForColumnsList(): void
109 $table = 'PMA_table';
110 self
::assertStringContainsString(
111 '<option value="id">id [ integer ]</option>',
112 $this->normalization
->getHtmlForColumnsList($table, $db),
115 '<input type="checkbox" value="col1">col1 [ varchar(100) ]<br>',
116 $this->normalization
->getHtmlForColumnsList($table, $db, 'String', 'checkbox'),
121 * Test for getHtmlForCreateNewColumn
123 public function testGetHtmlForCreateNewColumn(): void
125 $config = Config
::getInstance();
126 $config->settings
['BrowseMIME'] = true;
127 $config->settings
['MaxRows'] = 25;
128 $userPrivileges = new UserPrivileges();
129 $config->selectedServer
['DisableIS'] = false;
130 DatabaseInterface
::$instance = $this->dbi
;
134 $normalization = new Normalization(
136 new Relation($this->dbi
),
137 new Transformations(),
140 $result = $normalization->getHtmlForCreateNewColumn($userPrivileges, $numFields, $db, $table);
141 self
::assertStringContainsString('<table id="table_columns"', $result);
145 * Test for getHtmlFor1NFStep1
147 public function testGetHtmlFor1NFStep1(): void
150 $table = 'PMA_table';
151 $normalizedTo = '1nf';
152 $result = $this->normalization
->getHtmlFor1NFStep1($db, $table, $normalizedTo);
153 self
::assertStringContainsString('<h3>' . __('First step of normalization (1NF)') . '</h3>', $result);
154 self
::assertStringContainsString('<div class="card" id="mainContent"', $result);
155 self
::assertStringContainsString('<div class="card-header">' . __('Step 1.'), $result);
157 self
::assertStringContainsString('<h4', $result);
159 self
::assertStringContainsString('<p', $result);
161 self
::assertStringContainsString("<select id='selectNonAtomicCol'", $result);
163 self
::assertStringContainsString(
164 $this->normalization
->getHtmlForColumnsList(
167 _pgettext('string types', 'String'),
174 * Test for getHtmlContentsFor1NFStep2
176 public function testGetHtmlContentsFor1NFStep2(): void
179 $table = 'PMA_table1';
180 $result = $this->normalization
->getHtmlContentsFor1NFStep2($db, $table);
181 self
::assertArrayHasKey('legendText', $result);
182 self
::assertArrayHasKey('headText', $result);
183 self
::assertArrayHasKey('subText', $result);
184 self
::assertArrayHasKey('hasPrimaryKey', $result);
185 self
::assertArrayHasKey('extra', $result);
186 self
::assertStringContainsString('<a href="#" id="createPrimaryKey">', $result['subText']);
187 self
::assertStringContainsString('<a href="#" id="addNewPrimary">', $result['extra']);
188 self
::assertSame('0', $result['hasPrimaryKey']);
189 self
::assertStringContainsString(__('Step 1.') . 2, $result['legendText']);
190 $result1 = $this->normalization
->getHtmlContentsFor1NFStep2($db, 'PMA_table');
191 self
::assertSame('1', $result1['hasPrimaryKey']);
195 * Test for getHtmlContentsFor1NFStep4
197 public function testGetHtmlContentsFor1NFStep4(): void
200 $table = 'PMA_table';
201 $result = $this->normalization
->getHtmlContentsFor1NFStep4($db, $table);
202 self
::assertArrayHasKey('legendText', $result);
203 self
::assertArrayHasKey('headText', $result);
204 self
::assertArrayHasKey('subText', $result);
205 self
::assertArrayHasKey('extra', $result);
206 self
::assertStringContainsString(__('Step 1.') . 4, $result['legendText']);
207 self
::assertStringContainsString(
208 $this->normalization
->getHtmlForColumnsList($db, $table, 'all', 'checkbox'),
211 self
::assertStringContainsString(
212 '<input class="btn btn-secondary" type="submit" id="removeRedundant"',
218 * Test for getHtmlContentsFor1NFStep3
220 public function testGetHtmlContentsFor1NFStep3(): void
223 $table = 'PMA_table';
224 $result = $this->normalization
->getHtmlContentsFor1NFStep3($db, $table);
225 self
::assertArrayHasKey('legendText', $result);
226 self
::assertArrayHasKey('headText', $result);
227 self
::assertArrayHasKey('subText', $result);
228 self
::assertArrayHasKey('extra', $result);
229 self
::assertArrayHasKey('primary_key', $result);
230 self
::assertStringContainsString(__('Step 1.') . 3, $result['legendText']);
231 self
::assertStringContainsString(
232 $this->normalization
->getHtmlForColumnsList($db, $table, 'all', 'checkbox'),
235 self
::assertStringContainsString(
236 '<input class="btn btn-secondary" type="submit" id="moveRepeatingGroup"',
239 self
::assertSame(json_encode(['id']), $result['primary_key']);
243 * Test for getHtmlFor2NFstep1
245 public function testGetHtmlFor2NFstep1(): void
248 $table = 'PMA_table';
249 $result = $this->normalization
->getHtmlFor2NFstep1($db, $table);
250 self
::assertArrayHasKey('legendText', $result);
251 self
::assertArrayHasKey('headText', $result);
252 self
::assertArrayHasKey('subText', $result);
253 self
::assertArrayHasKey('extra', $result);
254 self
::assertArrayHasKey('primary_key', $result);
255 self
::assertStringContainsString(__('Step 2.') . 1, $result['legendText']);
256 self
::assertSame('id', $result['primary_key']);
257 $result1 = $this->normalization
->getHtmlFor2NFstep1($db, 'PMA_table2');
258 self
::assertSame('id, col1', $result1['primary_key']);
259 self
::assertStringContainsString('<a href="#" id="showPossiblePd"', $result1['headText']);
260 self
::assertStringContainsString('<input type="checkbox" name="pd" value="id"', $result1['extra']);
264 * Test for getHtmlForNewTables2NF
266 public function testGetHtmlForNewTables2NF(): void
268 $table = 'PMA_table';
269 $partialDependencies = ['col1' => ['col2']];
270 $result = $this->normalization
->getHtmlForNewTables2NF($partialDependencies, $table);
271 self
::assertStringContainsString('<input type="text" name="col1"', $result);
275 * Test for createNewTablesFor2NF
277 public function testCreateNewTablesFor2NF(): void
279 $table = 'PMA_table';
281 $tablesName = new stdClass();
282 $tablesName->id
= 'PMA_table';
283 $tablesName->col1
= 'PMA_table1';
284 $partialDependencies = ['id' => ['col2']];
285 $result = $this->normalization
->createNewTablesFor2NF($partialDependencies, $tablesName, $table, $db);
286 self
::assertArrayHasKey('legendText', $result);
287 self
::assertArrayHasKey('headText', $result);
288 self
::assertArrayHasKey('queryError', $result);
289 $partialDependencies = ['id' => ['col2'], 'col1' => ['col2']];
290 $result1 = $this->normalization
->createNewTablesFor2NF($partialDependencies, $tablesName, $table, $db);
291 self
::assertArrayHasKey('extra', $result1);
292 self
::assertSame(__('End of step'), $result1['legendText']);
293 self
::assertSame('', $result1['extra']);
297 * Test for getHtmlForNewTables3NF
299 public function testGetHtmlForNewTables3NF(): void
301 $tables = ['PMA_table' => ['col1']];
303 $dependencies = new stdClass();
304 $dependencies->col1
= ['col2'];
305 $result = $this->normalization
->getHtmlForNewTables3NF($dependencies, $tables, $db);
307 ['html' => '', 'success' => true, 'newTables' => []],
310 $tables = ['PMA_table' => ['col1', 'PMA_table']];
311 // phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps
312 $dependencies->PMA_table
= ['col4', 'col5'];
313 $result1 = $this->normalization
->getHtmlForNewTables3NF($dependencies, $tables, $db);
314 self
::assertStringContainsString('<input type="text" name="PMA_table"', $result1['html']);
318 'PMA_table' => ['pk' => 'col1', 'nonpk' => 'col2'],
319 'table2' => ['pk' => 'id', 'nonpk' => 'col4, col5'],
322 $result1['newTables'],
327 * Test for createNewTablesFor3NF
329 public function testCreateNewTablesFor3NF(): void
334 'PMA_table' => ['pk' => 'id', 'nonpk' => 'col1, col2'],
335 'table1' => ['pk' => 'col2', 'nonpk' => 'col3, col4'],
338 $result = $this->normalization
->createNewTablesFor3NF($newTables, $db);
339 self
::assertArrayHasKey('legendText', $result);
340 self
::assertArrayHasKey('headText', $result);
341 self
::assertArrayHasKey('queryError', $result);
343 $result1 = $this->normalization
->createNewTablesFor3NF($newTables1, $db);
344 self
::assertArrayHasKey('queryError', $result1);
345 self
::assertSame(__('End of step'), $result1['legendText']);
346 self
::assertFalse($result1['queryError']);
350 * Test for moveRepeatingGroup
352 public function testMoveRepeatingGroup(): void
354 $repeatingColumns = 'col1, col2';
355 $primaryColumns = 'id,col1';
356 $newTable = 'PMA_newTable';
357 $newColumn = 'PMA_newCol';
358 $table = 'PMA_table';
360 $result = $this->normalization
->moveRepeatingGroup(
368 self
::assertArrayHasKey('queryError', $result);
369 self
::assertArrayHasKey('message', $result);
370 self
::assertInstanceOf(Message
::class, $result['message']);
374 * Test for getHtmlFor3NFstep1
376 public function testGetHtmlFor3NFstep1(): void
379 $tables = ['PMA_table'];
380 $result = $this->normalization
->getHtmlFor3NFstep1($db, $tables);
381 self
::assertArrayHasKey('legendText', $result);
382 self
::assertArrayHasKey('headText', $result);
383 self
::assertArrayHasKey('subText', $result);
384 self
::assertArrayHasKey('extra', $result);
385 self
::assertStringContainsString(__('Step 3.') . 1, $result['legendText']);
386 self
::assertStringContainsString('<form', $result['extra']);
387 self
::assertStringContainsString('<input type="checkbox" name="pd" value="col1"', $result['extra']);
388 $result1 = $this->normalization
->getHtmlFor3NFstep1($db, ['PMA_table2']);
389 self
::assertSame('', $result1['subText']);
393 * Test for findPartialDependencies
395 public function testFindPartialDependencies(): void
397 $table = 'PMA_table2';
399 $result = $this->normalization
->findPartialDependencies($table, $db);
400 self
::assertStringContainsString('<div class="dependencies_box"', $result);
401 self
::assertStringContainsString(__('No partial dependencies found!'), $result);
405 * Test for getAllCombinationPartialKeys
407 public function testGetAllCombinationPartialKeys(): void
409 $primaryKey = ['id', 'col1', 'col2'];
410 $result = $this->callFunction(
411 $this->normalization
,
412 Normalization
::class,
413 'getAllCombinationPartialKeys',
418 ['', 'id', 'col1', 'col1,id', 'col2', 'col2,id', 'col2,col1'],