clusev/tests/Feature/HealthComponentTest.php

127 lines
3.9 KiB
PHP

<?php
namespace Tests\Feature;
use App\Livewire\Health\Index;
use App\Models\HealthCheck;
use App\Models\User;
use App\Services\HealthService;
use App\Support\Confirm\ConfirmToken;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Livewire\Livewire;
use Mockery;
use Tests\TestCase;
class HealthComponentTest extends TestCase
{
use RefreshDatabase;
protected function tearDown(): void
{
Mockery::close();
parent::tearDown();
}
private function operator(): User
{
return User::factory()->operator()->create(['must_change_password' => false]);
}
private function viewer(): User
{
return User::factory()->viewer()->create(['must_change_password' => false]);
}
public function test_viewer_cannot_open_health(): void
{
$this->actingAs($this->viewer())->get('/health')->assertForbidden();
}
public function test_operator_can_open_health(): void
{
$this->actingAs($this->operator())->get('/health')->assertOk();
}
public function test_operator_adds_an_http_check_and_it_is_audited(): void
{
$this->actingAs($this->operator());
Livewire::test(Index::class)
->set('type', 'http')
->set('target', 'https://svc.example.com/health')
->call('addCheck')
->assertHasNoErrors();
$this->assertDatabaseHas('health_checks', ['type' => 'http', 'target' => 'https://svc.example.com/health', 'port' => null]);
$this->assertDatabaseHas('audit_events', ['action' => 'health.check_add']);
}
public function test_http_check_rejects_a_non_url(): void
{
$this->actingAs($this->operator());
Livewire::test(Index::class)
->set('type', 'http')
->set('target', 'not a url; rm -rf')
->call('addCheck')
->assertHasErrors('target');
$this->assertDatabaseCount('health_checks', 0);
}
public function test_tcp_check_requires_a_host_and_port(): void
{
$this->actingAs($this->operator());
Livewire::test(Index::class)
->set('type', 'tcp')
->set('target', 'db.example.com')
->set('port', 5432)
->call('addCheck')
->assertHasNoErrors();
$this->assertDatabaseHas('health_checks', ['type' => 'tcp', 'target' => 'db.example.com', 'port' => 5432]);
}
public function test_tcp_check_rejects_a_missing_port(): void
{
$this->actingAs($this->operator());
Livewire::test(Index::class)
->set('type', 'tcp')
->set('target', 'db.example.com')
->set('port', null)
->call('addCheck')
->assertHasErrors('port');
}
public function test_scan_probes_each_check(): void
{
$this->actingAs($this->operator());
HealthCheck::create(['type' => 'http', 'target' => 'https://svc.example.com']);
$health = Mockery::mock(HealthService::class);
$health->shouldReceive('probe')->once()->andReturn(['ok' => true, 'latency' => 42, 'detail' => 'HTTP 200']);
app()->instance(HealthService::class, $health);
Livewire::test(Index::class)
->call('scan')
->assertOk()
->assertSet('ready', true)
->assertSee(__('health.status_up'))
->assertViewHas('up', 1);
}
public function test_operator_deletes_a_check_via_a_confirmed_token(): void
{
$this->actingAs($this->operator());
$check = HealthCheck::create(['type' => 'http', 'target' => 'https://gone.example.com']);
$token = ConfirmToken::issue('healthCheckDeleted', ['uuid' => $check->uuid], 'health.check_delete', $check->target, null);
ConfirmToken::confirm($token);
Livewire::test(Index::class)->call('deleteCheck', $token)->assertOk();
$this->assertDatabaseMissing('health_checks', ['id' => $check->id]);
}
}