trove/packages/Lvl0/FediDiscover/tests/Feature/PollInstancesCommandTest.php

150 lines
4.5 KiB
PHP

<?php
declare(strict_types=1);
namespace Lvl0\FediDiscover\Tests\Feature;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Lvl0\FediDiscover\Actions\PollFediverseAction;
use Lvl0\FediDiscover\Clients\FediverseClientInterface;
use Lvl0\FediDiscover\Config\InstanceType;
use Lvl0\FediDiscover\Models\Instance;
use Mockery;
use RuntimeException;
use Tests\TestCase;
class PollInstancesCommandTest extends TestCase
{
use RefreshDatabase;
protected function setUp(): void
{
parent::setUp();
// Bind a no-op stub so the command can resolve PollFediverseAction
// from the container without making real HTTP calls.
$stub = Mockery::mock(FediverseClientInterface::class);
$stub->shouldReceive('fetchPostsSince')->andReturn(collect());
$this->app->bind(FediverseClientInterface::class, fn () => $stub);
}
public function test_it_exits_zero_when_there_are_no_enabled_instances(): void
{
$this->artisan('fedi-discover:poll')
->assertExitCode(0);
}
public function test_it_calls_the_action_for_each_enabled_instance_and_skips_disabled(): void
{
$enabled1 = Instance::create([
'type' => InstanceType::Mastodon,
'url' => 'https://mastodon.social',
'enabled' => true,
'interval_seconds' => 600,
]);
$enabled2 = Instance::create([
'type' => InstanceType::Mastodon,
'url' => 'https://fosstodon.org',
'enabled' => true,
'interval_seconds' => 600,
]);
Instance::create([
'type' => InstanceType::Mastodon,
'url' => 'https://disabled.example',
'enabled' => false,
'interval_seconds' => 600,
]);
$calledWith = [];
$action = Mockery::mock(PollFediverseAction::class);
$action->shouldReceive('execute')
->twice()
->withArgs(function (Instance $instance) use (&$calledWith): bool {
$calledWith[] = $instance->url;
return true;
});
$this->app->instance(PollFediverseAction::class, $action);
$this->artisan('fedi-discover:poll')->assertExitCode(0);
$this->assertEqualsCanonicalizing(
[$enabled1->url, $enabled2->url],
$calledWith,
);
}
public function test_one_instance_throwing_does_not_stop_remaining_instances_from_being_polled(): void
{
Instance::create([
'type' => InstanceType::Mastodon,
'url' => 'https://failing.example',
'enabled' => true,
'interval_seconds' => 600,
]);
$healthy = Instance::create([
'type' => InstanceType::Mastodon,
'url' => 'https://healthy.example',
'enabled' => true,
'interval_seconds' => 600,
]);
$calledWith = [];
$action = Mockery::mock(PollFediverseAction::class);
$action->shouldReceive('execute')
->twice()
->andReturnUsing(function (Instance $instance) use (&$calledWith): void {
$calledWith[] = $instance->url;
if ($instance->url === 'https://failing.example') {
throw new RuntimeException('Connection refused');
}
});
$this->app->instance(PollFediverseAction::class, $action);
$this->artisan('fedi-discover:poll')->assertExitCode(1);
$this->assertEqualsCanonicalizing(
['https://failing.example', $healthy->url],
$calledWith,
);
}
public function test_it_exits_one_when_at_least_one_instance_fails(): void
{
Instance::create([
'type' => InstanceType::Mastodon,
'url' => 'https://failing.example',
'enabled' => true,
'interval_seconds' => 600,
]);
Instance::create([
'type' => InstanceType::Mastodon,
'url' => 'https://healthy.example',
'enabled' => true,
'interval_seconds' => 600,
]);
$action = Mockery::mock(PollFediverseAction::class);
$action->shouldReceive('execute')
->twice()
->andReturnUsing(function (Instance $instance): void {
if ($instance->url === 'https://failing.example') {
throw new RuntimeException('Connection refused');
}
});
$this->app->instance(PollFediverseAction::class, $action);
$this->artisan('fedi-discover:poll')->assertExitCode(1);
}
}