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

176 lines
5.5 KiB
PHP
Raw Normal View History

<?php
namespace Tests\Feature;
use App\Actions\CreateRouteArticlesAction;
use App\Enums\ApprovalStatusEnum;
use App\Livewire\Articles;
use App\Models\Article;
use App\Models\Feed;
use App\Models\Keyword;
use App\Models\PlatformChannel;
use App\Models\Route;
use App\Models\RouteArticle;
use App\Models\User;
use App\Services\Article\ArticleFetcher;
use App\Services\Article\ValidationService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Livewire\Livewire;
use Mockery;
use Tests\TestCase;
class RouteArticleDecisionStampingTest extends TestCase
{
use RefreshDatabase;
public function test_clearing_pending_articles_stamps_every_rejected_row(): void
{
$pending = RouteArticle::factory()->count(3)->create([
'approval_status' => ApprovalStatusEnum::PENDING,
'decided_at' => null,
]);
Livewire::test(Articles::class)->call('clear');
foreach ($pending as $routeArticle) {
/** @var RouteArticle $fresh */
$fresh = $routeArticle->fresh();
$this->assertNotNull($fresh->decided_at);
}
}
public function test_the_api_clear_endpoint_stamps_every_rejected_row(): void
{
$pending = RouteArticle::factory()->count(3)->create([
'approval_status' => ApprovalStatusEnum::PENDING,
'decided_at' => null,
]);
$this->actingAs(User::factory()->create())
->postJson('/api/v1/route-articles/clear')
->assertOk();
foreach ($pending as $routeArticle) {
/** @var RouteArticle $fresh */
$fresh = $routeArticle->fresh();
$this->assertNotNull($fresh->decided_at);
}
}
public function test_restoring_to_pending_clears_the_decision_time(): void
{
/** @var RouteArticle $routeArticle */
$routeArticle = RouteArticle::factory()->create([
'approval_status' => ApprovalStatusEnum::REJECTED,
'decided_at' => now(),
]);
Livewire::test(Articles::class)->call('restore', $routeArticle->id);
$this->assertNull($routeArticle->fresh()->decided_at);
}
public function test_the_api_restore_endpoint_clears_the_decision_time(): void
{
/** @var RouteArticle $routeArticle */
$routeArticle = RouteArticle::factory()->create([
'approval_status' => ApprovalStatusEnum::REJECTED,
'decided_at' => now(),
]);
$this->actingAs(User::factory()->create())
->postJson("/api/v1/route-articles/{$routeArticle->id}/restore")
->assertOk();
$this->assertNull($routeArticle->fresh()->decided_at);
}
public function test_auto_approved_articles_are_stamped_on_creation(): void
{
$article = $this->articleOnRouteWithoutKeywords(autoApprove: true);
$this->validate($article);
$routeArticle = RouteArticle::where('article_id', $article->id)->firstOrFail();
$this->assertSame(ApprovalStatusEnum::APPROVED, $routeArticle->approval_status);
$this->assertNotNull($routeArticle->decided_at);
}
public function test_keyword_rejected_articles_are_stamped_on_creation(): void
{
$article = $this->articleOnRouteWithKeyword('nothing-that-matches');
$this->validate($article);
$routeArticle = RouteArticle::where('article_id', $article->id)->firstOrFail();
$this->assertSame(ApprovalStatusEnum::REJECTED, $routeArticle->approval_status);
$this->assertNotNull($routeArticle->decided_at);
}
public function test_pending_articles_are_not_stamped_on_creation(): void
{
$article = $this->articleOnRouteWithoutKeywords(autoApprove: false);
$this->validate($article);
$routeArticle = RouteArticle::where('article_id', $article->id)->firstOrFail();
$this->assertSame(ApprovalStatusEnum::PENDING, $routeArticle->approval_status);
$this->assertNull($routeArticle->decided_at);
}
private function validate(Article $article): void
{
$fetcher = Mockery::mock(ArticleFetcher::class);
$fetcher->shouldReceive('fetchArticleData')
->with($article)
->once()
->andReturn([
'title' => 'Test Title',
'description' => 'Test description',
'full_article' => 'Body text',
]);
(new ValidationService($fetcher, new CreateRouteArticlesAction))->validate($article);
}
private function articleOnRouteWithoutKeywords(bool $autoApprove): Article
{
$feed = Feed::factory()->create();
$channel = PlatformChannel::factory()->create();
Route::factory()->create([
'feed_id' => $feed->id,
'platform_channel_id' => $channel->id,
'is_active' => true,
'auto_approve' => $autoApprove,
]);
return Article::factory()->create(['feed_id' => $feed->id, 'validated_at' => null]);
}
private function articleOnRouteWithKeyword(string $keyword): Article
{
$feed = Feed::factory()->create();
$channel = PlatformChannel::factory()->create();
Route::factory()->create([
'feed_id' => $feed->id,
'platform_channel_id' => $channel->id,
'is_active' => true,
]);
Keyword::factory()->active()->create([
'feed_id' => $feed->id,
'platform_channel_id' => $channel->id,
'keyword' => $keyword,
]);
return Article::factory()->create(['feed_id' => $feed->id, 'validated_at' => null]);
}
}