Issues (1885)

tests/unit/Error/ErrorHandlerTest.php (3 issues)

1
<?php
2
3
declare(strict_types=1);
4
5
namespace PhpMyAdmin\Tests\Error;
6
7
use Exception;
8
use PhpMyAdmin\Config;
9
use PhpMyAdmin\DatabaseInterface;
10
use PhpMyAdmin\Error\Error;
11
use PhpMyAdmin\Error\ErrorHandler;
12
use PhpMyAdmin\Exceptions\ExitException;
13
use PhpMyAdmin\Tests\AbstractTestCase;
14
use PHPUnit\Framework\Attributes\CoversClass;
15
use PHPUnit\Framework\Attributes\DataProvider;
16
use PHPUnit\Framework\Attributes\Group;
17
use Throwable;
18
19
use function array_keys;
20
use function array_pop;
21
22
use const E_ERROR;
23
use const E_RECOVERABLE_ERROR;
24
use const E_USER_NOTICE;
25
use const E_USER_WARNING;
26
use const E_WARNING;
27
28
#[CoversClass(ErrorHandler::class)]
29
class ErrorHandlerTest extends AbstractTestCase
30
{
31
    protected ErrorHandler $object;
32
33
    /**
34
     * Sets up the fixture, for example, opens a network connection.
35
     * This method is called before a test is executed.
36
     */
37
    protected function setUp(): void
38
    {
39
        parent::setUp();
40
41
        $GLOBALS['lang'] = 'en';
42
        DatabaseInterface::$instance = $this->createDatabaseInterface();
43
        $this->object = new ErrorHandler();
44
        $_SESSION['errors'] = [];
45
        $config = Config::getInstance();
46
        $config->settings['environment'] = 'production';
47
        $config->settings['SendErrorReports'] = 'always';
48
    }
49
50
    /**
51
     * Tears down the fixture, for example, closes a network connection.
52
     * This method is called after a test is executed.
53
     */
54
    protected function tearDown(): void
55
    {
56
        parent::tearDown();
57
58
        unset($this->object);
59
    }
60
61
    public function testUniqueness(): void
62
    {
63
        ErrorHandler::$instance = null;
64
        $instanceOne = ErrorHandler::getInstance();
65
        $instanceTwo = ErrorHandler::getInstance();
66
        self::assertSame($instanceOne, $instanceTwo);
67
    }
68
69
    /** @return array<array{int, string, string, int, string, string}> */
70
    public static function providerForTestHandleError(): array
71
    {
72
        return [
73
            [E_RECOVERABLE_ERROR, 'Compile Error', 'error.txt', 12, 'never', ''],
74
            [E_RECOVERABLE_ERROR, 'Compile Error', 'error.txt', 12, 'always', 'Compile Error'],
75
            [E_RECOVERABLE_ERROR, 'Compile Error', 'error.txt', 12, 'ask', 'Compile Error'],
76
            [E_USER_NOTICE, 'User notice', 'error.txt', 12, 'never', 'User notice'],
77
            [E_USER_NOTICE, 'User notice', 'error.txt', 12, 'always', 'User notice'],
78
            [E_USER_NOTICE, 'User notice', 'error.txt', 12, 'ask', 'User notice'],
79
        ];
80
    }
81
82
    #[DataProvider('providerForTestHandleError')]
83
    public function testGetDisplayErrors(
84
        int $errorNumber,
85
        string $errorMessage,
86
        string $errorFile,
87
        int $errorLine,
88
        string $reportErrorConfig,
89
        string $expected,
90
    ): void {
91
        $config = new Config();
92
        $config->settings['environment'] = 'production';
93
        $config->settings['SendErrorReports'] = $reportErrorConfig;
94
        Config::$instance = $config;
95
96
        $error = new Error($errorNumber, $errorMessage, $errorFile, $errorLine);
97
        $_SESSION['errors'] = [$error->getHash() => $error];
98
99
        $handler = new ErrorHandler();
100
        if ($expected === '') {
101
            self::assertSame('', $handler->getDispErrors());
102
        } else {
103
            self::assertStringContainsString($expected, $handler->getDispErrors());
104
        }
105
    }
106
107
    /**
108
     * Test for checkSavedErrors
109
     */
110
    public function testCheckSavedErrors(): void
111
    {
112
        $this->callFunction(
113
            $this->object,
114
            ErrorHandler::class,
115
            'checkSavedErrors',
116
            [],
117
        );
118
        self::assertArrayNotHasKey('errors', $_SESSION);
119
    }
120
121
    /**
122
     * Test for countErrors
123
     */
124
    #[Group('medium')]
125
    public function testCountErrors(): void
126
    {
127
        $this->object->addError('Compile Error', E_WARNING, 'error.txt', 15);
128
        self::assertSame(
129
            1,
130
            $this->object->countErrors(),
131
        );
132
    }
133
134
    /**
135
     * Test for sliceErrors
136
     */
137
    #[Group('medium')]
138
    public function testSliceErrors(): void
139
    {
140
        $this->object->addError('Compile Error', E_WARNING, 'error.txt', 15);
141
        $this->object->addError('Compile Error', E_WARNING, 'error.txt', 16);
142
        self::assertSame(
143
            2,
144
            $this->object->countErrors(),
145
        );
146
        self::assertSame(
147
            [],
148
            $this->object->sliceErrors(2),
149
        );
150
        self::assertSame(
151
            2,
152
            $this->object->countErrors(),
153
        );
154
        self::assertCount(
155
            1,
156
            $this->object->sliceErrors(1),
157
        );
158
        self::assertSame(
159
            1,
160
            $this->object->countErrors(),
161
        );
162
    }
163
164
    /**
165
     * Test for sliceErrors with 10 elements as an example
166
     */
167
    #[Group('medium')]
168
    public function testSliceErrorsOtherExample(): void
169
    {
170
        for ($i = 0; $i < 10; $i++) {
171
            $this->object->addError('Compile Error', E_WARNING, 'error.txt', $i);
172
        }
173
174
        // 10 initial items
175
        self::assertSame(10, $this->object->countErrors());
176
        self::assertCount(10, $this->object->getCurrentErrors());
177
178
        // slice 9 elements, returns one 10 - 9
179
        $elements = $this->object->sliceErrors(9);
180
        $firstKey = array_keys($elements)[0];
181
182
        // Gives the last element
183
        self::assertSame(
184
            [$firstKey => $elements[$firstKey]],
185
            $elements,
186
        );
187
        self::assertCount(9, $this->object->getCurrentErrors());
188
        self::assertSame(9, $this->object->countErrors());
189
190
        // Slice as much as there is (9), does nothing
191
        $elements = $this->object->sliceErrors(9);
192
        self::assertSame([], $elements);
193
        self::assertCount(9, $this->object->getCurrentErrors());
194
        self::assertSame(9, $this->object->countErrors());
195
196
        // Slice 0, removes everything
197
        $elements = $this->object->sliceErrors(0);
198
        self::assertCount(9, $elements);
199
        self::assertCount(0, $this->object->getCurrentErrors());
200
        self::assertSame(0, $this->object->countErrors());
201
    }
202
203
    /**
204
     * Test for countUserErrors
205
     */
206
    public function testCountUserErrors(): void
207
    {
208
        $this->object->addError('Compile Error', E_WARNING, 'error.txt', 15);
209
        self::assertSame(
210
            0,
211
            $this->object->countUserErrors(),
212
        );
213
        $this->object->addError('Compile Error', E_USER_WARNING, 'error.txt', 15);
214
        self::assertSame(
215
            1,
216
            $this->object->countUserErrors(),
217
        );
218
    }
219
220
    /**
221
     * Test for hasUserErrors
222
     */
223
    public function testHasUserErrors(): void
224
    {
225
        self::assertFalse($this->object->hasUserErrors());
226
    }
227
228
    /**
229
     * Test for hasErrors
230
     */
231
    public function testHasErrors(): void
232
    {
233
        self::assertFalse($this->object->hasErrors());
234
    }
235
236
    /**
237
     * Test for countDisplayErrors
238
     */
239
    public function testCountDisplayErrorsForDisplayTrue(): void
240
    {
241
        self::assertSame(
242
            0,
243
            $this->object->countDisplayErrors(),
244
        );
245
    }
246
247
    /**
248
     * Test for countDisplayErrors
249
     */
250
    public function testCountDisplayErrorsForDisplayFalse(): void
251
    {
252
        self::assertSame(
253
            0,
254
            $this->object->countDisplayErrors(),
255
        );
256
    }
257
258
    /**
259
     * Test for hasDisplayErrors
260
     */
261
    public function testHasDisplayErrors(): void
262
    {
263
        self::assertFalse($this->object->hasDisplayErrors());
264
    }
265
266
    public function testHandleExceptionForDevEnv(): void
267
    {
268
        $GLOBALS['lang'] = 'en';
269
        Config::getInstance()->set('environment', 'development');
270
        $errorHandler = new ErrorHandler();
271
        self::assertSame([], $errorHandler->getCurrentErrors());
272
        try {
273
            $errorHandler->handleException(new Exception('Exception message.'));
274
        } catch (Throwable $throwable) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
275
        }
276
277
        self::assertInstanceOf(ExitException::class, $throwable ?? null);
278
        $output = $this->getActualOutputForAssertion();
279
        $errors = $errorHandler->getCurrentErrors();
280
        self::assertCount(1, $errors);
281
        $error = array_pop($errors);
282
        self::assertSame('Exception: Exception message.', $error->getOnlyMessage());
283
        self::assertStringContainsString($error->getDisplay(), $output);
284
        self::assertStringContainsString('Internal error', $output);
285
        self::assertStringContainsString('ErrorHandlerTest.php#' . $error->getLine(), $output);
286
        self::assertStringContainsString('Exception: Exception message.', $output);
287
    }
