Skip to content

Commit 8f4a3f6

Browse files
committed
test: Test unitaire de la couche Debug (Logger; Timer, Iterator, ExceptionManager)
1 parent eb7fda0 commit 8f4a3f6

9 files changed

Lines changed: 1107 additions & 22 deletions

File tree

Lines changed: 324 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,324 @@
1+
<?php
2+
3+
/**
4+
* This file is part of Blitz PHP framework.
5+
*
6+
* (c) 2022 Dimitri Sitchet Tomkeu <devcode.dst@gmail.com>
7+
*
8+
* For the full copyright and license information, please view
9+
* the LICENSE file that was distributed with this source code.
10+
*/
11+
12+
use BlitzPHP\Container\Services;
13+
use BlitzPHP\Debug\ExceptionManager;
14+
use BlitzPHP\Exceptions\HttpException;
15+
use BlitzPHP\Exceptions\TokenMismatchException;
16+
use BlitzPHP\Filesystem\Filesystem;
17+
use BlitzPHP\Utilities\Reflection\ReflectionClass;
18+
use Kahlan\Plugin\Double;
19+
use Psr\Http\Message\RequestInterface;
20+
use Whoops\Handler\Handler;
21+
use Whoops\Handler\HandlerInterface;
22+
use Whoops\Run;
23+
24+
use function Kahlan\allow;
25+
use function Kahlan\expect;
26+
27+
describe('Debug / ExceptionManager', function (): void {
28+
beforeAll(function (): void {
29+
$this->exceptionsConfig = config('exceptions');
30+
});
31+
beforeEach(function (): void {
32+
config()->set('exceptions', [
33+
'log' => true,
34+
'ignore_codes' => [404],
35+
'error_view_path' => '/path/to/views',
36+
'handlers' => [],
37+
'editor' => 'vscode',
38+
'title' => 'Error',
39+
'blacklist' => [],
40+
'data' => [],
41+
]);
42+
43+
$mockFs = Double::instance([
44+
'extends' => Filesystem::class,
45+
'stubMethods' => [
46+
'files' => [],
47+
'directories' => [],
48+
],
49+
]);
50+
Services::injectMock('fs', $mockFs);
51+
52+
is_online(true);
53+
});
54+
55+
afterEach(function (): void {
56+
config()->set('exceptions', $this->exceptionsConfig);
57+
Services::resetSingle('fs');
58+
});
59+
60+
afterAll(function (): void {
61+
is_online(newReturn: false);
62+
});
63+
64+
it('initialise le debugger si Whoops est disponible', function (): void {
65+
expect(function (): void {
66+
$manager = new ExceptionManager();
67+
expect($manager)->toBeAnInstanceOf(ExceptionManager::class);
68+
})->not->toThrow();
69+
});
70+
71+
it('ne plante pas si Whoops n\'est pas installé', function (): void {
72+
// Simuler l'absence de Whoops
73+
allow('class_exists')->toBeCalled()->with(Run::class)->andReturn(false);
74+
75+
expect(function (): void {
76+
$manager = new ExceptionManager();
77+
$manager->register(); // Ne devrait pas lancer d'exception
78+
})->not->toThrow();
79+
});
80+
81+
it('enregistre les gestionnaires callable', function (): void {
82+
$handlerCalled = false;
83+
$handler = function () use (&$handlerCalled) {
84+
$handlerCalled = true;
85+
return Handler::DONE;
86+
};
87+
88+
config()->set('exceptions', [
89+
'log' => true,
90+
'ignore_codes' => [],
91+
'error_view_path' => '/path/to/views',
92+
'handlers' => [$handler],
93+
'editor' => 'vscode',
94+
'title' => 'Error',
95+
'blacklist' => [],
96+
'data' => [],
97+
]);
98+
99+
$manager = new ExceptionManager();
100+
101+
// On ne peut pas tester directement l'enregistrement, mais on peut vérifier
102+
// que la méthode ne lance pas d'exception
103+
expect(function () use ($manager): void {
104+
$manager->register();
105+
})->not->toThrow();
106+
});
107+
108+
it('enregistre les gestionnaires de classe', function (): void {
109+
$mockHandler = Double::instance([
110+
'implements' => [HandlerInterface::class],
111+
]);
112+
113+
config()->set('exceptions', [
114+
'log' => true,
115+
'ignore_codes' => [],
116+
'error_view_path' => '/path/to/views',
117+
'handlers' => [get_class($mockHandler)],
118+
'editor' => 'vscode',
119+
'title' => 'Error',
120+
'blacklist' => [],
121+
'data' => [],
122+
]);
123+
124+
$manager = new ExceptionManager();
125+
126+
expect(function () use ($manager): void {
127+
$manager->register();
128+
})->not->toThrow();
129+
});
130+
131+
it('prépare TokenMismatchException en HttpException 419', function (): void {
132+
$tokenException = new TokenMismatchException('Token mismatch');
133+
134+
// Utilisation de la réflexion pour tester la méthode privée
135+
$manager = new ExceptionManager();
136+
$reflection = new ReflectionClass($manager);
137+
$result = $reflection->invoke('prepareException', $tokenException);
138+
139+
expect($result)->toBeAnInstanceOf(HttpException::class);
140+
expect($result->getCode())->toBe(419);
141+
expect($result->getMessage())->toBe('Token mismatch');
142+
expect($result->getPrevious())->toBe($tokenException);
143+
});
144+
145+
it('ne modifie pas les autres exceptions', function (): void {
146+
$originalException = new RuntimeException('Test exception');
147+
148+
$manager = new ExceptionManager();
149+
$reflection = new ReflectionClass($manager);
150+
$result = $reflection->invoke('prepareException', $originalException);
151+
152+
expect($result)->toBe($originalException);
153+
});
154+
155+
xit('récupère les chemins d\'application correctement', function (): void {
156+
$mockFs = Double::instance([
157+
'extends' => Filesystem::class,
158+
'stubMethods' => [
159+
'directories' => [
160+
'/app/controllers',
161+
'/app/models',
162+
'/app/vendor',
163+
'/app/config',
164+
],
165+
]
166+
]);
167+
168+
Services::injectMock('fs', $mockFs);
169+
170+
$manager = new ExceptionManager();
171+
$reflection = new ReflectionClass($manager);
172+
173+
$result = $reflection->invoke('getApplicationPaths');
174+
175+
expect($result)->toContain('/app/controllers');
176+
expect($result)->toContain('/app/models');
177+
expect($result)->toContain('/app/config');
178+
expect($result)->not->toContain('/app/vendor');
179+
180+
Services::resetSingle('fs');
181+
});
182+
183+
it('gère les environnements CLI', function (): void {
184+
allow('Misc::isCommandLine')->toBeCalled()->andReturn(true);
185+
186+
$manager = new ExceptionManager();
187+
188+
expect(function () use ($manager): void {
189+
$manager->register();
190+
})->not->toThrow();
191+
});
192+
193+
it('gère les environnements de production', function (): void {
194+
is_online(true);
195+
allow('Misc::isCommandLine')->toBeCalled()->andReturn(false);
196+
197+
$manager = new ExceptionManager();
198+
199+
expect(function () use ($manager): void {
200+
$manager->register();
201+
})->not->toThrow();
202+
});
203+
204+
it('gère les requêtes AJAX/JSON', function (): void {
205+
allow('Misc::isCommandLine')->toBeCalled()->andReturn(false);
206+
is_online(false);
207+
allow('Misc::isAjaxRequest')->toBeCalled()->andReturn(true);
208+
209+
$mockRequest = Double::instance([
210+
'implements' => [RequestInterface::class],
211+
'stubMethods' => [
212+
'isJson' => false,
213+
],
214+
]);
215+
Services::injectMock('request', $mockRequest);
216+
217+
$manager = new ExceptionManager();
218+
219+
expect(function () use ($manager): void {
220+
$manager->register();
221+
})->not->toThrow();
222+
223+
Services::resetSingle('request');
224+
});
225+
226+
it('configure correctement la liste noire', function (): void {
227+
config()->set('exceptions', [
228+
'log' => true,
229+
'ignore_codes' => [],
230+
'error_view_path' => '/path/to/views',
231+
'handlers' => [],
232+
'editor' => 'vscode',
233+
'title' => 'Error',
234+
'blacklist' => ['post/password', 'get/api_key'],
235+
'data' => [],
236+
]);
237+
238+
// Simuler des données POST
239+
$_POST['password'] = 'secret';
240+
$_GET['api_key'] = '12345';
241+
242+
$manager = new ExceptionManager();
243+
244+
expect(function () use ($manager): void {
245+
$manager->register();
246+
})->not->toThrow();
247+
248+
// Nettoyer les variables globales
249+
unset($_POST['password'], $_GET['api_key']);
250+
});
251+
252+
describe('registerHttpErrorsHandler', function (): void {
253+
xit('enregistre un gestionnaire pour les erreurs HTTP', function (): void {
254+
$mockFs = Double::instance([
255+
'extends' => Filesystem::class,
256+
'stubMethods' => [
257+
'files' => [
258+
Double::instance([
259+
'extends' => SplFileInfo::class,
260+
'stubMethods' => [
261+
'getFilenameWithoutExtension' => '404',
262+
]
263+
]),
264+
Double::instance([
265+
'extends' => SplFileInfo::class,
266+
'stubMethods' => [
267+
'getFilenameWithoutExtension' => '500',
268+
]
269+
]),
270+
],
271+
],
272+
]);
273+
274+
Services::injectMock('fs', $mockFs);
275+
is_online(newReturn: false);
276+
277+
$manager = new ExceptionManager();
278+
279+
expect(function () use ($manager): void {
280+
$reflection = new ReflectionClass($manager);
281+
$reflection->invoke('registerHttpErrorsHandler');
282+
})->not->toThrow();
283+
});
284+
285+
it('ne journalise pas les codes ignorés', function (): void {
286+
config()->set('exceptions', [
287+
'log' => true,
288+
'ignore_codes' => [404],
289+
'error_view_path' => '/path/to/views',
290+
'handlers' => [],
291+
'editor' => 'vscode',
292+
'title' => 'Error',
293+
'blacklist' => [],
294+
'data' => [],
295+
]);
296+
297+
$manager = new ExceptionManager();
298+
299+
// Vérifier que le code 404 est dans la liste des codes ignorés
300+
expect(function () use ($manager): void {
301+
$manager->register();
302+
})->not->toThrow();
303+
});
304+
305+
it('ne journalise pas si log est false', function (): void {
306+
config()->set('exceptions', [
307+
'log' => false,
308+
'ignore_codes' => [],
309+
'error_view_path' => '/path/to/views',
310+
'handlers' => [],
311+
'editor' => 'vscode',
312+
'title' => 'Error',
313+
'blacklist' => [],
314+
'data' => [],
315+
]);
316+
317+
$manager = new ExceptionManager();
318+
319+
expect(function () use ($manager): void {
320+
$manager->register();
321+
})->not->toThrow();
322+
});
323+
});
324+
});

0 commit comments

Comments
 (0)