Passed
Push — master ( 37473d...30ff7d )
by Maurício
11:30 queued 14s
created

testGetHtmlForInsertEditRowBasedOnColumnPrivileges()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 73
Code Lines 49

Duplication

Lines 0
Ratio 0 %

Importance

Changes 7
Bugs 0 Features 0
Metric Value
cc 1
eloc 49
nc 1
nop 0
dl 0
loc 73
rs 9.1127
c 7
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
namespace PhpMyAdmin\Tests;
6
7
use PhpMyAdmin\ColumnFull;
8
use PhpMyAdmin\ConfigStorage\Relation;
9
use PhpMyAdmin\Core;
10
use PhpMyAdmin\DatabaseInterface;
0 ignored issues
show
Bug introduced by
The type PhpMyAdmin\DatabaseInterface was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
11
use PhpMyAdmin\Dbal\Warning;
12
use PhpMyAdmin\EditField;
13
use PhpMyAdmin\FileListing;
14
use PhpMyAdmin\InsertEdit;
15
use PhpMyAdmin\ResponseRenderer;
0 ignored issues
show
Bug introduced by
The type PhpMyAdmin\ResponseRenderer was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
16
use PhpMyAdmin\Table;
17
use PhpMyAdmin\Template;
18
use PhpMyAdmin\Tests\Stubs\DbiDummy;
19
use PhpMyAdmin\Tests\Stubs\DummyResult;
20
use PhpMyAdmin\Transformations;
21
use PhpMyAdmin\Url;
22
use ReflectionProperty;
23
24
use function hash;
25
use function is_object;
26
use function is_scalar;
27
use function is_string;
28
use function mb_substr;
29
use function md5;
30
use function password_verify;
31
use function sprintf;
32
use function strval;
33
34
use const MYSQLI_PRI_KEY_FLAG;
35
use const MYSQLI_TYPE_DECIMAL;
36
use const MYSQLI_TYPE_TIMESTAMP;
37
use const MYSQLI_TYPE_TINY;
38
39
/**
40
 * @covers \PhpMyAdmin\InsertEdit
41
 * @group medium
42
 */
