fedi-feed-router/tests/Feature/Jobs/CheckPlatformCredentialsJobTest.php

163 lines
5.1 KiB
PHP

<?php
namespace Tests\Feature\Jobs;
use App\Enums\AccountStatusEnum;
use App\Enums\NotificationTypeEnum;
use App\Jobs\CheckPlatformCredentialsJob;
use App\Models\Notification;
use App\Models\PlatformAccount;
use App\Modules\Lemmy\Services\LemmyApiService;
use App\Services\Notification\NotificationService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Mockery;
use Tests\TestCase;
class CheckPlatformCredentialsJobTest extends TestCase
{
use RefreshDatabase;
private function runJob(bool $loginSucceeds): void
{
$api = Mockery::mock(LemmyApiService::class);
$api->shouldReceive('login')->andReturn($loginSucceeds ? 'a-token' : null);
$job = new class($api) extends CheckPlatformCredentialsJob
{
public function __construct(private LemmyApiService $api) {}
protected function makeApiService(PlatformAccount $account): LemmyApiService
{
return $this->api;
}
};
$job->handle(app(NotificationService::class));
}
/**
* @param array<string, mixed> $attributes
*/
private function account(array $attributes = []): PlatformAccount
{
return PlatformAccount::factory()->create(array_merge(['is_active' => true], $attributes));
}
public function test_a_successful_login_marks_the_account_healthy(): void
{
$account = $this->account(['status' => AccountStatusEnum::UNTESTED]);
$this->runJob(true);
$account->refresh();
$this->assertSame(AccountStatusEnum::HEALTHY, $account->status);
$this->assertSame(0, $account->consecutive_failures);
$this->assertNotNull($account->last_tested_at);
}
public function test_a_successful_login_clears_earlier_failures(): void
{
$account = $this->account(['status' => AccountStatusEnum::UNHEALTHY, 'consecutive_failures' => 2]);
$this->runJob(true);
$account->refresh();
$this->assertSame(AccountStatusEnum::HEALTHY, $account->status);
$this->assertSame(0, $account->consecutive_failures);
}
public function test_a_single_failure_does_not_mark_the_account_unhealthy(): void
{
$account = $this->account(['status' => AccountStatusEnum::HEALTHY]);
$this->runJob(false);
$account->refresh();
$this->assertSame(AccountStatusEnum::HEALTHY, $account->status);
$this->assertSame(1, $account->consecutive_failures);
}
public function test_the_threshold_failure_marks_the_account_unhealthy(): void
{
$account = $this->account([
'status' => AccountStatusEnum::HEALTHY,
'consecutive_failures' => PlatformAccount::FAILURES_BEFORE_UNHEALTHY - 1,
]);
$this->runJob(false);
$this->assertSame(AccountStatusEnum::UNHEALTHY, $account->refresh()->status);
}
public function test_a_single_failure_does_not_notify(): void
{
$this->account(['status' => AccountStatusEnum::HEALTHY]);
$this->runJob(false);
$this->assertDatabaseCount('notifications', 0);
}
public function test_becoming_unhealthy_notifies(): void
{
$account = $this->account([
'status' => AccountStatusEnum::HEALTHY,
'consecutive_failures' => PlatformAccount::FAILURES_BEFORE_UNHEALTHY - 1,
'username' => 'newsbot',
]);
$this->runJob(false);
$this->assertDatabaseHas('notifications', [
'type' => NotificationTypeEnum::CREDENTIAL_EXPIRED->value,
'notifiable_type' => $account->getMorphClass(),
'notifiable_id' => $account->id,
]);
$this->assertStringContainsString('newsbot', Notification::first()->title);
}
public function test_an_account_already_unhealthy_does_not_notify_again(): void
{
$this->account([
'status' => AccountStatusEnum::UNHEALTHY,
'consecutive_failures' => PlatformAccount::FAILURES_BEFORE_UNHEALTHY,
]);
$this->runJob(false);
$this->assertDatabaseCount('notifications', 0);
}
public function test_inactive_accounts_are_not_checked(): void
{
$account = $this->account(['is_active' => false, 'status' => AccountStatusEnum::UNTESTED]);
$this->runJob(false);
$account->refresh();
$this->assertSame(AccountStatusEnum::UNTESTED, $account->status);
$this->assertNull($account->last_tested_at);
}
public function test_an_exception_during_login_counts_as_a_failure(): void
{
$account = $this->account(['status' => AccountStatusEnum::HEALTHY]);
$api = Mockery::mock(LemmyApiService::class);
$api->shouldReceive('login')->andThrow(new \RuntimeException('instance unreachable'));
$job = new class($api) extends CheckPlatformCredentialsJob
{
public function __construct(private LemmyApiService $api) {}
protected function makeApiService(PlatformAccount $account): LemmyApiService
{
return $this->api;
}
};
$job->handle(app(NotificationService::class));
$this->assertSame(1, $account->refresh()->consecutive_failures);
}
}