109 lines
3.8 KiB
PHP
109 lines
3.8 KiB
PHP
<?php
|
|
|
|
namespace Tests\Feature;
|
|
|
|
use App\Livewire\Services\Index;
|
|
use App\Models\Server;
|
|
use App\Models\User;
|
|
use App\Services\FleetService;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Livewire\Livewire;
|
|
use Mockery;
|
|
use Tests\TestCase;
|
|
|
|
/**
|
|
* The journal is "live" via an incremental poll: each tick appends entries AFTER the
|
|
* stored cursor (no gaps), advances the cursor, and caps the retained rows.
|
|
*/
|
|
class ServicesJournalPollTest extends TestCase
|
|
{
|
|
use RefreshDatabase;
|
|
|
|
protected function tearDown(): void
|
|
{
|
|
Mockery::close();
|
|
parent::tearDown();
|
|
}
|
|
|
|
private function activeServer(): Server
|
|
{
|
|
$server = Server::create(['name' => 'box', 'ip' => '10.0.0.2', 'ssh_port' => 22, 'status' => 'online']);
|
|
$server->credential()->create(['username' => 'root', 'auth_type' => 'password', 'secret' => 'x']);
|
|
session(['active_server_id' => $server->id]);
|
|
|
|
return $server;
|
|
}
|
|
|
|
public function test_poll_appends_new_entries_and_advances_the_cursor(): void
|
|
{
|
|
$this->actingAs(User::factory()->create());
|
|
$this->activeServer();
|
|
|
|
$fleet = Mockery::mock(FleetService::class);
|
|
$fleet->shouldReceive('journalSince')
|
|
->once()
|
|
->with(Mockery::type(Server::class), 'cur-0', 300)
|
|
->andReturn([
|
|
'journal' => [['time' => '10:00:05', 'unit' => 'nginx', 'level' => 'info', 'text' => 'new line']],
|
|
'cursor' => 'cur-1',
|
|
]);
|
|
app()->instance(FleetService::class, $fleet);
|
|
|
|
Livewire::test(Index::class)
|
|
->set('ready', true)
|
|
->set('connected', true)
|
|
->set('journalCursor', 'cur-0')
|
|
->set('journal', [['time' => '10:00:01', 'unit' => 'sshd', 'level' => 'info', 'text' => 'old line']])
|
|
->call('pollJournal')
|
|
->assertSet('journalCursor', 'cur-1')
|
|
->assertSet('journal', [
|
|
['time' => '10:00:01', 'unit' => 'sshd', 'level' => 'info', 'text' => 'old line'],
|
|
['time' => '10:00:05', 'unit' => 'nginx', 'level' => 'info', 'text' => 'new line'],
|
|
]);
|
|
}
|
|
|
|
public function test_poll_caps_retained_rows_at_the_ceiling(): void
|
|
{
|
|
$this->actingAs(User::factory()->create());
|
|
$this->activeServer();
|
|
|
|
$fresh = array_map(
|
|
fn (int $i) => ['time' => '10:00:'.$i, 'unit' => 'u', 'level' => 'info', 'text' => 'n'.$i],
|
|
range(1, 350),
|
|
);
|
|
|
|
$fleet = Mockery::mock(FleetService::class);
|
|
$fleet->shouldReceive('journalSince')->once()->andReturn(['journal' => $fresh, 'cursor' => 'c']);
|
|
app()->instance(FleetService::class, $fleet);
|
|
|
|
$component = Livewire::test(Index::class)
|
|
->set('ready', true)
|
|
->set('connected', true)
|
|
->set('journal', [['time' => '09:59:59', 'unit' => 'old', 'level' => 'info', 'text' => 'oldest']])
|
|
->call('pollJournal');
|
|
|
|
$journal = $component->get('journal');
|
|
$this->assertCount(300, $journal);
|
|
// The oldest row is evicted; the newest survives.
|
|
$this->assertSame('n350', end($journal)['text']);
|
|
$this->assertNotSame('oldest', $journal[0]['text']);
|
|
}
|
|
|
|
public function test_poll_is_a_noop_until_connected(): void
|
|
{
|
|
$this->actingAs(User::factory()->create());
|
|
$this->activeServer();
|
|
|
|
$fleet = Mockery::mock(FleetService::class);
|
|
$fleet->shouldNotReceive('journalSince');
|
|
app()->instance(FleetService::class, $fleet);
|
|
|
|
Livewire::test(Index::class)
|
|
->set('ready', true)
|
|
->set('connected', false)
|
|
->set('journal', [['time' => 't', 'unit' => 'u', 'level' => 'info', 'text' => 'unchanged']])
|
|
->call('pollJournal')
|
|
->assertSet('journal', [['time' => 't', 'unit' => 'u', 'level' => 'info', 'text' => 'unchanged']]);
|
|
}
|
|
}
|