artisan('bad-ip-blocker:add', ['ip' => '203.0.113.10', '--reason' => 'bot', '--ttl' => 60]) ->assertSuccessful(); expect(BadIpBlocker::all()['203.0.113.10']) ->reason->toBe('bot') ->expires_at->not->toBeNull(); }); it('rejects an invalid ip or ttl', function () { $this->artisan('bad-ip-blocker:add', ['ip' => 'nope'])->assertFailed(); $this->artisan('bad-ip-blocker:add', ['ip' => '203.0.113.10', '--ttl' => 'abc'])->assertFailed(); expect(BadIpBlocker::all())->toBe([]); }); it('removes an ip', function () { BadIpBlocker::block('203.0.113.10'); $this->artisan('bad-ip-blocker:remove', ['ip' => '203.0.113.10'])->assertSuccessful(); $this->artisan('bad-ip-blocker:remove', ['ip' => '203.0.113.10'])->assertFailed(); }); it('lists ips', function () { $this->artisan('bad-ip-blocker:list')->expectsOutputToContain('No blocked')->assertSuccessful(); BadIpBlocker::block('203.0.113.10', 'bot'); $blockedAt = BadIpBlocker::all()['203.0.113.10']['blocked_at']; $this->artisan('bad-ip-blocker:list') ->expectsTable(['IP', 'Reason', 'Blocked at', 'Expires at'], [['203.0.113.10', 'bot', $blockedAt, 'never']]) ->assertSuccessful(); }); it('prunes expired blocks', function () { $this->artisan('bad-ip-blocker:prune')->assertSuccessful(); }); it('clears all blocks after confirmation', function () { BadIpBlocker::block('203.0.113.10'); $this->artisan('bad-ip-blocker:clear') ->expectsConfirmation('Remove all blocked IP addresses?', 'no') ->assertFailed(); expect(BadIpBlocker::all())->not->toBe([]); $this->artisan('bad-ip-blocker:clear', ['--force' => true])->assertSuccessful(); expect(BadIpBlocker::all())->toBe([]); });