Sorry to bump this again, but to finish my thought, here is my working test:
// 10 attempts, 5 minute cooldown
Route::group(['middleware' => ['guest', 'throttle:10,5']], function () {});
To actually test it, you need to be aware of this:
When making multiple requests in one test, the state of your laravel application is not reset between the requests. The Auth manager is a singleton in the laravel container, and it keeps a local cache of the resolved auth guards. The resolved auth guards keep a local cache of the authed user.
So, your first request to your api/logout endpoint resolves the auth manager, which resolves the api guard, which stores a references to the authed user whose token you will be revoking.
Now, when you make your second request to /api/user, the already resolved auth manager is pulled from the container, the already resolved api guard is pulled from it's local cache, and the same already resolved user is pulled from the guard's local cache. This is why the second request passes authentication instead of failing it.
When testing auth related stuff with multiple requests in the same test, you need to reset the resolved instances between tests. Also, you can't just unset the resolved auth manager instance.
Source: https://stackoverflow.com/questions/57813795/method-illuminate-auth-requestguardlogout-does-not-exist-laravel-passport
To accomplish that, you can add this:
TestCase.php
use Illuminate\Auth\SessionGuard;
...
protected function resetAuth(array $guards = null) : void
{
$guards = $guards ?: array_keys(config('auth.guards'));
foreach ($guards as $guard) {
$guard = $this->app['auth']->guard($guard);
if ($guard instanceof SessionGuard) {
$guard->logout();
}
}
$protectedProperty = new \ReflectionProperty($this->app['auth'], 'guards');
$protectedProperty->setAccessible(true);
$protectedProperty->setValue($this->app['auth'], []);
}
Then, you can access it via $this->resetAuth(); in any unit test. By my estimates, this is a miracle utility function for testing edge cases.
Then you can simply test throttling like this:
/** @test */
public function it_should_throw_error_429_when_login_attempt_is_throttled()
{
$this->resetAuth();
$throttledUser = factory(User::class, 1)->create()->first();
foreach (range(0, 9) as $attempt) {
$this->postJson(route('login'), ['email' => $throttledUser->email, 'password' => "{TestCase::AUTH_PASSWORD}_{$attempt}"]);
}
$this->postJson(route('login'), ['email' => $throttledUser->email, 'password' => 'k'])
->assertStatus(429)
->assertJson(['message' => 'Too Many Attempts.']);
$this->resetAuth();
}
And for the record I have no real issue with that in_array middleware test shown above. I figure we can look at the meme: "why not both.gif". If any change causes anything related to the auth unit tests to fail, it probably warrants any brittleness or extra keyboard time to confirm the logic is correct.
But my key opinion is that we want to test-to-confirm the behaviour is seen.