create(); $block = app(BlockAddress::class)->forInstance($instance, '203.0.113.7', 12); expect($block)->not->toBeNull() // diffInMinutes ist SIGNED (Carbon 3) — wie App\Models\Incident und // Host::healthState() schon halten es hier: früher.diffInX(später) // für ein positives Ergebnis, nicht wortwörtlich wie im Auftragszettel. ->and(now()->diffInMinutes($block->expires_at))->toBeGreaterThan(55) ->and(now()->diffInMinutes($block->expires_at))->toBeLessThan(65) ->and($block->strikes)->toBe(1) ->and($block->attempts)->toBe(12); }); it('verdoppelt bei Wiederholung und haelt bei 24 Stunden an', function () { $instance = Instance::factory()->create(); $dienst = app(BlockAddress::class); $dauern = []; for ($i = 0; $i < 7; $i++) { $block = $dienst->forInstance($instance, '203.0.113.7', 10); $dauern[] = (int) round($block->blocked_at->diffInHours($block->expires_at)); $block->release(null); // freigegeben, aber der Zähler bleibt } expect($dauern)->toBe([1, 2, 4, 8, 16, 24, 24]); }); it('faengt nach 24 Stunden ohne Vorfall wieder bei einer Stunde an', function () { $instance = Instance::factory()->create(); $dienst = app(BlockAddress::class); $dienst->forInstance($instance, '203.0.113.7', 10)->release(null); Carbon::setTestNow(now()->addHours(25)); $zweiter = $dienst->forInstance($instance, '203.0.113.7', 10); expect($zweiter->strikes)->toBe(1); }); it('sperrt NIEMALS eine Adresse aus dem Verwaltungsnetz', function () { // Eine Sperrliste, die sich selbst aussperren kann, ist eine Falle: über // genau dieses Netz erreicht CluPilot den Host. $host = Host::factory()->create(); expect(app(BlockAddress::class)->forHost($host, '10.66.0.1', 999))->toBeNull() ->and(app(BlockAddress::class)->forHost($host, '127.0.0.1', 999))->toBeNull() ->and(app(BlockAddress::class)->forHost($host, '::1', 999))->toBeNull() ->and(SecurityBlock::count())->toBe(0); }); it('haelt eine laufende Sperre nicht zweimal', function () { $instance = Instance::factory()->create(); $dienst = app(BlockAddress::class); $dienst->forInstance($instance, '203.0.113.7', 10); expect($dienst->forInstance($instance, '203.0.113.7', 10))->toBeNull() ->and(SecurityBlock::count())->toBe(1); }); it('sperrt niemals die eigene oeffentliche Adresse des CluPilot-Servers', function () { // Der vierte Eintrag der Ausnahmeliste, und der einzige, der nicht hart // verdrahtet ist. Ohne diesen Test bliebe er unbelegt — ausgerechnet der, // der verhindert, dass sich der Server selbst aussperrt. config()->set('provisioning.wireguard.endpoint', '203.0.113.10:51820'); $host = Host::factory()->create(); expect(app(BlockAddress::class)->forHost($host, '203.0.113.10', 999))->toBeNull() ->and(SecurityBlock::count())->toBe(0); // Und die Gegenprobe, damit der Test nicht bloss beweist, dass gar nichts // gesperrt wird: der Nachbar in derselben Zeile ist NICHT ausgenommen. expect(app(BlockAddress::class)->forHost($host, '203.0.113.11', 999))->not->toBeNull(); });