2
2
3
3
declare (strict_types=1 );
4
4
5
- namespace KaririCode \Logging \Tests \KaririCode \ Logging \ Handler ;
5
+ namespace KaririCode \Logging \Tests \Handler ;
6
6
7
- use KaririCode \Contract \ImmutableValue ;
8
7
use KaririCode \Logging \Exception \LoggingException ;
9
8
use KaririCode \Logging \Formatter \LineFormatter ;
10
9
use KaririCode \Logging \Handler \FileHandler ;
11
10
use KaririCode \Logging \LogLevel ;
11
+ use KaririCode \Logging \LogRecord ;
12
12
use PHPUnit \Framework \TestCase ;
13
13
14
14
class FileHandlerTest extends TestCase
15
15
{
16
16
private string $ testLogDir ;
17
17
private string $ testLogFile ;
18
+ private array $ mockFunctions = [];
18
19
19
20
protected function setUp (): void
20
21
{
21
- $ this ->testLogDir = sys_get_temp_dir () . '/test_logs ' ;
22
+ $ this ->testLogDir = sys_get_temp_dir () . '/test_logs_ ' . uniqid ();
23
+ mkdir ($ this ->testLogDir );
22
24
$ this ->testLogFile = $ this ->testLogDir . '/test.log ' ;
23
25
24
26
if (file_exists ($ this ->testLogFile )) {
@@ -31,12 +33,19 @@ protected function setUp(): void
31
33
32
34
protected function tearDown (): void
33
35
{
34
- if (file_exists ($ this ->testLogFile )) {
35
- unlink ($ this ->testLogFile );
36
+ $ this ->removeDirectory ($ this ->testLogDir );
37
+ }
38
+
39
+ private function removeDirectory (string $ dir ): void
40
+ {
41
+ if (!is_dir ($ dir )) {
42
+ return ;
36
43
}
37
- if (is_dir ($ this ->testLogDir )) {
38
- rmdir ($ this ->testLogDir );
44
+ $ files = array_diff (scandir ($ dir ), ['. ' , '.. ' ]);
45
+ foreach ($ files as $ file ) {
46
+ (is_dir ("$ dir/ $ file " )) ? $ this ->removeDirectory ("$ dir/ $ file " ) : unlink ("$ dir/ $ file " );
39
47
}
48
+ rmdir ($ dir );
40
49
}
41
50
42
51
public function testConstructorCreatesDirectory (): void
@@ -48,25 +57,67 @@ public function testConstructorCreatesDirectory(): void
48
57
public function testConstructorThrowsExceptionOnInvalidPath (): void
49
58
{
50
59
$ this ->expectException (LoggingException::class);
60
+ $ this ->expectExceptionMessage ('Unable to create log directory ' );
61
+
62
+ $ invalidPath = '/path/that/cant/be/created/ ' . uniqid ();
63
+
64
+ $ mockFileHandler = $ this ->getMockBuilder (FileHandler::class)
65
+ ->setConstructorArgs ([$ invalidPath . '/test.log ' ])
66
+ ->onlyMethods (['createDirectory ' ])
67
+ ->getMock ();
68
+
69
+ $ mockFileHandler ->expects ($ this ->once ())
70
+ ->method ('createDirectory ' )
71
+ ->willReturn (false );
72
+
73
+ /** @var AbstractFileHandlerc $mockFileHandler */
74
+ $ mockFileHandler ->__construct ($ invalidPath . '/test.log ' );
75
+ }
76
+
77
+ public function testConstructorThrowsExceptionOnNonWritableDirectory (): void
78
+ {
79
+ $ this ->expectException (LoggingException::class);
80
+ $ this ->expectExceptionMessage ('Log directory is not writable ' );
51
81
52
- // Suppress warnings for this test
53
- set_error_handler (function ($ errno , $ errstr , $ errfile , $ errline ) {
54
- // Don't throw E_WARNING, E_NOTICE, or E_USER_WARNING errors
55
- return true ;
56
- });
57
-
58
- try {
59
- new FileHandler ('/invalid/path/test.log ' );
60
- } finally {
61
- // Restore the original error handler
62
- restore_error_handler ();
82
+ $ nonWritableDir = sys_get_temp_dir () . '/non_writable_dir_ ' . uniqid ();
83
+ mkdir ($ nonWritableDir );
84
+
85
+ $ mockFileHandler = $ this ->getMockBuilder (FileHandler::class)
86
+ ->setConstructorArgs ([$ nonWritableDir . '/test.log ' ])
87
+ ->onlyMethods (['isDirectoryWritable ' ])
88
+ ->getMock ();
89
+
90
+ $ mockFileHandler ->expects ($ this ->once ())
91
+ ->method ('isDirectoryWritable ' )
92
+ ->willReturn (false );
93
+ /** @var AbstractFileHandlerc $mockFileHandler */
94
+ $ mockFileHandler ->__construct ($ nonWritableDir . '/test.log ' );
95
+
96
+ $ this ->removeDirectory ($ nonWritableDir );
97
+ }
98
+
99
+ protected function createDirectory ($ path )
100
+ {
101
+ if (isset ($ this ->mockFunctions ['mkdir ' ])) {
102
+ return call_user_func ($ this ->mockFunctions ['mkdir ' ], $ path );
103
+ }
104
+
105
+ return mkdir ($ path , 0777 , true );
106
+ }
107
+
108
+ protected function isDirectoryWritable ($ path )
109
+ {
110
+ if (isset ($ this ->mockFunctions ['is_writable ' ])) {
111
+ return call_user_func ($ this ->mockFunctions ['is_writable ' ], $ path );
63
112
}
113
+
114
+ return is_writable ($ path );
64
115
}
65
116
66
117
public function testHandleWritesToFile (): void
67
118
{
68
119
$ handler = new FileHandler ($ this ->testLogFile );
69
- $ record = $ this -> createMockRecord ( 'Test message ' , LogLevel:: INFO );
120
+ $ record = new LogRecord (LogLevel:: INFO , 'Test message ' );
70
121
71
122
$ handler ->handle ($ record );
72
123
@@ -78,8 +129,8 @@ public function testHandleWritesToFile(): void
78
129
public function testHandleRespectsMinimumLogLevel (): void
79
130
{
80
131
$ handler = new FileHandler ($ this ->testLogFile , LogLevel::WARNING );
81
- $ debugRecord = $ this -> createMockRecord ( 'Debug message ' , LogLevel:: DEBUG );
82
- $ warningRecord = $ this -> createMockRecord ( 'Warning message ' , LogLevel:: WARNING );
132
+ $ debugRecord = new LogRecord (LogLevel:: DEBUG , 'Debug message ' );
133
+ $ warningRecord = new LogRecord (LogLevel:: WARNING , 'Warning message ' );
83
134
84
135
$ handler ->handle ($ debugRecord );
85
136
$ handler ->handle ($ warningRecord );
@@ -99,7 +150,7 @@ public function testHandleUsesFormatter(): void
99
150
$ handler = new FileHandler ($ this ->testLogFile );
100
151
$ handler ->setFormatter ($ mockFormatter );
101
152
102
- $ record = $ this -> createMockRecord ( 'Test message ' , LogLevel:: INFO );
153
+ $ record = new LogRecord (LogLevel:: INFO , 'Test message ' );
103
154
$ handler ->handle ($ record );
104
155
105
156
$ content = file_get_contents ($ this ->testLogFile );
@@ -120,28 +171,10 @@ public function testDestructorClosesFileHandle(): void
120
171
$ this ->assertFalse (is_resource ($ fileHandleProperty ->getValue ($ handler )));
121
172
}
122
173
123
- private function createMockRecord ( string $ message , LogLevel $ level ): ImmutableValue
174
+ public function testLogFileCreatedWithCorrectPermissions ( ): void
124
175
{
125
- return new class ($ message , $ level ) implements ImmutableValue {
126
- public function __construct (
127
- public readonly string $ message ,
128
- public readonly LogLevel $ level ,
129
- public readonly array $ context = [],
130
- public readonly array $ extra = [],
131
- public readonly \DateTimeImmutable $ datetime = new \DateTimeImmutable ()
132
- ) {
133
- }
134
-
135
- public function toArray (): array
136
- {
137
- return [
138
- 'message ' => $ this ->message ,
139
- 'level ' => $ this ->level ,
140
- 'context ' => $ this ->context ,
141
- 'extra ' => $ this ->extra ,
142
- 'datetime ' => $ this ->datetime ,
143
- ];
144
- }
145
- };
176
+ new FileHandler ($ this ->testLogFile );
177
+ $ this ->assertFileExists ($ this ->testLogFile );
178
+ $ this ->assertEquals ('0644 ' , substr (sprintf ('%o ' , fileperms ($ this ->testLogFile )), -4 ));
146
179
}
147
180
}
0 commit comments