|
13 | 13 | $this->loadLaravelMigrations(); |
14 | 14 | $this->artisan('migrate', ['--database' => 'testing'])->run(); |
15 | 15 | Cache::flush(); |
| 16 | + |
| 17 | + // Reset Carbon time to ensure test isolation (prevents time mocking from leaking between tests) |
| 18 | + \Illuminate\Support\Carbon::setTestNow(); |
16 | 19 | }); |
17 | 20 |
|
18 | 21 | it('sends suspicious activity notification when enabled and multiple failed logins detected', function () { |
|
149 | 152 | 'authentication-log.suspicious.usual_hours' => [9, 10, 11, 12, 13, 14, 15, 16, 17], |
150 | 153 | ]); |
151 | 154 |
|
| 155 | + // Set current time to 3 AM (outside usual hours) FIRST, before creating user/logs |
| 156 | + $testTime = \Illuminate\Support\Carbon::create(2024, 1, 1, 3, 0, 0); |
| 157 | + \Illuminate\Support\Carbon::setTestNow($testTime); |
| 158 | + |
152 | 159 | $user = TestUser::factory()->create([ |
153 | | - 'created_at' => now()->subMinutes(10), |
| 160 | + 'created_at' => $testTime->copy()->subMinutes(10), |
154 | 161 | ]); |
155 | 162 |
|
156 | 163 | // Set request values and generate device ID |
|
160 | 167 | request()->headers->set('User-Agent', $sameUserAgent); |
161 | 168 | $deviceId = \Rappasoft\LaravelAuthenticationLog\Helpers\DeviceFingerprint::generate(request()); |
162 | 169 |
|
163 | | - // Create a previous successful login so device is recognized as known |
| 170 | + // Create a previous successful login so device is recognized as known (30 minutes before test time) |
164 | 171 | AuthenticationLog::factory()->create([ |
165 | 172 | 'authenticatable_type' => get_class($user), |
166 | 173 | 'authenticatable_id' => $user->id, |
167 | 174 | 'login_successful' => true, |
168 | | - 'login_at' => now()->subMinutes(30), |
| 175 | + 'login_at' => $testTime->copy()->subMinutes(30), |
169 | 176 | 'ip_address' => $sameIp, |
170 | 177 | 'user_agent' => $sameUserAgent, |
171 | 178 | 'device_id' => $deviceId, |
172 | 179 | ]); |
173 | 180 |
|
174 | | - // Set current time to 3 AM (outside usual hours) |
175 | | - $originalNow = now(); |
176 | | - \Illuminate\Support\Carbon::setTestNow(now()->setHour(3)->setMinute(0)); |
177 | | - |
178 | 181 | Event::dispatch(new Login('web', $user, false)); |
179 | 182 |
|
180 | 183 | Notification::assertSentTo($user, SuspiciousActivity::class, function ($notification) { |
|
183 | 186 | }); |
184 | 187 |
|
185 | 188 | // Reset time |
186 | | - \Illuminate\Support\Carbon::setTestNow($originalNow); |
| 189 | + \Illuminate\Support\Carbon::setTestNow(); |
187 | 190 | }); |
188 | 191 |
|
189 | 192 | it('does not send notification for unusual login times when check is disabled', function () { |
|
200 | 203 | 'created_at' => now()->subMinutes(10), |
201 | 204 | ]); |
202 | 205 |
|
| 206 | + // Set current time to 3 AM (outside usual hours) FIRST, before creating logs |
| 207 | + $testTime = \Illuminate\Support\Carbon::create(2024, 1, 1, 3, 0, 0); |
| 208 | + \Illuminate\Support\Carbon::setTestNow($testTime); |
| 209 | + |
203 | 210 | // Set request values and generate device ID |
204 | 211 | $sameIp = '192.168.1.1'; |
205 | 212 | $sameUserAgent = 'Test Browser'; |
206 | 213 | request()->server->set('REMOTE_ADDR', $sameIp); |
207 | 214 | request()->headers->set('User-Agent', $sameUserAgent); |
208 | 215 | $deviceId = \Rappasoft\LaravelAuthenticationLog\Helpers\DeviceFingerprint::generate(request()); |
209 | 216 |
|
210 | | - // Create a previous successful login so device is recognized as known |
| 217 | + // Create a previous successful login so device is recognized as known (30 minutes before test time) |
211 | 218 | AuthenticationLog::factory()->create([ |
212 | 219 | 'authenticatable_type' => get_class($user), |
213 | 220 | 'authenticatable_id' => $user->id, |
214 | 221 | 'login_successful' => true, |
215 | | - 'login_at' => now()->subMinutes(30), |
| 222 | + 'login_at' => $testTime->copy()->subMinutes(30), |
216 | 223 | 'ip_address' => $sameIp, |
217 | 224 | 'user_agent' => $sameUserAgent, |
218 | 225 | 'device_id' => $deviceId, |
219 | 226 | ]); |
220 | 227 |
|
221 | | - // Set current time to 3 AM (outside usual hours) |
222 | | - $originalNow = now(); |
223 | | - \Illuminate\Support\Carbon::setTestNow(now()->setHour(3)->setMinute(0)); |
224 | | - |
225 | 228 | Event::dispatch(new Login('web', $user, false)); |
226 | 229 |
|
227 | 230 | Notification::assertNothingSent(); |
228 | 231 |
|
229 | 232 | // Reset time |
230 | | - \Illuminate\Support\Carbon::setTestNow($originalNow); |
| 233 | + \Illuminate\Support\Carbon::setTestNow(); |
231 | 234 | }); |
232 | 235 |
|
233 | 236 | it('respects rate limiting for suspicious activity notifications', function () { |
|
460 | 463 | ]); |
461 | 464 |
|
462 | 465 | // Set time to unusual hour |
463 | | - $originalNow = now(); |
464 | 466 | \Illuminate\Support\Carbon::setTestNow(now()->setHour(3)->setMinute(0)); |
465 | 467 |
|
466 | 468 | // Trigger login which should detect multiple suspicious activities |
|
476 | 478 | }); |
477 | 479 |
|
478 | 480 | // Reset time |
479 | | - \Illuminate\Support\Carbon::setTestNow($originalNow); |
| 481 | + \Illuminate\Support\Carbon::setTestNow(); |
480 | 482 | }); |
0 commit comments