fedi-feed-router/tests/Feature/ActivityRecordingTest.php

126 lines
3.9 KiB
PHP
Raw Normal View History

2026-08-10 20:46:07 +02:00
<?php
namespace Tests\Feature;
use App\Enums\ActivityTypeEnum;
use App\Enums\ApprovalStatusEnum;
use App\Events\ActivityLogged;
use App\Events\RouteArticleApproved;
use App\Livewire\Articles;
use App\Models\ActivityLog;
use App\Models\Article;
use App\Models\Feed;
use App\Models\Route;
use App\Models\RouteArticle;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Event;
use Livewire\Livewire;
use Tests\TestCase;
class ActivityRecordingTest extends TestCase
{
use RefreshDatabase;
private function pendingRouteArticle(): RouteArticle
{
$feed = Feed::factory()->create();
/** @var Route $route */
$route = Route::factory()->active()->create(['feed_id' => $feed->id]);
$article = Article::factory()->create(['feed_id' => $feed->id, 'title' => 'A Headline']);
/** @var RouteArticle $routeArticle */
$routeArticle = RouteArticle::factory()->forRoute($route)->create([
'article_id' => $article->id,
'approval_status' => ApprovalStatusEnum::PENDING,
'validated_at' => now(),
]);
return $routeArticle;
}
public function test_approving_records_activity(): void
{
Event::fake([RouteArticleApproved::class]);
$this->pendingRouteArticle()->approve();
$log = ActivityLog::ofType(ActivityTypeEnum::APPROVE)->first();
$this->assertNotNull($log);
$this->assertStringContainsString('A Headline', $log->message);
$this->assertSame(Article::class, $log->subject_type);
}
public function test_rejecting_records_activity(): void
{
$this->pendingRouteArticle()->reject();
$log = ActivityLog::ofType(ActivityTypeEnum::REJECT)->first();
$this->assertNotNull($log);
$this->assertStringContainsString('A Headline', $log->message);
}
public function test_approving_an_already_approved_article_records_nothing(): void
{
Event::fake([RouteArticleApproved::class]);
$routeArticle = $this->pendingRouteArticle();
$routeArticle->approve();
$routeArticle->approve();
$this->assertSame(1, ActivityLog::ofType(ActivityTypeEnum::APPROVE)->count());
}
public function test_rejecting_an_already_rejected_article_records_nothing(): void
{
$routeArticle = $this->pendingRouteArticle();
$routeArticle->reject();
$routeArticle->reject();
$this->assertSame(1, ActivityLog::ofType(ActivityTypeEnum::REJECT)->count());
}
public function test_clearing_pending_articles_records_one_summary_entry(): void
{
$routeArticle = $this->pendingRouteArticle();
$this->pendingRouteArticle();
Livewire::test(Articles::class)->call('clear');
$entries = ActivityLog::ofType(ActivityTypeEnum::REJECT)->get();
$this->assertCount(1, $entries);
$this->assertStringContainsString('2 pending articles', $entries->first()->message);
$this->assertSame(ApprovalStatusEnum::REJECTED, $routeArticle->fresh()->approval_status);
}
public function test_clearing_nothing_records_no_entry(): void
{
Livewire::test(Articles::class)->call('clear');
$this->assertSame(0, ActivityLog::count());
}
public function test_one_event_records_exactly_one_entry(): void
{
ActivityLogged::dispatch(ActivityTypeEnum::FETCH, 'Single entry');
$this->assertSame(1, ActivityLog::where('message', 'Single entry')->count());
}
public function test_a_long_message_is_truncated_to_fit_the_column(): void
{
ActivityLogged::dispatch(ActivityTypeEnum::PUBLISH, str_repeat('a', 400));
$this->assertSame(255, strlen(ActivityLog::first()->message));
}
public function test_an_empty_context_is_stored_as_null(): void
{
ActivityLogged::dispatch(ActivityTypeEnum::FETCH, 'No context');
$this->assertNull(ActivityLog::first()->context);
}
}