43
class InsertEditTest extends AbstractTestCase
44
{
45
    protected DatabaseInterface $dbi;
46
47
    protected DbiDummy $dummyDbi;
48
49
    private InsertEdit $insertEdit;
50
51
    /**
52
     * Setup for test cases
53
     */
54
    protected function setUp(): void
55
    {
56
        parent::setUp();
57
58
        parent::setLanguage();
59
60
        parent::setGlobalConfig();
61
62
        parent::setTheme();
63
64
        $this->dummyDbi = $this->createDbiDummy();
65
        $this->dbi = $this->createDatabaseInterface($this->dummyDbi);
66
        $GLOBALS['dbi'] = $this->dbi;
67
        $GLOBALS['server'] = 1;
68
        $GLOBALS['cfg']['ServerDefault'] = 1;
69
        $GLOBALS['text_dir'] = 'ltr';
70
        $GLOBALS['db'] = 'db';
71
        $GLOBALS['table'] = 'table';
72
        $GLOBALS['cfg']['LimitChars'] = 50;
73
        $GLOBALS['cfg']['LongtextDoubleTextarea'] = false;
74
        $GLOBALS['cfg']['ShowFieldTypesInDataEditView'] = true;
75
        $GLOBALS['cfg']['ShowFunctionFields'] = true;
76
        $GLOBALS['cfg']['ProtectBinary'] = 'blob';
77
        $GLOBALS['cfg']['MaxSizeForInputField'] = 10;
78
        $GLOBALS['cfg']['MinSizeForInputField'] = 2;
79
        $GLOBALS['cfg']['TextareaRows'] = 5;
80
        $GLOBALS['cfg']['TextareaCols'] = 4;
81
        $GLOBALS['cfg']['CharTextareaRows'] = 5;
82
        $GLOBALS['cfg']['CharTextareaCols'] = 6;
83
        $GLOBALS['cfg']['AllowThirdPartyFraming'] = false;
84
        $GLOBALS['cfg']['SendErrorReports'] = 'ask';
85
        $GLOBALS['cfg']['DefaultTabDatabase'] = 'structure';
86
        $GLOBALS['cfg']['ShowDatabasesNavigationAsTree'] = true;
87
        $GLOBALS['cfg']['DefaultTabTable'] = 'browse';
88
        $GLOBALS['cfg']['NavigationTreeDefaultTabTable'] = 'structure';
89
        $GLOBALS['cfg']['NavigationTreeDefaultTabTable2'] = '';
90
        $GLOBALS['cfg']['Confirm'] = true;
91
        $GLOBALS['cfg']['LoginCookieValidity'] = 1440;
92
        $GLOBALS['cfg']['enable_drag_drop_import'] = true;
93
        $this->insertEdit = new InsertEdit(
94
            $this->dbi,
95
            new Relation($this->dbi),
96
            new Transformations(),
97
            new FileListing(),
98
            new Template(),
99
        );
100
101
        $this->dbi->setVersion([
102
            '@@version' => '10.9.3-MariaDB-1:10.9.3+maria~ubu2204',
103
            '@@version_comment' => 'mariadb.org binary distribution',
104
        ]);
105
    }
106
107
    /**
108
     * Teardown all objects
109
     */
110
    protected function tearDown(): void
111
    {
112
        parent::tearDown();
113
114
        $response = new ReflectionProperty(ResponseRenderer::class, 'instance');
115
        $response->setValue(null);
116
    }
117
118
    /**
119
     * Test for getFormParametersForInsertForm
120
     */
121
    public function testGetFormParametersForInsertForm(): void
122
    {
123
        $whereClause = ['foo' => 'bar ', '1' => ' test'];
124
        $_POST['clause_is_unique'] = false;
125
        $_POST['sql_query'] = 'SELECT a';
126
        $GLOBALS['goto'] = 'index.php';
127
128
        $result = $this->insertEdit->getFormParametersForInsertForm(
129
            'dbname',
130
            'tablename',
131
            [],
132
            $whereClause,
133
            'localhost',
134
        );
135
136
        $this->assertEquals(
137
            [
138
                'db' => 'dbname',
139
                'table' => 'tablename',
140
                'goto' => 'index.php',
141
                'err_url' => 'localhost',
142
                'sql_query' => 'SELECT a',
143
                'where_clause[foo]' => 'bar',
144
                'where_clause[1]' => 'test',
145
                'clause_is_unique' => false,
146
            ],
147
            $result,
148
        );
149
    }
150
151
    /**
152
     * Test for getFormParametersForInsertForm
153
     */
154
    public function testGetFormParametersForInsertFormGet(): void
155
    {
156
        $whereClause = ['foo' => 'bar ', '1' => ' test'];
157
        $_GET['clause_is_unique'] = false;
158
        $_GET['sql_query'] = 'SELECT a';
159
        $_GET['sql_signature'] = Core::signSqlQuery($_GET['sql_query']);
160
        $GLOBALS['goto'] = 'index.php';
161
162
        $result = $this->insertEdit->getFormParametersForInsertForm(
163
            'dbname',
164
            'tablename',
165
            [],
166
            $whereClause,
167
            'localhost',
168
        );
169
170
        $this->assertEquals(
171
            [
172
                'db' => 'dbname',
173
                'table' => 'tablename',
174
                'goto' => 'index.php',
175
                'err_url' => 'localhost',
176
                'sql_query' => 'SELECT a',
177
                'where_clause[foo]' => 'bar',
178
                'where_clause[1]' => 'test',
179
                'clause_is_unique' => false,
180
            ],
181
            $result,
182
        );
183
    }
184
185
    /**
186
     * Test for analyzeWhereClauses
187
     */
188
    public function testAnalyzeWhereClause(): void
189
    {
190
        $clauses = ['a=1', 'b="fo\o"'];
191
192
        $resultStub1 = $this->createMock(DummyResult::class);
193
        $resultStub2 = $this->createMock(DummyResult::class);
194
195
        $dbi = $this->getMockBuilder(DatabaseInterface::class)
196
            ->disableOriginalConstructor()
197
            ->getMock();
198
199
        $dbi->expects($this->exactly(2))
200
            ->method('query')
201
            ->willReturnOnConsecutiveCalls($resultStub1, $resultStub2);
202
203
        $resultStub1->expects($this->once())
204
            ->method('fetchAssoc')
205
            ->will($this->returnValue(['assoc1']));
206
207
        $resultStub2->expects($this->once())
208
            ->method('fetchAssoc')
209
            ->will($this->returnValue(['assoc2']));
210
211
        $dbi->expects($this->exactly(2))
212
            ->method('getFieldsMeta')
213
            ->willReturnOnConsecutiveCalls(
214
                [],
215
                [],
216
            );
217
218
        $GLOBALS['dbi'] = $dbi;
219
        $this->insertEdit = new InsertEdit(
220
            $GLOBALS['dbi'],
221
            new Relation($GLOBALS['dbi']),
222
            new Transformations(),
223
            new FileListing(),
224
            new Template(),
225
        );
226
        $result = $this->callFunction(
227
            $this->insertEdit,
228
            InsertEdit::class,
229
            'analyzeWhereClauses',
230
            [$clauses, 'table', 'db'],
231
        );
232
233
        $this->assertSame(
234
            [['a=1', 'b="fo\\\\o"'], [$resultStub1, $resultStub2], [['assoc1'], ['assoc2']], false],
235
            $result,
236
        );
237
    }
238
239
    /**
240
     * Test for showEmptyResultMessageOrSetUniqueCondition
241
     */
242
    public function testShowEmptyResultMessageOrSetUniqueCondition(): void
243
    {
244
        $meta = FieldHelper::fromArray([
245
            'type' => MYSQLI_TYPE_DECIMAL,
246
            'flags' => MYSQLI_PRI_KEY_FLAG,
247
            'table' => 'table',
248
            'orgname' => 'orgname',
249
        ]);
250
251
        $resultStub = $this->createMock(DummyResult::class);
252
253
        $dbi = $this->getMockBuilder(DatabaseInterface::class)
254
            ->disableOriginalConstructor()
255
            ->getMock();
256
257
        $dbi->expects($this->once())
258
            ->method('getFieldsMeta')
259
            ->with($resultStub)
260
            ->will($this->returnValue([$meta]));
261
262
        $GLOBALS['dbi'] = $dbi;
263
        $this->insertEdit = new InsertEdit(
264
            $GLOBALS['dbi'],
265
            new Relation($GLOBALS['dbi']),
266
            new Transformations(),
267
            new FileListing(),
268
            new Template(),
269
        );
270
271
        $result = $this->callFunction(
272
            $this->insertEdit,
273
            InsertEdit::class,
274
            'showEmptyResultMessageOrSetUniqueCondition',
275
            [['1' => ['1' => 1]], 1, [], 'SELECT', ['1' => $resultStub]],
276
        );
277
278
        $this->assertTrue($result);
279
280
        // case 2
281
        $GLOBALS['cfg']['ShowSQL'] = false;
282
283
        $responseMock = $this->getMockBuilder(ResponseRenderer::class)
284
            ->disableOriginalConstructor()
285
            ->onlyMethods(['addHtml'])
286
            ->getMock();
287
288
        $restoreInstance = ResponseRenderer::getInstance();
289
        $response = new ReflectionProperty(ResponseRenderer::class, 'instance');
290
        $response->setValue($responseMock);
291
292
        $result = $this->callFunction(
293
            $this->insertEdit,
294
            InsertEdit::class,
295
            'showEmptyResultMessageOrSetUniqueCondition',
296
            [[false], 0, ['1'], 'SELECT', ['1' => 'result1']],
297
        );
298
299
        $response->setValue($restoreInstance);
300
301
        $this->assertFalse($result);
302
    }
303
304
    public function testLoadFirstRow(): void
305
    {
306
        $resultStub = $this->createMock(DummyResult::class);
307
308
        $dbi = $this->getMockBuilder(DatabaseInterface::class)
309
            ->disableOriginalConstructor()
310
            ->getMock();
311
312
        $dbi->expects($this->once())
313
            ->method('query')
314
            ->with('SELECT * FROM `db`.`table` LIMIT 1;')
315
            ->will($this->returnValue($resultStub));
316
317
        $GLOBALS['dbi'] = $dbi;
318
        $this->insertEdit = new InsertEdit(
319
            $GLOBALS['dbi'],
320
            new Relation($GLOBALS['dbi']),
321
            new Transformations(),
322
            new FileListing(),
323
            new Template(),
324
        );
325
326
        $result = $this->callFunction(
327
            $this->insertEdit,
328
            InsertEdit::class,
329
            'loadFirstRow',
330
            ['table', 'db'],
331
        );
332
333
        $this->assertEquals($resultStub, $result);
334
    }
335
336
    /** @return list<array{int|string, array<false>}> */
337
    public static function dataProviderConfigValueInsertRows(): array
338
    {
339
        return [[2, [false, false]], ['2', [false, false]], [3, [false, false, false]], ['3', [false, false, false]]];
340
    }
341
342
    /**
343
     * Test for loadFirstRow
344
     *
345
     * @param array<false> $rowsValue
346
     *
347
     * @dataProvider dataProviderConfigValueInsertRows
348
     */
349
    public function testGetInsertRows(string|int $configValue, array $rowsValue): void
350
    {
351
        $GLOBALS['cfg']['InsertRows'] = $configValue;
352
353
        $result = $this->callFunction(
354
            $this->insertEdit,
355
            InsertEdit::class,
356
            'getInsertRows',
357
            [],
358
        );
359
360
        $this->assertEquals($rowsValue, $result);
361
    }
362
363
    /**
364
     * Test for showTypeOrFunction
365
     */
366
    public function testShowTypeOrFunction(): void
367
    {
368
        $GLOBALS['cfg']['ShowFieldTypesInDataEditView'] = true;
369
        $GLOBALS['cfg']['ServerDefault'] = 1;
370
        $urlParams = ['ShowFunctionFields' => 2];
371
372
        $result = $this->insertEdit->showTypeOrFunction('function', $urlParams, false);
373
374
        $this->assertStringContainsString('index.php?route=/table/change', $result);
375
        $this->assertStringContainsString(
376
            'ShowFunctionFields=1&ShowFieldTypesInDataEditView=1&goto=index.php%3Froute%3D%2Fsql',
377
            $result,
378
        );
379
        $this->assertStringContainsString('Function', $result);
380
381
        // case 2
382
        $result = $this->insertEdit->showTypeOrFunction('function', $urlParams, true);
383
384
        $this->assertStringContainsString('index.php?route=/table/change', $result);
385
        $this->assertStringContainsString(
386
            'ShowFunctionFields=0&ShowFieldTypesInDataEditView=1&goto=index.php%3Froute%3D%2Fsql',
387
            $result,
388
        );
389
        $this->assertStringContainsString('Function', $result);
390
391
        // case 3
392
        $result = $this->insertEdit->showTypeOrFunction('type', $urlParams, false);
393
394
        $this->assertStringContainsString('index.php?route=/table/change', $result);
395
        $this->assertStringContainsString(
396
            'ShowFunctionFields=1&ShowFieldTypesInDataEditView=1&goto=index.php%3Froute%3D%2Fsql',
397
            $result,
398
        );
399
        $this->assertStringContainsString('Type', $result);
400
401
        // case 4
402
        $result = $this->insertEdit->showTypeOrFunction('type', $urlParams, true);
403
404
        $this->assertStringContainsString('index.php?route=/table/change', $result);
405
        $this->assertStringContainsString(
406
            'ShowFunctionFields=1&ShowFieldTypesInDataEditView=0&goto=index.php%3Froute%3D%2Fsql',
407
            $result,
408
        );
409
        $this->assertStringContainsString('Type', $result);
410
    }
411
412
    /**
413
     * Test for analyzeTableColumnsArray
414
     */
415
    public function testAnalyzeTableColumnsArray(): void
416
    {
417
        $column = new ColumnFull('1<2', 'float(10, 1)', null, false, '', null, '', '', '');
418
419
        $result = $this->callFunction(
420
            $this->insertEdit,
421
            InsertEdit::class,
422
            'analyzeTableColumnsArray',
423
            [$column, [], -1, false],
424
        );
425
426
        $this->assertEquals($result['Field_md5'], '4342210df36bf2ff2c4e2a997a6d4089');
427
428
        $this->assertEquals($result['True_Type'], 'float');
429
430
        $this->assertEquals($result['len'], 100);
431
432
        $this->assertEquals($result['Field_title'], '1&lt;2');
433
434
        $this->assertEquals($result['is_binary'], false);
435
436
        $this->assertEquals($result['is_blob'], false);
437
438
        $this->assertEquals($result['is_char'], false);
439
440
        $this->assertEquals($result['pma_type'], 'float(10, 1)');
441
442
        $this->assertEquals($result['wrap'], ' text-nowrap');
443
444
        $this->assertEquals($result['Field'], '1<2');
445
    }
446
447
    /**
448
     * Test for getColumnTitle
449
     */
450
    public function testGetColumnTitle(): void
451
    {
452
        $fieldName = 'f1<';
453
454
        $this->assertEquals(
455
            $this->callFunction(
456
                $this->insertEdit,
457
                InsertEdit::class,
458
                'getColumnTitle',
459
                [$fieldName, []],
460
            ),
461
            'f1&lt;',
462
        );
463
464
        $comments = [];
465
        $comments['f1<'] = 'comment>';
466
467
        $result = $this->callFunction(
468
            $this->insertEdit,
469
            InsertEdit::class,
470
            'getColumnTitle',
471
            [$fieldName, $comments],
472
        );
473
474
        $result = $this->parseString($result);
475
476
        $this->assertStringContainsString('title="comment&gt;"', $result);
477
478
        $this->assertStringContainsString('f1&lt;', $result);
479
    }
480
481
    /**
482
     * Test for isColumn
483
     */
484
    public function testIsColumn(): void
485
    {
486
        $types = ['binary', 'varbinary'];
487
488
        $columnType = 'binaryfoo';
489
        $this->assertTrue($this->insertEdit->isColumn($columnType, $types));
490
491
        $columnType = 'Binaryfoo';
492
        $this->assertTrue($this->insertEdit->isColumn($columnType, $types));
493
494
        $columnType = 'varbinaryfoo';
495
        $this->assertTrue($this->insertEdit->isColumn($columnType, $types));
496
497
        $columnType = 'barbinaryfoo';
498
        $this->assertFalse($this->insertEdit->isColumn($columnType, $types));
499
500
        $types = ['char', 'varchar'];
501
502
        $columnType = 'char(10)';
503
        $this->assertTrue($this->insertEdit->isColumn($columnType, $types));
504
505
        $columnType = 'VarChar(20)';
506
        $this->assertTrue($this->insertEdit->isColumn($columnType, $types));
507
508
        $columnType = 'foochar';
509
        $this->assertFalse($this->insertEdit->isColumn($columnType, $types));
510
511
        $types = ['blob', 'tinyblob', 'mediumblob', 'longblob'];
512
513
        $columnType = 'blob';
514
        $this->assertTrue($this->insertEdit->isColumn($columnType, $types));
515
516
        $columnType = 'bloB';
517
        $this->assertTrue($this->insertEdit->isColumn($columnType, $types));
518
519
        $columnType = 'mediumBloB';
520
        $this->assertTrue($this->insertEdit->isColumn($columnType, $types));
521
522
        $columnType = 'tinyblobabc';
523
        $this->assertTrue($this->insertEdit->isColumn($columnType, $types));
524
525
        $columnType = 'longblob';
526
        $this->assertTrue($this->insertEdit->isColumn($columnType, $types));
527
528
        $columnType = 'foolongblobbar';
529
        $this->assertFalse($this->insertEdit->isColumn($columnType, $types));
530
    }
531
532
    /**
533
     * Test for getNullifyCodeForNullColumn
534
     */
535
    public function testGetNullifyCodeForNullColumn(): void
536
    {
537
        $column = $foreignData = [];
538
        $foreigners = ['foreign_keys_data' => []];
539
        $column['Field'] = 'f';
540
        $column['True_Type'] = 'enum';
541
        $column['Type'] = 'ababababababababababa';
542
        $this->assertEquals(
543
            '1',
544
            $this->callFunction(
545
                $this->insertEdit,
546
                InsertEdit::class,
547
                'getNullifyCodeForNullColumn',
548
                [$column, $foreigners, []],
549
            ),
550
        );
551
552
        $column['True_Type'] = 'enum';
553
        $column['Type'] = 'abababababababababab';
554
        $this->assertEquals(
555
            '2',
556
            $this->callFunction(
557
                $this->insertEdit,
558
                InsertEdit::class,
559
                'getNullifyCodeForNullColumn',
560
                [$column, $foreigners, []],
561
            ),
562
        );
563
564
        $column['True_Type'] = 'set';
565
        $this->assertEquals(
566
            '3',
567
            $this->callFunction(
568
                $this->insertEdit,
569
                InsertEdit::class,
570
                'getNullifyCodeForNullColumn',
571
                [$column, $foreigners, []],
572
            ),
573
        );
574
575
        $column['True_Type'] = '';
576
        $foreigners['f'] = ['something'/* What should the mocked value actually be? */];
577
        $foreignData['foreign_link'] = '';
578
        $this->assertEquals(
579
            '4',
580
            $this->callFunction(
581
                $this->insertEdit,
582
                InsertEdit::class,
583
                'getNullifyCodeForNullColumn',
584
                [$column, $foreigners, $foreignData],
585
            ),
586
        );
587
    }
588
589
    /**
590
     * Test for getTextarea
591
     */
592
    public function testGetTextarea(): void
593
    {
594
        $GLOBALS['cfg']['TextareaRows'] = 20;
595
        $GLOBALS['cfg']['TextareaCols'] = 10;
596
        $GLOBALS['cfg']['CharTextareaRows'] = 7;
597
        $GLOBALS['cfg']['CharTextareaCols'] = 1;
598
        $GLOBALS['cfg']['LimitChars'] = 20;
599
600
        $column = [];
601
        $column['is_char'] = true;
602
        $column['Type'] = 'char(10)';
603
        $column['True_Type'] = 'char';
604
        (new ReflectionProperty(InsertEdit::class, 'fieldIndex'))->setValue($this->insertEdit, 2);
605
        $result = $this->callFunction(
606
            $this->insertEdit,
607
            InsertEdit::class,
608
            'getTextarea',
609
            [$column, 'a', 'b', '', 'abc/', 'foobar', 'CHAR'],
610
        );
611
612
        $result = $this->parseString($result);
613
614
        $this->assertStringContainsString(
615
            '<textarea name="fieldsb" class="char charField" '
616
            . 'data-maxlength="10" rows="7" cols="1" dir="abc/" '
617
            . 'id="field_2_3" tabindex="2" data-type="CHAR">',
618
            $result,
619
        );
620
    }
621
622
    /**
623
     * Test for getHtmlInput
624
     */
625
    public function testGetHTMLinput(): void
626
    {
627
        $GLOBALS['cfg']['ShowFunctionFields'] = true;
628
        $column = [];
629
        $column['pma_type'] = 'date';
630
        $column['True_Type'] = 'date';
631
        (new ReflectionProperty(InsertEdit::class, 'fieldIndex'))->setValue($this->insertEdit, 23);
632
        $result = $this->callFunction(
633
            $this->insertEdit,
634
            InsertEdit::class,
635
            'getHtmlInput',
636
            [$column, 'a', 'b', 30, 'c', 'DATE'],
637
        );
638
639
        $this->assertEquals(
640
            '<input type="text" name="fieldsa" value="b" size="30" data-type="DATE"'
641
            . ' class="textfield datefield" onchange="c" tabindex="23" id="field_23_3">',
642
            $result,
643
        );
644
645
        // case 2 datetime
646
        $column['pma_type'] = 'datetime';
647
        $column['True_Type'] = 'datetime';
648
        $result = $this->callFunction(
649
            $this->insertEdit,
650
            InsertEdit::class,
651
            'getHtmlInput',
652
            [$column, 'a', 'b', 30, 'c', 'DATE'],
653
        );
654
        $this->assertEquals(
655
            '<input type="text" name="fieldsa" value="b" size="30" data-type="DATE"'
656
            . ' class="textfield datetimefield" onchange="c" tabindex="23" id="field_23_3">',
657
            $result,
658
        );
659
660
        // case 3 timestamp
661
        $column['pma_type'] = 'timestamp';
662
        $column['True_Type'] = 'timestamp';
663
        $result = $this->callFunction(
664
            $this->insertEdit,
665
            InsertEdit::class,
666
            'getHtmlInput',
667
            [$column, 'a', 'b', 30, 'c', 'DATE'],
668
        );
669
        $this->assertEquals(
670
            '<input type="text" name="fieldsa" value="b" size="30" data-type="DATE"'
671
            . ' class="textfield datetimefield" onchange="c" tabindex="23" id="field_23_3">',
672
            $result,
673
        );
674
675
        // case 4 int
676
        $column['pma_type'] = 'int';
677
        $column['True_Type'] = 'int';
678
        $column['Type'] = 'int(11)';
679
        $result = $this->callFunction(
680
            $this->insertEdit,
681
            InsertEdit::class,
682
            'getHtmlInput',
683
            [$column, 'a', 'b', 11, 'c', 'INT'],
684
        );
685
        $this->assertEquals(
686
            '<input type="text" name="fieldsa" value="b" size="11" min="-2147483648" max="2147483647" data-type="INT"'
687
            . ' class="textfield" onchange="c" tabindex="23" inputmode="numeric" id="field_23_3">',
688
            $result,
689
        );
690
    }
691
692
    /**
693
     * Test for getMaxUploadSize
694
     */
695
    public function testGetMaxUploadSize(): void
696
    {
697
        $GLOBALS['config']->set('max_upload_size', 257);
698
        $pmaType = 'tinyblob';
699
        $result = $this->callFunction(
700
            $this->insertEdit,
701
            InsertEdit::class,
702
            'getMaxUploadSize',
703
            [$pmaType],
704
        );
705
706
        $this->assertEquals("(Max: 256B)\n", $result);
707
708
        // case 2
709
        $GLOBALS['config']->set('max_upload_size', 250);
710
        $pmaType = 'tinyblob';
711
        $result = $this->callFunction(
712
            $this->insertEdit,
713
            InsertEdit::class,
714
            'getMaxUploadSize',
715
            [$pmaType],
716
        );
717
718
        $this->assertEquals("(Max: 250B)\n", $result);
719
    }
720
721
    /**
722
     * Test for getValueColumnForOtherDatatypes
723
     */
724
    public function testGetValueColumnForOtherDatatypes(): void
725
    {
726
        $column = [];
727
        $column['len'] = 20;
728
        $column['is_char'] = true;
729
        $column['Type'] = 'char(25)';
730
        $column['True_Type'] = 'char';
731
        $GLOBALS['cfg']['CharEditing'] = '';
732
        $GLOBALS['cfg']['MaxSizeForInputField'] = 30;
733
        $GLOBALS['cfg']['MinSizeForInputField'] = 10;
734
        $GLOBALS['cfg']['TextareaRows'] = 20;
735
        $GLOBALS['cfg']['TextareaCols'] = 10;
736
        $GLOBALS['cfg']['CharTextareaRows'] = 7;
737
        $GLOBALS['cfg']['CharTextareaCols'] = 1;
738
        $GLOBALS['cfg']['LimitChars'] = 50;
739
        $GLOBALS['cfg']['ShowFunctionFields'] = true;
740
741
        $extractedColumnSpec = [];
742
        $extractedColumnSpec['spec_in_brackets'] = '25';
743
        (new ReflectionProperty(InsertEdit::class, 'fieldIndex'))->setValue($this->insertEdit, 22);
744
        $result = $this->callFunction(
745
            $this->insertEdit,
746
            InsertEdit::class,
747
            'getValueColumnForOtherDatatypes',
748
            [
749
                $column,
750
                'defchar',
751
                'a',
752
                'b',
753
                'c',
754
                '&lt;',
755
                '/',
756
                '&lt;',
757
                "foo\nbar",
758
                $extractedColumnSpec,
759
            ],
760
        );
761
762
        $this->assertEquals(
763
            "a\na\n"
764
            . '<textarea name="fieldsb" class="char charField" '
765
            . 'data-maxlength="25" rows="7" cols="1" dir="/" '
766
            . 'id="field_22_3" onchange="c" tabindex="22" data-type="CHAR">'
767
            . '&lt;</textarea>',
768
            $result,
769
        );
770
771
        // case 2: (else)
772
        $column['is_char'] = false;
773
        $column['Extra'] = 'auto_increment';
774
        $column['pma_type'] = 'timestamp';
775
        $column['True_Type'] = 'timestamp';
776
        $result = $this->callFunction(
777
            $this->insertEdit,
778
            InsertEdit::class,
779
            'getValueColumnForOtherDatatypes',
780
            [
781
                $column,
782
                'defchar',
783
                'a',
784
                'b',
785
                'c',
786
                '&lt;',
787
                '/',
788
                '&lt;',
789
                "foo\nbar",
790
                $extractedColumnSpec,
791
            ],
792
        );
793
794
        $this->assertEquals(
795
            "a\n"
796
            . '<input type="text" name="fieldsb" value="&lt;" size="20" data-type="'
797
            . 'DATE" class="textfield datetimefield" onchange="c" tabindex="22" id="field_22_3"'
798
            . '><input type="hidden" name="auto_incrementb" value="1">'
799
            . '<input type="hidden" name="fields_typeb" value="timestamp">',
800
            $result,
801
        );
802
803
        // case 3: (else -> datetime)
804
        $column['pma_type'] = 'datetime';
805
        $result = $this->callFunction(
806
            $this->insertEdit,
807
            InsertEdit::class,
808
            'getValueColumnForOtherDatatypes',
809
            [
810
                $column,
811
                'defchar',
812
                'a',
813
                'b',
814
                'c',
815
                '&lt;',
816
                '/',
817
                '&lt;',
818
                "foo\nbar",
819
                $extractedColumnSpec,
820
            ],
821
        );
822
823
        $result = $this->parseString($result);
824
825
        $this->assertStringContainsString('<input type="hidden" name="fields_typeb" value="datetime">', $result);
826
827
        // case 4: (else -> date)
828
        $column['pma_type'] = 'date';
829
        $result = $this->callFunction(
830
            $this->insertEdit,
831
            InsertEdit::class,
832
            'getValueColumnForOtherDatatypes',
833
            [
834
                $column,
835
                'defchar',
836
                'a',
837
                'b',
838
                'c',
839
                '&lt;',
840
                '/',
841
                '&lt;',
842
                "foo\nbar",
843
                $extractedColumnSpec,
844
            ],
845
        );
846
847
        $result = $this->parseString($result);
848
849
        $this->assertStringContainsString('<input type="hidden" name="fields_typeb" value="date">', $result);
850
851
        // case 5: (else -> bit)
852
        $column['True_Type'] = 'bit';
853
        $result = $this->callFunction(
854
            $this->insertEdit,
855
            InsertEdit::class,
856
            'getValueColumnForOtherDatatypes',
857
            [
858
                $column,
859
                'defchar',
860
                'a',
861
                'b',
862
                'c',
863
                '&lt;',
864
                '/',
865
                '&lt;',
866
                "foo\nbar",
867
                $extractedColumnSpec,
868
            ],
869
        );
870
871
        $result = $this->parseString($result);
872
873
        $this->assertStringContainsString('<input type="hidden" name="fields_typeb" value="bit">', $result);
874
875
        // case 6: (else -> uuid)
876
        $column['True_Type'] = 'uuid';
877
        $result = $this->callFunction(
878
            $this->insertEdit,
879
            InsertEdit::class,
880
            'getValueColumnForOtherDatatypes',
881
            [
882
                $column,
883
                'defchar',
884
                'a',
885
                'b',
886
                'c',
887
                '&lt;',
888
                '/',
889
                '&lt;',
890
                "foo\nbar",
891
                $extractedColumnSpec,
892
            ],
893
        );
894
895
        $result = $this->parseString($result);
896
897
        $this->assertStringContainsString('<input type="hidden" name="fields_typeb" value="uuid">', $result);
898
    }
899
900
    /**
901
     * Test for getColumnSize
902
     */
903
    public function testGetColumnSize(): void
904
    {
905
        $column = [];
906
        $column['is_char'] = true;
907
        $specInBrackets = '45';
908
        $GLOBALS['cfg']['MinSizeForInputField'] = 30;
909
        $GLOBALS['cfg']['MaxSizeForInputField'] = 40;
910
911
        $this->assertEquals(
912
            40,
913
            $this->callFunction(
914
                $this->insertEdit,
915
                InsertEdit::class,
916
                'getColumnSize',
917
                [$column, $specInBrackets],
918
            ),
919
        );
920
921
        $this->assertEquals('textarea', $GLOBALS['cfg']['CharEditing']);
922
923
        // case 2
924
        $column['is_char'] = false;
925
        $column['len'] = 20;
926
        $this->assertEquals(
927
            30,
928
            $this->callFunction(
929
                $this->insertEdit,
930
                InsertEdit::class,
931
                'getColumnSize',
932
                [$column, $specInBrackets],
933
            ),
934
        );
935
    }
936
937
    /**
938
     * Test for getContinueInsertionForm
939
     */
940
    public function testGetContinueInsertionForm(): void
941
    {
942
        $whereClauseArray = ['a<b'];
943
        $GLOBALS['cfg']['InsertRows'] = 1;
944
        $GLOBALS['cfg']['ServerDefault'] = 1;
945
        $GLOBALS['goto'] = 'index.php';
946
        $_POST['where_clause'] = true;
947
        $_POST['sql_query'] = 'SELECT 1';
948
949
        $result = $this->insertEdit->getContinueInsertionForm('tbl', 'db', $whereClauseArray, 'localhost');
950
951
        $this->assertStringContainsString(
952
            '<form id="continueForm" method="post" action="' . Url::getFromRoute('/table/replace')
953
            . '" name="continueForm">',
954
            $result,
955
        );
956
957
        $this->assertStringContainsString('<input type="hidden" name="db" value="db">', $result);
958
959
        $this->assertStringContainsString('<input type="hidden" name="table" value="tbl">', $result);
960
961
        $this->assertStringContainsString('<input type="hidden" name="goto" value="index.php">', $result);
962
963
        $this->assertStringContainsString('<input type="hidden" name="err_url" value="localhost">', $result);
964
965
        $this->assertStringContainsString('<input type="hidden" name="sql_query" value="SELECT 1">', $result);
966
967
        $this->assertStringContainsString('<input type="hidden" name="where_clause[0]" value="a&lt;b">', $result);
968
    }
969
970
    public function testIsWhereClauseNumeric(): void
971
    {
972
        $this->assertFalse(InsertEdit::isWhereClauseNumeric(null));
973
        $this->assertFalse(InsertEdit::isWhereClauseNumeric(''));
974
        $this->assertFalse(InsertEdit::isWhereClauseNumeric([]));
975
        $this->assertTrue(InsertEdit::isWhereClauseNumeric('`actor`.`actor_id` = 1'));
976
        $this->assertTrue(InsertEdit::isWhereClauseNumeric(['`actor`.`actor_id` = 1']));
977
        $this->assertFalse(InsertEdit::isWhereClauseNumeric('`actor`.`first_name` = \'value\''));
978
        $this->assertFalse(InsertEdit::isWhereClauseNumeric(['`actor`.`first_name` = \'value\'']));
979
    }
980
981
    /**
982
     * Test for getHeadAndFootOfInsertRowTable
983
     */
984
    public function testGetHeadAndFootOfInsertRowTable(): void
985
    {
986
        $GLOBALS['cfg']['ShowFieldTypesInDataEditView'] = true;
987
        $GLOBALS['cfg']['ShowFunctionFields'] = true;
988
        $GLOBALS['cfg']['ServerDefault'] = 1;
989
        $urlParams = ['ShowFunctionFields' => 2];
990
991
        $result = $this->callFunction(
992
            $this->insertEdit,
993
            InsertEdit::class,
994
            'getHeadAndFootOfInsertRowTable',
995
            [$urlParams],
996
        );
997
998
        $result = $this->parseString($result);
999
1000
        $this->assertStringContainsString('index.php?route=/table/change', $result);
1001
1002
        $this->assertStringContainsString('ShowFunctionFields=1&ShowFieldTypesInDataEditView=0', $result);
1003
1004
        $this->assertStringContainsString('ShowFunctionFields=0&ShowFieldTypesInDataEditView=1', $result);
1005
    }
1006
1007
    /**
1008
     * Test for getSpecialCharsAndBackupFieldForExistingRow
1009
     */
1010
    public function testGetSpecialCharsAndBackupFieldForExistingRow(): void
1011
    {
1012
        $column = $currentRow = $extractedColumnSpec = [];
1013
        $column['Field'] = 'f';
1014
        $currentRow['f'] = null;
1015
        $_POST['default_action'] = 'insert';
1016
        $column['Key'] = 'PRI';
1017
        $column['Extra'] = 'fooauto_increment';
1018
1019
        $result = $this->callFunction(
1020
            $this->insertEdit,
1021
            InsertEdit::class,
1022
            'getSpecialCharsAndBackupFieldForExistingRow',
1023
            [$currentRow, $column, [], [], 'a', false],
1024
        );
1025
1026
        $this->assertEquals(
1027
            [true, null, null, null, '<input type="hidden" name="fields_preva" value="">'],
1028
            $result,
1029
        );
1030
1031
        // Case 2 (bit)
1032
        unset($_POST['default_action']);
1033
1034
        $currentRow['f'] = '123';
1035
        $extractedColumnSpec['spec_in_brackets'] = '20';
1036
        $column['True_Type'] = 'bit';
1037
1038
        $result = $this->callFunction(
1039
            $this->insertEdit,
1040
            InsertEdit::class,
1041
            'getSpecialCharsAndBackupFieldForExistingRow',
1042
            [$currentRow, $column, $extractedColumnSpec, [], 'a', false],
1043
        );
1044
1045
        $this->assertEquals(
1046
            [false, '', '00000000000001111011', null, '<input type="hidden" name="fields_preva" value="123">'],
1047
            $result,
1048
        );
1049
1050
        $currentRow['f'] = 'abcd';
1051
        $result = $this->callFunction(
1052
            $this->insertEdit,
1053
            InsertEdit::class,
1054
            'getSpecialCharsAndBackupFieldForExistingRow',
1055
            [$currentRow, $column, $extractedColumnSpec, [], 'a', true],
1056
        );
1057
1058
        $this->assertEquals(
1059
            [false, '', 'abcd', null, '<input type="hidden" name="fields_preva" value="abcd">'],
1060
            $result,
1061
        );
1062
1063
        // Case 3 (bit)
1064
        $dbi = $this->getMockBuilder(DatabaseInterface::class)
1065
            ->disableOriginalConstructor()
1066
            ->getMock();
1067
1068
        $GLOBALS['dbi'] = $dbi;
1069
        $this->insertEdit = new InsertEdit(
1070
            $GLOBALS['dbi'],
1071
            new Relation($GLOBALS['dbi']),
1072
            new Transformations(),
1073
            new FileListing(),
1074
            new Template(),
1075
        );
1076
1077
        $currentRow['f'] = '123';
1078
        $extractedColumnSpec['spec_in_brackets'] = '20';
1079
        $column['True_Type'] = 'int';
1080
1081
        $result = $this->callFunction(
1082
            $this->insertEdit,
1083
            InsertEdit::class,
1084
            'getSpecialCharsAndBackupFieldForExistingRow',
1085
            [$currentRow, $column, $extractedColumnSpec, ['int'], 'a', false],
1086
        );
1087
1088
        $this->assertEquals(
1089
            [false, '', "'',", null, '<input type="hidden" name="fields_preva" value="\'\',">'],
1090
            $result,
1091
        );
1092
1093
        // Case 4 (else)
1094
        $column['is_binary'] = false;
1095
        $column['is_blob'] = true;
1096
        $GLOBALS['cfg']['ProtectBinary'] = false;
1097
        $currentRow['f'] = '11001';
1098
        $extractedColumnSpec['spec_in_brackets'] = '20';
1099
        $column['True_Type'] = 'char';
1100
        $GLOBALS['cfg']['ShowFunctionFields'] = true;
1101
1102
        $result = $this->callFunction(
1103
            $this->insertEdit,
1104
            InsertEdit::class,
1105
            'getSpecialCharsAndBackupFieldForExistingRow',
1106
            [$currentRow, $column, $extractedColumnSpec, ['int'], 'a', false],
1107
        );
1108
1109
        $this->assertEquals(
1110
            [
1111
                false,
1112
                '3131303031',
1113
                '3131303031',
1114
                '3131303031',
1115
                '<input type="hidden" name="fields_preva" value="3131303031">',
1116
            ],
1117
            $result,
1118
        );
1119
1120
        // Case 5
1121
        $currentRow['f'] = "11001\x00";
1122
1123
        $result = $this->callFunction(
1124
            $this->insertEdit,
1125
            InsertEdit::class,
1126
            'getSpecialCharsAndBackupFieldForExistingRow',
1127
            [$currentRow, $column, $extractedColumnSpec, ['int'], 'a', false],
1128
        );
1129
1130
        $this->assertEquals(
1131
            [
1132
                false,
1133
                '313130303100',
1134
                '313130303100',
1135
                '313130303100',
1136
                '<input type="hidden" name="fields_preva" value="313130303100">',
1137
            ],
1138
            $result,
1139
        );
1140
    }
1141
1142
    /**
1143
     * Test for getSpecialCharsForInsertingMode
1144
     *
1145
     * @param array  $column   Column parameters
1146
     * @param string $expected Expected result
1147
     * @psalm-param array<string, string|bool|null> $column
1148
     *
1149
     * @dataProvider providerForTestGetSpecialCharsForInsertingMode
1150
     */
1151
    public function testGetSpecialCharsForInsertingMode(
1152
        array $column,
1153
        string $expected,
1154
    ): void {
1155
        $GLOBALS['cfg']['ProtectBinary'] = false;
1156
        $GLOBALS['cfg']['ShowFunctionFields'] = true;
1157
1158
        /** @var string $result */
1159
        $result = $this->callFunction(
1160
            $this->insertEdit,
1161
            InsertEdit::class,
1162
            'getSpecialCharsForInsertingMode',
1163
            [$column['Default'] ?? null, $column['True_Type']],
1164
        );
1165
1166
        $this->assertEquals($expected, $result);
1167
    }
1168
1169
    /**
1170
     * Data provider for test getSpecialCharsForInsertingMode()
1171
     *
1172
     * @return array<string, array{array<string, string|bool|null>, string}>
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<string, array{arra...ng|bool|null>, string}> at position 6 could not be parsed: Expected ':' at position 6, but found 'array'.
Loading history...
1173
     */
1174
    public static function providerForTestGetSpecialCharsForInsertingMode(): array
1175
    {
1176
        return [
1177
            'bit' => [
1178
                ['True_Type' => 'bit', 'Default' => 'b\'101\'', 'is_binary' => true],
1179
                '101',
1180
            ],
1181
            'char' => [['True_Type' => 'char', 'is_binary' => true], ''],
1182
            'time with CURRENT_TIMESTAMP value' => [
1183
                ['True_Type' => 'time', 'Default' => 'CURRENT_TIMESTAMP'],
1184
                'CURRENT_TIMESTAMP',
1185
            ],
1186
            'time with current_timestamp() value' => [
1187
                ['True_Type' => 'time', 'Default' => 'current_timestamp()'],
1188
                'current_timestamp()',
1189
            ],
1190
            'time with no dot value' => [
1191
                ['True_Type' => 'time', 'Default' => '10'],
1192
                '10.000000',
1193
            ],
1194
            'time with dot value' => [
1195
                ['True_Type' => 'time', 'Default' => '10.08'],
1196
                '10.080000',
1197
            ],
1198
            'any text with escape text default' => [
1199
                ['True_Type' => 'text', 'Default' => '"lorem\"ipsem"'],
1200
                'lorem"ipsem',
1201
            ],
1202
            'varchar with html special chars' => [
1203
                ['True_Type' => 'varchar', 'Default' => 'hello world<br><b>lorem</b> ipsem'],
1204
                'hello world&lt;br&gt;&lt;b&gt;lorem&lt;/b&gt; ipsem',
1205
            ],
1206
        ];
1207
    }
1208
1209
    /**
1210
     * Test for setSessionForEditNext
1211
     */
1212
    public function testSetSessionForEditNext(): void
1213
    {
1214
        $meta = FieldHelper::fromArray([
1215
            'type' => MYSQLI_TYPE_DECIMAL,
1216
            'flags' => MYSQLI_PRI_KEY_FLAG,
1217
            'orgname' => 'orgname',
1218
            'table' => 'table',
1219
            'orgtable' => 'table',
1220
        ]);
1221
1222
        $row = ['1' => 1];
1223
1224
        $resultStub = $this->createMock(DummyResult::class);
1225
1226
        $dbi = $this->getMockBuilder(DatabaseInterface::class)
1227
            ->disableOriginalConstructor()
1228
            ->getMock();
1229
1230
        $dbi->expects($this->once())
1231
            ->method('query')
1232
            ->with('SELECT * FROM `db`.`table` WHERE `a` > 2 LIMIT 1;')
1233
            ->will($this->returnValue($resultStub));
1234
1235
        $resultStub->expects($this->once())
1236
            ->method('fetchRow')
1237
            ->will($this->returnValue($row));
1238
1239
        $dbi->expects($this->once())
1240
            ->method('getFieldsMeta')
1241
            ->with($resultStub)
1242
            ->will($this->returnValue([$meta]));
1243
1244
        $GLOBALS['dbi'] = $dbi;
1245
        $GLOBALS['db'] = 'db';
1246
        $GLOBALS['table'] = 'table';
1247
        $this->insertEdit = new InsertEdit(
1248
            $GLOBALS['dbi'],
1249
            new Relation($GLOBALS['dbi']),
1250
            new Transformations(),
1251
            new FileListing(),
1252
            new Template(),
1253
        );
1254
        $this->insertEdit->setSessionForEditNext('`a` = 2');
1255
1256
        $this->assertEquals('CONCAT(`table`.`orgname`) IS NULL', $_SESSION['edit_next']);
1257
    }
1258
1259
    /**
1260
     * Test for getGotoInclude
1261
     */
1262
    public function testGetGotoInclude(): void
1263
    {
1264
        $GLOBALS['goto'] = '123.php';
1265
        $GLOBALS['table'] = '';
1266
1267
        $this->assertEquals(
1268
            '/database/sql',
1269
            $this->insertEdit->getGotoInclude('index'),
1270
        );
1271
1272
        $GLOBALS['table'] = 'tbl';
1273
        $this->assertEquals(
1274
            '/table/sql',
1275
            $this->insertEdit->getGotoInclude('index'),
1276
        );
1277
1278
        $GLOBALS['goto'] = 'index.php?route=/database/sql';
1279
1280
        $this->assertEquals(
1281
            '/database/sql',
1282
            $this->insertEdit->getGotoInclude('index'),
1283
        );
1284
1285
        $this->assertEquals('', $GLOBALS['table']);
1286
1287
        $_POST['after_insert'] = 'new_insert';
1288
        $this->assertEquals(
1289
            '/table/change',
1290
            $this->insertEdit->getGotoInclude('index'),
1291
        );
1292
    }
1293
1294
    /**
1295
     * Test for getErrorUrl
1296
     */
1297
    public function testGetErrorUrl(): void
1298
    {
1299
        $GLOBALS['cfg']['ServerDefault'] = 1;
1300
        $this->assertEquals(
1301
            'index.php?route=/table/change&lang=en',
1302
            $this->insertEdit->getErrorUrl([]),
1303
        );
1304
1305
        $_POST['err_url'] = 'localhost';
1306
        $this->assertEquals(
1307
            'localhost',
1308
            $this->insertEdit->getErrorUrl([]),
1309
        );
1310
    }
1311
1312
    /**
1313
     * Test for buildSqlQuery
1314
     */
1315
    public function testBuildSqlQuery(): void
1316
    {
1317
        $queryFields = ['a', 'b'];
1318
        $valueSets = ['1', '2'];
1319
1320
        $this->assertEquals(
1321
            'INSERT IGNORE INTO `table` (a, b) VALUES (1), (2)',
1322
            $this->insertEdit->buildInsertSqlQuery('table', true, $queryFields, $valueSets),
1323
        );
1324
1325
        $this->assertEquals(
1326
            'INSERT INTO `table` (a, b) VALUES (1), (2)',
1327
            $this->insertEdit->buildInsertSqlQuery('table', false, $queryFields, $valueSets),
1328
        );
1329
    }
1330
1331
    /**
1332
     * Test for executeSqlQuery
1333
     */
1334
    public function testExecuteSqlQuery(): void
1335
    {
1336
        $query = ['SELECT * FROM `test_db`.`test_table`;', 'SELECT * FROM `test_db`.`test_table_yaml`;'];
1337
        $GLOBALS['cfg']['IgnoreMultiSubmitErrors'] = false;
1338
        $_POST['submit_type'] = '';
1339
1340
        $this->insertEdit = new InsertEdit(
1341
            $GLOBALS['dbi'],
1342
            new Relation($GLOBALS['dbi']),
1343
            new Transformations(),
1344
            new FileListing(),
1345
            new Template(),
1346
        );
1347
        $result = $this->insertEdit->executeSqlQuery($query);
1348
1349
        $this->assertEquals([], $result[3]);
1350
    }
1351
1352
    /**
1353
     * Test for executeSqlQuery
1354
     */
1355
    public function testExecuteSqlQueryWithTryQuery(): void
1356
    {
1357
        $query = ['SELECT * FROM `test_db`.`test_table`;', 'SELECT * FROM `test_db`.`test_table_yaml`;'];
1358
        $GLOBALS['cfg']['IgnoreMultiSubmitErrors'] = true;
1359
        $_POST['submit_type'] = '';
1360
1361
        $this->insertEdit = new InsertEdit(
1362
            $GLOBALS['dbi'],
1363
            new Relation($GLOBALS['dbi']),
1364
            new Transformations(),
1365
            new FileListing(),
1366
            new Template(),
1367
        );
1368
        $result = $this->insertEdit->executeSqlQuery($query);
1369
1370
        $this->assertEquals([], $result[3]);
1371
    }
1372
1373
    /**
1374
     * Test for getWarningMessages
1375
     */
1376
    public function testGetWarningMessages(): void
1377
    {
1378
        $warnings = [
1379
            Warning::fromArray(['Level' => 'Error', 'Code' => '1001', 'Message' => 'Message 1']),
1380
            Warning::fromArray(['Level' => 'Warning', 'Code' => '1002', 'Message' => 'Message 2']),
1381
        ];
1382
1383
        $dbi = $this->getMockBuilder(DatabaseInterface::class)
1384
            ->disableOriginalConstructor()
1385
            ->getMock();
1386
1387
        $dbi->expects($this->once())
1388
            ->method('getWarnings')
1389
            ->will($this->returnValue($warnings));
1390
1391
        $GLOBALS['dbi'] = $dbi;
1392
        $this->insertEdit = new InsertEdit(
1393
            $GLOBALS['dbi'],
1394
            new Relation($GLOBALS['dbi']),
1395
            new Transformations(),
1396
            new FileListing(),
1397
            new Template(),
1398
        );
1399
1400
        $result = (array) $this->callFunction(
1401
            $this->insertEdit,
1402
            InsertEdit::class,
1403
            'getWarningMessages',
1404
            [],
1405
        );
1406
1407
        $this->assertEquals(['Error: #1001 Message 1', 'Warning: #1002 Message 2'], $result);
1408
    }
1409
1410
    /**
1411
     * Test for getDisplayValueForForeignTableColumn
1412
     */
1413
    public function testGetDisplayValueForForeignTableColumn(): void
1414
    {
1415
        $map = [];
1416
        $map['f']['foreign_db'] = 'information_schema';
1417
        $map['f']['foreign_table'] = 'TABLES';
1418
        $map['f']['foreign_field'] = 'f';
1419
1420
        $resultStub = $this->createMock(DummyResult::class);
1421
1422
        $dbi = $this->getMockBuilder(DatabaseInterface::class)
1423
            ->disableOriginalConstructor()
1424
            ->getMock();
1425
1426
        $dbi->expects($this->once())
1427
            ->method('tryQuery')
1428
            ->with('SELECT `TABLE_COMMENT` FROM `information_schema`.`TABLES` WHERE `f`=1')
1429
            ->will($this->returnValue($resultStub));
1430
1431
        $resultStub->expects($this->once())
1432
            ->method('numRows')
1433
            ->will($this->returnValue(2));
1434
1435
        $resultStub->expects($this->once())
1436
            ->method('fetchValue')
1437
            ->with(0)
1438
            ->will($this->returnValue('2'));
1439
1440
        $GLOBALS['dbi'] = $dbi;
1441
        $this->insertEdit = new InsertEdit(
1442
            $GLOBALS['dbi'],
1443
            new Relation($GLOBALS['dbi']),
1444
            new Transformations(),
1445
            new FileListing(),
1446
            new Template(),
1447
        );
1448
1449
        $result = $this->insertEdit->getDisplayValueForForeignTableColumn('=1', $map, 'f');
1450
1451
        $this->assertEquals(2, $result);
1452
    }
1453
1454
    /**
1455
     * Test for getLinkForRelationalDisplayField
1456
     */
1457
    public function testGetLinkForRelationalDisplayField(): void
1458
    {
1459
        $GLOBALS['cfg']['ServerDefault'] = 1;
1460
        $_SESSION['tmpval']['relational_display'] = 'K';
1461
        $map = [];
1462
        $map['f']['foreign_db'] = 'information_schema';
1463
        $map['f']['foreign_table'] = 'TABLES';
1464
        $map['f']['foreign_field'] = 'f';
1465
1466
        $result = $this->insertEdit->getLinkForRelationalDisplayField($map, 'f', '=1', 'a>', 'b<');
1467
1468
        $sqlSignature = Core::signSqlQuery('SELECT * FROM `information_schema`.`TABLES` WHERE `f`=1');
1469
1470
        $this->assertEquals(
1471
            '<a href="index.php?route=/sql&db=information_schema&table=TABLES&pos=0&'
1472
            . 'sql_signature=' . $sqlSignature . '&'
1473
            . 'sql_query=SELECT+%2A+FROM+%60information_schema%60.%60TABLES%60+WHERE'
1474
            . '+%60f%60%3D1&lang=en" title="a&gt;">b&lt;</a>',
1475
            $result,
1476
        );
1477
1478
        $_SESSION['tmpval']['relational_display'] = 'D';
1479
        $result = $this->insertEdit->getLinkForRelationalDisplayField($map, 'f', '=1', 'a>', 'b<');
1480
1481
        $this->assertEquals(
1482
            '<a href="index.php?route=/sql&db=information_schema&table=TABLES&pos=0&'
1483
            . 'sql_signature=' . $sqlSignature . '&'
1484
            . 'sql_query=SELECT+%2A+FROM+%60information_schema%60.%60TABLES%60+WHERE'
1485
            . '+%60f%60%3D1&lang=en" title="b&lt;">a&gt;</a>',
1486
            $result,
1487
        );
1488
    }
1489
1490
    /**
1491
     * Test for transformEditedValues
1492
     */
1493
    public function testTransformEditedValues(): void
1494
    {
1495
        $_SESSION[' HMAC_secret '] = hash('sha1', 'test');
1496
        $editedValues = [['c' => 'cname']];
1497
        $GLOBALS['cfg']['DefaultTransformations']['PreApPend'] = ['', ''];
1498
        $GLOBALS['cfg']['ServerDefault'] = 1;
1499
        $_POST['where_clause'] = '1';
1500
        $_POST['where_clause_sign'] = Core::signSqlQuery($_POST['where_clause']);
1501
        $transformation = ['transformation_options' => "'','option ,, quoted',abd"];
1502
        $result = $this->insertEdit->transformEditedValues(
1503
            'db',
1504
            'table',
1505
            $transformation,
1506
            $editedValues,
1507
            'Text_Plain_PreApPend.php',
1508
            'c',
1509
            ['a' => 'b'],
1510
            'transformation',
1511
        );
1512
1513
        $this->assertEquals(
1514
            ['a' => 'b', 'transformations' => ['cnameoption ,, quoted']],
1515
            $result,
1516
        );
1517
    }
1518
1519
    /**
1520
     * Test for getQueryValuesForInsert
1521
     */
1522
    public function testGetQueryValuesForInsert(): void
1523
    {
1524
        // Simple insert
1525
        $result = $this->insertEdit->getQueryValueForInsert(
1526
            new EditField(
1527
                'fld',
1528
                'foo',
1529
                '',
1530
                false,
1531
                false,
1532
                false,
1533
                '',
1534
                null,
1535
                null,
1536
                false,
1537
            ),
1538
            false,
1539
            '',
1540
        );
1541
        $this->assertEquals("'foo'", $result);
1542
1543
        // Test for file upload
1544
        $result = $this->insertEdit->getQueryValueForInsert(
1545
            new EditField(
1546
                '',
1547
                '0x123',
1548
                '',
1549
                false,
1550
                false,
1551
                false,
1552
                '',
1553
                null,
1554
                null,
1555
                true,
1556
            ),
1557
            false,
1558
            '',
1559
        );
1560
1561
        $this->assertEquals('0x123', $result);
1562
1563
        // Test functions
1564
        $this->dummyDbi->addResult(
1565
            'SELECT UUID()',
1566
            [
1567
                ['uuid1234'],// Minimal working setup for 2FA
1568
            ],
1569
        );
1570
1571
        // case 1
1572
        $result = $this->insertEdit->getQueryValueForInsert(
1573
            new EditField(
1574
                '',
1575
                '',
1576
                '',
1577
                false,
1578
                false,
1579
                false,
1580
                'UUID',
1581
                null,
1582
                null,
1583
                false,
1584
            ),
1585
            false,
1586
            '',
1587
        );
1588
1589
        $this->assertEquals("'uuid1234'", $result);
1590
1591
        // case 2
1592
        $result = $this->insertEdit->getQueryValueForInsert(
1593
            new EditField(
1594
                '',
1595
                "'",
1596
                '',
1597
                false,
1598
                false,
1599
                false,
1600
                'AES_ENCRYPT',
1601
                '',
1602
                null,
1603
                false,
1604
            ),
1605
            false,
1606
            '',
1607
        );
1608
        $this->assertEquals("AES_ENCRYPT('\\'','')", $result);
1609
1610
        // case 3
1611
        $result = $this->insertEdit->getQueryValueForInsert(
1612
            new EditField(
1613
                '',
1614
                "'",
1615
                '',
1616
                false,
1617
                false,
1618
                false,
1619
                'ABS',
1620
                null,
1621
                null,
1622
                false,
1623
            ),
1624
            false,
1625
            '',
1626
        );
1627
        $this->assertEquals("ABS('\\'')", $result);
1628
1629
        // case 4
1630
        $result = $this->insertEdit->getQueryValueForInsert(
1631
            new EditField(
1632
                '',
1633
                '',
1634
                '',
1635
                false,
1636
                false,
1637
                false,
1638
                'RAND',
1639
                null,
1640
                null,
1641
                false,
1642
            ),
1643
            false,
1644
            '',
1645
        );
1646
        $this->assertEquals('RAND()', $result);
1647
1648
        // case 5
1649
        $result = $this->insertEdit->getQueryValueForInsert(
1650
            new EditField(
1651
                '',
1652
                "a'c",
1653
                '',
1654
                false,
1655
                false,
1656
                false,
1657
                'PHP_PASSWORD_HASH',
1658
                null,
1659
                null,
1660
                false,
1661
            ),
1662
            false,
1663
            '',
1664
        );
1665
        $this->assertTrue(password_verify("a'c", mb_substr($result, 1, -1)));
1666
1667
        // case 7
1668
        $result = $this->insertEdit->getQueryValueForInsert(
1669
            new EditField('', "'POINT(3 4)',4326", '', true, false, false, 'ST_GeomFromText', null, null, false),
1670
            false,
1671
            '',
1672
        );
1673
        $this->assertEquals('ST_GeomFromText(\'POINT(3 4)\',4326)', $result);
1674
1675
        // case 8
1676
        $result = $this->insertEdit->getQueryValueForInsert(
1677
            new EditField('', 'POINT(3 4),4326', '', true, false, false, 'ST_GeomFromText', null, null, false),
1678
            false,
1679
            '',
1680
        );
1681
        $this->assertEquals('ST_GeomFromText(\'POINT(3 4)\',4326)', $result);
1682
1683
        // case 9
1684
        $result = $this->insertEdit->getQueryValueForInsert(
1685
            new EditField('', "'POINT(3 4)'", '', true, false, false, 'ST_GeomFromText', null, null, false),
1686
            false,
1687
            '',
1688
        );
1689
        $this->assertEquals('ST_GeomFromText(\'POINT(3 4)\')', $result);
1690
1691
        // case 10
1692
        $result = $this->insertEdit->getQueryValueForInsert(
1693
            new EditField('', 'POINT(3 4)', '', true, false, false, 'ST_GeomFromText', null, null, false),
1694
            false,
1695
            '',
1696
        );
1697
        $this->assertEquals('ST_GeomFromText(\'POINT(3 4)\')', $result);
1698
1699
        // Test different data types
1700
1701
        // Datatype: protected copied from the databse
1702
        $GLOBALS['table'] = 'test_table';
1703
        $result = $this->insertEdit->getQueryValueForInsert(
1704
            new EditField(
1705
                'name',
1706
                '',
1707
                'protected',
1708
                false,
1709
                false,
1710
                false,
1711
                '',
1712
                null,
1713
                null,
1714
                false,
1715
            ),
1716
            true,
1717
            '`id` = 4',
1718
        );
1719
        $this->assertEquals('0x313031', $result);
1720
1721
        // An empty value for auto increment column should be converted to NULL
1722
        $result = $this->insertEdit->getQueryValueForInsert(
1723
            new EditField(
1724
                '',
1725
                '', // empty for null
1726
                '',
1727
                true,
1728
                false,
1729
                false,
1730
                '',
1731
                null,
1732
                null,
1733
                false,
1734
            ),
1735
            false,
1736
            '',
1737
        );
1738
        $this->assertEquals('NULL', $result);
1739
1740
        // Simple empty value
1741
        $result = $this->insertEdit->getQueryValueForInsert(
1742
            new EditField(
1743
                '',
1744
                '',
1745
                '',
1746
                false,
1747
                false,
1748
                false,
1749
                '',
1750
                null,
1751
                null,
1752
                false,
1753
            ),
1754
            false,
1755
            '',
1756
        );
1757
        $this->assertEquals("''", $result);
1758
1759
        // Datatype: set
1760
        $result = $this->insertEdit->getQueryValueForInsert(
1761
            new EditField(
1762
                '',
1763
                '', // doesn't matter what the value is
1764
                'set',
1765
                false,
1766
                false,
1767
                false,
1768
                '',
1769
                null,
1770
                null,
1771
                false,
1772
            ),
1773
            false,
1774
            '',
1775
        );
1776
        $this->assertEquals("''", $result);
1777
1778
        // Datatype: protected with no value should produce an empty string
1779
        $result = $this->insertEdit->getQueryValueForInsert(
1780
            new EditField(
1781
                '',
1782
                '',
1783
                'protected',
1784
                false,
1785
                false,
1786
                false,
1787
                '',
1788
                null,
1789
                null,
1790
                false,
1791
            ),
1792
            false,
1793
            '',
1794
        );
1795
        $this->assertEquals('', $result);
1796
1797
        // Datatype: protected with null flag set
1798
        $result = $this->insertEdit->getQueryValueForInsert(
1799
            new EditField(
1800
                '',
1801
                '',
1802
                'protected',
1803
                false,
1804
                true,
1805
                false,
1806
                '',
1807
                null,
1808
                null,
1809
                false,
1810
            ),
1811
            false,
1812
            '',
1813
        );
1814
        $this->assertEquals('NULL', $result);
1815
1816
        // Datatype: bit
1817
        $result = $this->insertEdit->getQueryValueForInsert(
1818
            new EditField(
1819
                '',
1820
                '20\'12',
1821
                'bit',
1822
                false,
1823
                false,
1824
                false,
1825
                '',
1826
                null,
1827
                null,
1828
                false,
1829
            ),
1830
            false,
1831
            '',
1832
        );
1833
        $this->assertEquals("b'00010'", $result);
1834
1835
        // Datatype: date
1836
        $result = $this->insertEdit->getQueryValueForInsert(
1837
            new EditField(
1838
                '',
1839
                '20\'12',
1840
                'date',
1841
                false,
1842
                false,
1843
                false,
1844
                '',
1845
                null,
1846
                null,
1847
                false,
1848
            ),
1849
            false,
1850
            '',
1851
        );
1852
        $this->assertEquals("'20\\'12'", $result);
1853
1854
        // A NULL checkbox
1855
        $result = $this->insertEdit->getQueryValueForInsert(
1856
            new EditField(
1857
                '',
1858
                '',
1859
                'set',
1860
                false,
1861
                true,
1862
                false,
1863
                '',
1864
                null,
1865
                null,
1866
                false,
1867
            ),
1868
            false,
1869
            '',
1870
        );
1871
        $this->assertEquals('NULL', $result);
1872
1873
        // Datatype: protected but NULL checkbox was unchecked without uploading a file
1874
        $result = $this->insertEdit->getQueryValueForInsert(
1875
            new EditField(
1876
                '',
1877
                '',
1878
                'protected',
1879
                false,
1880
                false,
1881
                true, // was previously NULL
1882
                '',
1883
                null,
1884
                null,
1885
                false, // no upload
1886
            ),
1887
            false,
1888
            '',
1889
        );
1890
        $this->assertEquals("''", $result);
1891
1892
        // Datatype: date with default value
1893
        $result = $this->insertEdit->getQueryValueForInsert(
1894
            new EditField(
1895
                '',
1896
                'current_timestamp()',
1897
                'date',
1898
                false,
1899
                false,
1900
                true, // NULL should be ignored
1901
                '',
1902
                null,
1903
                null,
1904
                false,
1905
            ),
1906
            false,
1907
            '',
1908
        );
1909
        $this->assertEquals('current_timestamp()', $result);
1910
1911
        // Datatype: hex without 0x
1912
        $result = $this->insertEdit->getQueryValueForInsert(
1913
            new EditField(
1914
                '',
1915
                '222aaafff',
1916
                'hex',
1917
                false,
1918
                false,
1919
                false,
1920
                '',
1921
                null,
1922
                null,
1923
                false,
1924
            ),
1925
            false,
1926
            '',
1927
        );
1928
        $this->assertEquals('0x222aaafff', $result);
1929
1930
        // Datatype: hex with 0x
1931
        $result = $this->insertEdit->getQueryValueForInsert(
1932
            new EditField(
1933
                '',
1934
                '0x222aaafff',
1935
                'hex',
1936
                false,
1937
                false,
1938
                false,
1939
                '',
1940
                null,
1941
                null,
1942
                false,
1943
            ),
1944
            false,
1945
            '',
1946
        );
1947
        $this->assertEquals('0x222aaafff', $result);
1948
    }
1949
1950
    /**
1951
     * Test for getQueryValuesForUpdate
1952
     */
1953
    public function testGetQueryValuesForUpdate(): void
1954
    {
1955
        // Simple update
1956
        $result = $this->insertEdit->getQueryValueForUpdate(
1957
            new EditField(
1958
                'fld',
1959
                'foo',
1960
                '',
1961
                false,
1962
                false,
1963
                false,
1964
                '',
1965
                null,
1966
                null,
1967
                false,
1968
            ),
1969
        );
1970
        $this->assertEquals("`fld` = 'foo'", $result);
1971
1972
        // Update of null when it was null previously
1973
        $result = $this->insertEdit->getQueryValueForUpdate(
1974
            new EditField(
1975
                'fld',
1976
                '', // null fields will have no value
1977
                '',
1978
                false,
1979
                true,
1980
                true,
1981
                '',
1982
                null,
1983
                null,
1984
                false,
1985
            ),
1986
        );
1987
        $this->assertEquals('', $result);
1988
1989
        // Update of null when it was NOT null previously
1990
        $result = $this->insertEdit->getQueryValueForUpdate(
1991
            new EditField(
1992
                'fld',
1993
                '', // null fields will have no value
1994
                '',
1995
                false,
1996
                true,
1997
                false,
1998
                '',
1999
                null,
2000
                '', // in edit mode the previous value will be empty string
2001
                false,
2002
            ),
2003
        );
2004
        $this->assertEquals('`fld` = NULL', $result);
2005
2006
        // Update to NOT null when it was null previously
2007
        $result = $this->insertEdit->getQueryValueForUpdate(
2008
            new EditField(
2009
                'fld',
2010
                "ab'c",
2011
                '',
2012
                false,
2013
                false,
2014
                true,
2015
                '',
2016
                null,
2017
                null,
2018
                false,
2019
            ),
2020
        );
2021
        $this->assertEquals("`fld` = 'ab\'c'", $result);
2022
2023
        // Test to see if a zero-string is not ignored
2024
        $result = $this->insertEdit->getQueryValueForUpdate(
2025
            new EditField(
2026
                'fld',
2027
                '0', // zero-string provided as value
2028
                '',
2029
                false,
2030
                false,
2031
                false,
2032
                '',
2033
                null,
2034
                null,
2035
                false,
2036
            ),
2037
        );
2038
        $this->assertEquals("`fld` = '0'", $result);
2039
2040
        // Test to check if blob field that was left unchanged during edit will be ignored
2041
        $result = $this->insertEdit->getQueryValueForUpdate(
2042
            new EditField(
2043
                'fld',
2044
                '', // no value
2045
                'protected',
2046
                false,
2047
                false,
2048
                false,
2049
                '',
2050
                null,
2051
                null,
2052
                false,
2053
            ),
2054
        );
2055
        $this->assertEquals('', $result);
2056
2057
        // Test to see if a field will be ignored if it the value is unchanged
2058
        $result = $this->insertEdit->getQueryValueForUpdate(
2059
            new EditField(
2060
                'fld',
2061
                "a'b",
2062
                '',
2063
                false,
2064
                false,
2065
                false,
2066
                '',
2067
                null,
2068
                "a'b",
2069
                false,
2070
            ),
2071
        );
2072
2073
        $this->assertEquals('', $result);
2074
2075
        // Test that an empty value uses the uuid function to generate a value
2076
        $result = $this->insertEdit->getQueryValueForUpdate(
2077
            new EditField(
2078
                'fld',
2079
                "''",
2080
                'uuid',
2081
                false,
2082
                false,
2083
                false,
2084
                '',
2085
                null,
2086
                '',
2087
                false,
2088
            ),
2089
        );
2090
2091
        $this->assertEquals('`fld` = uuid()', $result);
2092
2093
        // Test that the uuid function as a value uses the uuid function to generate a value
2094
        $result = $this->insertEdit->getQueryValueForUpdate(
2095
            new EditField(
2096
                'fld',
2097
                "'uuid()'",
2098
                'uuid',
2099
                false,
2100
                false,
2101
                false,
2102
                '',
2103
                null,
2104
                '',
2105
                false,
2106
            ),
2107
        );
2108
2109
        $this->assertEquals('`fld` = uuid()', $result);
2110
2111
        // Test that the uuid function as a value uses the uuid function to generate a value
2112
        $result = $this->insertEdit->getQueryValueForUpdate(
2113
            new EditField(
2114
                'fld',
2115
                'uuid()',
2116
                'uuid',
2117
                false,
2118
                false,
2119
                false,
2120
                '',
2121
                null,
2122
                '',
2123
                false,
2124
            ),
2125
        );
2126
2127
        $this->assertEquals('`fld` = uuid()', $result);
2128
2129
        // Test that the uuid type does not have a default value other than null when it is nullable
2130
        $result = $this->insertEdit->getQueryValueForUpdate(
2131
            new EditField(
2132
                'fld',
2133
                '',
2134
                'uuid',
2135
                false,
2136
                true,
2137
                false,
2138
                '',
2139
                null,
2140
                '',
2141
                false,
2142
            ),
2143
        );
2144
2145
        $this->assertEquals('`fld` = NULL', $result);
2146
    }
2147
2148
    /**
2149
     * Test for verifyWhetherValueCanBeTruncatedAndAppendExtraData
2150
     */
2151
    public function testVerifyWhetherValueCanBeTruncatedAndAppendExtraData(): void
2152
    {
2153
        $extraData = ['isNeedToRecheck' => true];
2154
2155
        $_POST['where_clause'] = [];
2156
        $_POST['where_clause'][0] = 1;
2157
2158
        $dbi = $this->getMockBuilder(DatabaseInterface::class)
2159
            ->disableOriginalConstructor()
2160
            ->getMock();
2161
2162
        $resultStub = $this->createMock(DummyResult::class);
2163
2164
        $dbi->expects($this->exactly(3))
2165
            ->method('tryQuery')
2166
            ->with('SELECT `table`.`a` FROM `db`.`table` WHERE 1')
2167
            ->willReturn($resultStub);
2168
2169
        $meta1 = FieldHelper::fromArray(['type' => MYSQLI_TYPE_TINY]);
2170
        $meta2 = FieldHelper::fromArray(['type' => MYSQLI_TYPE_TINY]);
2171
        $meta3 = FieldHelper::fromArray(['type' => MYSQLI_TYPE_TIMESTAMP]);
2172
        $dbi->expects($this->exactly(3))
2173
            ->method('getFieldsMeta')
2174
            ->will($this->onConsecutiveCalls([$meta1], [$meta2], [$meta3]));
2175
2176
        $resultStub->expects($this->exactly(3))
2177
            ->method('fetchValue')
2178
            ->will($this->onConsecutiveCalls(false, '123', '2013-08-28 06:34:14'));
2179
2180
        $GLOBALS['dbi'] = $dbi;
2181
        $this->insertEdit = new InsertEdit(
2182
            $GLOBALS['dbi'],
2183
            new Relation($GLOBALS['dbi']),
2184
            new Transformations(),
2185
            new FileListing(),
2186
            new Template(),
2187
        );
2188
2189
        $this->insertEdit->verifyWhetherValueCanBeTruncatedAndAppendExtraData('db', 'table', 'a', $extraData);
2190
2191
        $this->assertFalse($extraData['isNeedToRecheck']);
2192
2193
        $this->insertEdit->verifyWhetherValueCanBeTruncatedAndAppendExtraData('db', 'table', 'a', $extraData);
2194
2195
        $this->assertEquals('123', $extraData['truncatableFieldValue']);
2196
        $this->assertTrue($extraData['isNeedToRecheck']);
2197
2198
        $this->insertEdit->verifyWhetherValueCanBeTruncatedAndAppendExtraData('db', 'table', 'a', $extraData);
2199
2200
        $this->assertEquals('2013-08-28 06:34:14.000000', $extraData['truncatableFieldValue']);
2201
        $this->assertTrue($extraData['isNeedToRecheck']);
2202
    }
2203
2204
    /**
2205
     * Test for getTableColumns
2206
     */
2207
    public function testGetTableColumns(): void
2208
    {
2209
        $dbi = $this->getMockBuilder(DatabaseInterface::class)
2210
            ->disableOriginalConstructor()
2211
            ->getMock();
2212
2213
        $dbi->expects($this->once())
2214
            ->method('selectDb')
2215
            ->with('db');
2216
2217
        $dbi->expects($this->once())
2218
            ->method('getColumns')
2219
            ->with('db', 'table')
2220
            ->will($this->returnValue([
2221
                [
2222
                    'Field' => 'b',
2223
                    'Type' => 'd',
2224
                    'Collation' => null,
2225
                    'Null' => 'NO',
2226
                    'Key' => '',
2227
                    'Default' => null,
2228
                    'Extra' => '',
2229
                    'Privileges' => '',
2230
                    'Comment' => '',
2231
                ],
2232
                [
2233
                    'Field' => 'f',
2234
                    'Type' => 'h',
2235
                    'Collation' => null,
2236
                    'Null' => 'YES',
2237
                    'Key' => '',
2238
                    'Default' => null,
2239
                    'Extra' => '',
2240
                    'Privileges' => '',
2241
                    'Comment' => '',
2242
                ],
2243
            ]));
2244
2245
        $GLOBALS['dbi'] = $dbi;
2246
        $this->insertEdit = new InsertEdit(
2247
            $GLOBALS['dbi'],
2248
            new Relation($GLOBALS['dbi']),
2249
            new Transformations(),
2250
            new FileListing(),
2251
            new Template(),
2252
        );
2253
2254
        $result = $this->insertEdit->getTableColumns('db', 'table');
2255
2256
        $this->assertEquals(
2257
            [
2258
                new ColumnFull('b', 'd', null, false, '', null, '', '', ''),
2259
                new ColumnFull('f', 'h', null, true, '', null, '', '', ''),
2260
            ],
2261
            $result,
2262
        );
2263
    }
2264
2265
    /**
2266
     * Test for determineInsertOrEdit
2267
     */
2268
    public function testDetermineInsertOrEdit(): void
2269
    {
2270
        $dbi = $this->getMockBuilder(DatabaseInterface::class)
2271
            ->disableOriginalConstructor()
2272
            ->getMock();
2273
2274
        $resultStub = $this->createMock(DummyResult::class);
2275
2276
        $dbi->expects($this->exactly(2))
2277
            ->method('query')
2278
            ->will($this->returnValue($resultStub));
2279
2280
        $GLOBALS['dbi'] = $dbi;
2281
        $_POST['where_clause'] = '1';
2282
        $_SESSION['edit_next'] = '1';
2283
        $_POST['ShowFunctionFields'] = true;
2284
        $_POST['ShowFieldTypesInDataEditView'] = true;
2285
        $_POST['after_insert'] = 'edit_next';
2286
        $GLOBALS['cfg']['InsertRows'] = 2;
2287
        $GLOBALS['cfg']['ShowSQL'] = false;
2288
        $_POST['default_action'] = 'insert';
2289
2290
        $responseMock = $this->getMockBuilder(ResponseRenderer::class)
2291
            ->disableOriginalConstructor()
2292
            ->onlyMethods(['addHtml'])
2293
            ->getMock();
2294
2295
        $restoreInstance = ResponseRenderer::getInstance();
2296
        $response = new ReflectionProperty(ResponseRenderer::class, 'instance');
2297
        $response->setValue($responseMock);
2298
2299
        $this->insertEdit = new InsertEdit(
2300
            $GLOBALS['dbi'],
2301
            new Relation($GLOBALS['dbi']),
2302
            new Transformations(),
2303
            new FileListing(),
2304
            new Template(),
2305
        );
2306
2307
        $result = $this->insertEdit->determineInsertOrEdit('1', 'db', 'table');
2308
2309
        $this->assertEquals(
2310
            [false, null, [1], null, [$resultStub], [[]], false, 'edit_next'],
2311
            $result,
2312
        );
2313
2314
        // case 2
2315
        unset($_POST['where_clause']);
2316
        unset($_SESSION['edit_next']);
2317
        $_POST['default_action'] = '';
2318
2319
        $result = $this->insertEdit->determineInsertOrEdit(null, 'db', 'table');
2320
2321
        $response->setValue($restoreInstance);
2322
2323
        $this->assertEquals(
2324
            [true, null, [], null, $resultStub, [false, false], false, 'edit_next'],
2325
            $result,
2326
        );
2327
    }
2328
2329
    /**
2330
     * Test for getCommentsMap
2331
     */
2332
    public function testGetCommentsMap(): void
2333
    {
2334
        $GLOBALS['cfg']['ShowPropertyComments'] = false;
2335
2336
        $dbi = $this->getMockBuilder(DatabaseInterface::class)
2337
            ->disableOriginalConstructor()
2338
            ->getMock();
2339
2340
        $dbi->expects($this->once())
2341
            ->method('getColumns')
2342
            ->with('db', 'table', true)
2343
            ->will(
2344
                $this->returnValue(
2345
                    [['Comment' => 'b', 'Field' => 'd']],
2346
                ),
2347
            );
2348
2349
        $dbi->expects($this->any())
2350
            ->method('getTable')
2351
            ->will(
2352
                $this->returnValue(
2353
                    new Table('table', 'db', $GLOBALS['dbi']),
2354
                ),
2355
            );
2356
2357
        $GLOBALS['dbi'] = $dbi;
2358
        $this->insertEdit = new InsertEdit(
2359
            $GLOBALS['dbi'],
2360
            new Relation($GLOBALS['dbi']),
2361
            new Transformations(),
2362
            new FileListing(),
2363
            new Template(),
2364
        );
2365
2366
        $this->assertEquals(
2367
            [],
2368
            $this->insertEdit->getCommentsMap('db', 'table'),
2369
        );
2370
2371
        $GLOBALS['cfg']['ShowPropertyComments'] = true;
2372
2373
        $this->assertEquals(
2374
            ['d' => 'b'],
2375
            $this->insertEdit->getCommentsMap('db', 'table'),
2376
        );
2377
    }
2378
2379
    /**
2380
     * Test for getHtmlForIgnoreOption
2381
     */
2382
    public function testGetHtmlForIgnoreOption(): void
2383
    {
2384
        $expected = '<input type="checkbox" %sname="insert_ignore_1"'
2385
            . ' id="insert_ignore_1"><label for="insert_ignore_1">'
2386
            . 'Ignore</label><br>' . "\n";
2387
        $checked = 'checked="checked" ';
2388
        $this->assertEquals(
2389
            sprintf($expected, $checked),
2390
            $this->insertEdit->getHtmlForIgnoreOption(1),
2391
        );
2392
2393
        $this->assertEquals(
2394
            sprintf($expected, ''),
2395
            $this->insertEdit->getHtmlForIgnoreOption(1, false),
2396
        );
2397
    }
2398
2399
    /**
2400
     * Test for getHtmlForInsertEditFormColumn
2401
     */
2402
    public function testGetHtmlForInsertEditFormColumn(): void
2403
    {
2404
        $_SESSION[' HMAC_secret '] = hash('sha1', 'test');
2405
        $GLOBALS['plugin_scripts'] = [];
2406
        $foreigners = ['foreign_keys_data' => []];
2407
        $tableColumn = new ColumnFull('col', 'varchar(20)', null, true, '', null, '', 'insert,update,select', '');
2408
        $repopulate = [md5('col') => 'val'];
2409
        $columnMime = [
2410
            'input_transformation' => 'Input/Image_JPEG_Upload.php',
2411
            'input_transformation_options' => '150',
2412
        ];
2413
2414
        // Test w/ input transformation
2415
        $actual = $this->callFunction(
2416
            $this->insertEdit,
2417
            InsertEdit::class,
2418
            'getHtmlForInsertEditFormColumn',
2419
            [
2420
                $tableColumn,
2421
                0,
2422
                [],
2423
                -1,
2424
                false,
2425
                [],
2426
                0,
2427
                false,
2428
                $foreigners,
2429
                'table',
2430
                'db',
2431
                0,
2432
                '',
2433
                '',
2434
                $repopulate,
2435
                $columnMime,
2436
                '',
2437
            ],
2438
        );
2439
2440
        $actual = $this->parseString($actual);
2441
2442
        $this->assertStringContainsString('col', $actual);
2443
        $this->assertStringContainsString('<option>AES_ENCRYPT</option>', $actual);
2444
        $this->assertStringContainsString('<span class="column_type" dir="ltr">varchar(20)</span>', $actual);
2445
        $this->assertStringContainsString('<tr class="noclick">', $actual);
2446
        $this->assertStringContainsString('<span class="default_value hide">', $actual);
2447
        $this->assertStringContainsString('<img src="" width="150" height="100" alt="Image preview here">', $actual);
2448
        $this->assertStringContainsString(
2449
            '<input type="file" '
2450
            . 'name="fields_upload[multi_edit][0][d89e2ddb530bb8953b290ab0793aecb0]" '
2451
            . 'accept="image/*" '
2452
            . 'class="image-upload"'
2453
            . '>',
2454
            $actual,
2455
        );
2456
2457
        // Test w/o input_transformation
2458
        $tableColumn = new ColumnFull('qwerty', 'datetime', null, true, '', null, '', 'insert,update,select', '');
2459
        $repopulate = [md5('qwerty') => '12-10-14'];
2460
        $actual = $this->callFunction(
2461
            $this->insertEdit,
2462
            InsertEdit::class,
2463
            'getHtmlForInsertEditFormColumn',
2464
            [
2465
                $tableColumn,
2466
                0,
2467
                [],
2468
                -1,
2469
                true,
2470
                [],
2471
                0,
2472
                false,
2473
                $foreigners,
2474
                'table',
2475
                'db',
2476
                0,
2477
                '',
2478
                '',
2479
                $repopulate,
2480
                [],
2481
                '',
2482
            ],
2483
        );
2484
2485
        $actual = $this->parseString($actual);
2486
2487
        $this->assertStringContainsString('qwerty', $actual);
2488
        $this->assertStringContainsString('<option>UUID</option>', $actual);
2489
        $this->assertStringContainsString('<span class="column_type" dir="ltr">datetime</span>', $actual);
2490
        $this->assertStringContainsString(
2491
            '<input type="text" name="fields[multi_edit][0][d8578edf8458ce06fbc5bb76a58c5ca4]" value="12-10-14.000000"',
2492
            $actual,
2493
        );
2494
2495
        $this->assertStringContainsString(
2496
            '<select name="funcs[multi_edit][0][d8578edf8458ce06fbc5bb76a58c5ca4]"'
2497
            . ' onchange="return verificationsAfterFieldChange(\'d8578edf8458ce06fbc5bb76a58c5ca4\','
2498
            . ' \'0\', \'datetime\')" id="field_1_1">',
2499
            $actual,
2500
        );
2501
        $this->assertStringContainsString('<option>DATE</option>', $actual);
2502
2503
        $this->assertStringContainsString(
2504
            '<input type="hidden" name="fields_null_prev[multi_edit][0][d8578edf8458ce06fbc5bb76a58c5ca4]">',
2505
            $actual,
2506
        );
2507
2508
        $this->assertStringContainsString(
2509
            '<input type="checkbox" class="checkbox_null"'
2510
            . ' name="fields_null[multi_edit][0][d8578edf8458ce06fbc5bb76a58c5ca4]" id="field_1_2"'
2511
            . ' aria-label="Use the NULL value for this column.">',
2512
            $actual,
2513
        );
2514
2515
        $this->assertStringContainsString(
2516
            '<input type="hidden" class="nullify_code"'
2517
            . ' name="nullify_code[multi_edit][0][d8578edf8458ce06fbc5bb76a58c5ca4]" value="5"',
2518
            $actual,
2519
        );
2520
2521
        $this->assertStringContainsString(
2522
            '<input type="hidden" class="hashed_field"'
2523
            . ' name="hashed_field[multi_edit][0][d8578edf8458ce06fbc5bb76a58c5ca4]" '
2524
            . 'value="d8578edf8458ce06fbc5bb76a58c5ca4">',
2525
            $actual,
2526
        );
2527
2528
        $this->assertStringContainsString(
2529
            '<input type="hidden" class="multi_edit"'
2530
            . ' name="multi_edit[multi_edit][0][d8578edf8458ce06fbc5bb76a58c5ca4]" value="[multi_edit][0]"',
2531
            $actual,
2532
        );
2533
    }
2534
2535
    /**
2536
     * Test for getHtmlForInsertEditRow
2537
     */
2538
    public function testGetHtmlForInsertEditRow(): void
2539
    {
2540
        $GLOBALS['plugin_scripts'] = [];
2541
        $GLOBALS['cfg']['LongtextDoubleTextarea'] = true;
2542
        $GLOBALS['cfg']['CharEditing'] = 'input';
2543
        $GLOBALS['cfg']['TextareaRows'] = 10;
2544
        $GLOBALS['cfg']['TextareaCols'] = 11;
2545
        $foreigners = ['foreign_keys_data' => []];
2546
        $tableColumns = [
2547
            new ColumnFull('test', 'longtext', null, true, '', null, '', 'select,insert,update,references', ''),
2548
        ];
2549
2550
        $resultStub = $this->createMock(DummyResult::class);
2551
        $resultStub->expects($this->any())
2552
            ->method('getFieldsMeta')
2553
            ->will($this->returnValue([FieldHelper::fromArray(['type' => 0, 'length' => -1])]));
2554
2555
        $actual = $this->insertEdit->getHtmlForInsertEditRow(
2556
            [],
2557
            $tableColumns,
2558
            [],
2559
            $resultStub,
2560
            false,
2561
            [],
2562
            false,
2563
            $foreigners,
2564
            'table',
2565
            'db',
2566
            0,
2567
            'ltr',
2568
            [],
2569
            ['wc'],
2570
        );
2571
        $this->assertStringContainsString('test', $actual);
2572
        $this->assertStringContainsString('<th>Column</th>', $actual);
2573
        $this->assertStringContainsString('<a', $actual);
2574
        $this->assertStringContainsString('<th class="w-50">Value</th>', $actual);
2575
        $this->assertStringContainsString('<span class="column_type" dir="ltr">longtext</span>', $actual);
2576
        $this->assertStringContainsString(
2577
            '<textarea name="fields[multi_edit][0][098f6bcd4621d373cade4e832627b4f6]" id="field_1_3"'
2578
                . ' data-type="CHAR" dir="ltr" rows="20" cols="22"',
2579
            $actual,
2580
        );
2581
    }
2582
2583
    /**
2584
     * Test for getHtmlForInsertEditRow based on the column privilges
2585
     */
2586
    public function testGetHtmlForInsertEditRowBasedOnColumnPrivileges(): void
2587
    {
2588
        $GLOBALS['plugin_scripts'] = [];
2589
        $GLOBALS['cfg']['LongtextDoubleTextarea'] = true;
2590
        $GLOBALS['cfg']['CharEditing'] = 'input';
2591
        $foreigners = ['foreign_keys_data' => []];
2592
2593
        // edit
2594
        $tableColumns = [
2595
            new ColumnFull('foo', 'longtext', null, true, '', null, '', 'select,insert,update,references', ''),
2596
            new ColumnFull('bar', 'longtext', null, true, '', null, '', 'select,insert,references', ''),
2597
        ];
2598
2599
        $resultStub = $this->createMock(DummyResult::class);
2600
        $resultStub->expects($this->any())
2601
            ->method('getFieldsMeta')
2602
            ->will($this->returnValue([
2603
                FieldHelper::fromArray(['type' => 0, 'length' => -1]),
2604
                FieldHelper::fromArray(['type' => 0, 'length' => -1]),
2605
                FieldHelper::fromArray(['type' => 0, 'length' => -1]),
2606
            ]));
2607
2608
        $actual = $this->insertEdit->getHtmlForInsertEditRow(
2609
            [],
2610
            $tableColumns,
2611
            [],
2612
            $resultStub,
2613
            false,
2614
            [],
2615
            false,
2616
            $foreigners,
2617
            'table',
2618
            'db',
2619
            0,
2620
            '',
2621
            [],
2622
            ['wc'],
2623
        );
2624
        $this->assertStringContainsString('foo', $actual);
2625
        $this->assertStringContainsString('bar', $actual);
2626
2627
        // insert
2628
        $tableColumns = [
2629
            new ColumnFull('foo', 'longtext', null, true, '', null, '', 'select,insert,update,references', ''),
2630
            new ColumnFull('bar', 'longtext', null, true, '', null, '', 'select,update,references', ''),
2631
            new ColumnFull('point', 'point', null, false, '', null, '', 'select,update,references', ''),
2632
        ];
2633
        $actual = $this->insertEdit->getHtmlForInsertEditRow(
2634
            [],
2635
            $tableColumns,
2636
            [],
2637
            $resultStub,
2638
            true,
2639
            [],
2640
            false,
2641
            $foreigners,
2642
            'table',
2643
            'db',
2644
            0,
2645
            '',
2646
            [],
2647
            ['wc'],
2648
        );
2649
        $this->assertStringContainsString('foo', $actual);
2650
        $this->assertStringContainsString(
2651
            '<textarea name="fields[multi_edit][0][37b51d194a7513e45b56f6524f2d51f2]"',
2652
            $actual,
2653
        );
2654
        $this->assertStringContainsString(
2655
            '<a href="#" target="_blank"><span class="text-nowrap"><img src="themes/dot.'
2656
            . 'gif" title="Edit/Insert" alt="Edit/Insert" class="icon ic_b_edit">&nbsp;Edit/Insert'
2657
            . '</span></a>',
2658
            $actual,
2659
        );
2660
    }
2661
2662
    /**
2663
     * Convert mixed type value to string
2664
     */
2665
    private function parseString(mixed $value): string
2666
    {
2667
        if (is_string($value)) {
2668
            return $value;
2669
        }
2670
2671
        if (is_object($value) || is_scalar($value)) {
2672
            return strval($value);
2673
        }
2674
2675
        return '';
2676
    }
2677
}
2678