288
289
    public function testHandleExceptionForProdEnv(): void
290
    {
291
        $GLOBALS['lang'] = 'en';
292
        Config::getInstance()->set('environment', 'production');
293
        $errorHandler = new ErrorHandler();
294
        self::assertSame([], $errorHandler->getCurrentErrors());
295
        try {
296
            $errorHandler->handleException(new Exception('Exception message.'));
297
        } catch (Throwable $throwable) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
298
        }
299
300
        self::assertInstanceOf(ExitException::class, $throwable ?? null);
301
        $output = $this->getActualOutputForAssertion();
302
        $errors = $errorHandler->getCurrentErrors();
303
        self::assertCount(1, $errors);
304
        $error = array_pop($errors);
305
        self::assertSame('Exception: Exception message.', $error->getOnlyMessage());
306
        self::assertStringContainsString($error->getDisplay(), $output);
307
        self::assertStringContainsString('Exception: Exception message.', $output);
308
        self::assertStringNotContainsString('ErrorHandlerTest.php#' . $error->getLine(), $output);
309
    }
310
311
    public function testAddErrorWithFatalError(): void
312
    {
313
        $GLOBALS['lang'] = 'en';
314
        Config::getInstance()->set('environment', 'production');
315
        $errorHandler = new ErrorHandler();
316
        try {
317
            $errorHandler->addError('Fatal error message!', E_ERROR, './file/name', 1);
318
        } catch (Throwable $exception) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
319
        }
320
321
        self::assertInstanceOf(ExitException::class, $exception ?? null);
322
        // phpcs:disable Generic.Files.LineLength.TooLong
323
        $expectedStart = <<<'HTML'
324
<!DOCTYPE html>
325
<html lang="en">
326
<head><title>Error: Fatal error message!</title></head>
327
<body>
328
<div class="alert alert-danger" role="alert"><p><strong>Error</strong> in name#1</p><img src="themes/dot.gif" title="" alt="" class="icon ic_s_error"> Fatal error message!<p class="mt-3"><strong>Backtrace</strong></p><ol class="list-group"><li class="list-group-item">
329
HTML;
330
        // phpcs:enable
331
        $output = $this->getActualOutputForAssertion();
332
        self::assertStringStartsWith($expectedStart, $output);
333
        self::assertStringEndsWith('</li></ol></div>' . "\n\n" . '</body>' . "\n" . '</html>', $output);
334
    }
335
}
336