216 lines
6.6 KiB
PHP
216 lines
6.6 KiB
PHP
|
|
<?php
|
||
|
|
|
||
|
|
namespace Tests\Unit\Actions;
|
||
|
|
|
||
|
|
use App\Actions\CreateRouteArticlesAction;
|
||
|
|
use App\Enums\ApprovalStatusEnum;
|
||
|
|
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\Setting;
|
||
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||
|
|
use Tests\TestCase;
|
||
|
|
|
||
|
|
class CreateRouteArticlesActionTest extends TestCase
|
||
|
|
{
|
||
|
|
use RefreshDatabase;
|
||
|
|
|
||
|
|
private function action(): CreateRouteArticlesAction
|
||
|
|
{
|
||
|
|
return new CreateRouteArticlesAction;
|
||
|
|
}
|
||
|
|
|
||
|
|
private function route(bool $isActive = true, ?bool $autoApprove = null): Route
|
||
|
|
{
|
||
|
|
$feed = Feed::factory()->create();
|
||
|
|
$channel = PlatformChannel::factory()->create();
|
||
|
|
|
||
|
|
return Route::create([
|
||
|
|
'feed_id' => $feed->id,
|
||
|
|
'platform_channel_id' => $channel->id,
|
||
|
|
'priority' => 50,
|
||
|
|
'is_active' => $isActive,
|
||
|
|
'auto_approve' => $autoApprove,
|
||
|
|
]);
|
||
|
|
}
|
||
|
|
|
||
|
|
private function articleFor(Route $route): Article
|
||
|
|
{
|
||
|
|
return Article::factory()->create([
|
||
|
|
'feed_id' => $route->feed_id,
|
||
|
|
'title' => 'A title',
|
||
|
|
'description' => 'A description',
|
||
|
|
]);
|
||
|
|
}
|
||
|
|
|
||
|
|
public function test_it_creates_a_route_article_per_active_route(): void
|
||
|
|
{
|
||
|
|
$route = $this->route();
|
||
|
|
$article = $this->articleFor($route);
|
||
|
|
|
||
|
|
$this->action()->execute($article, 'body content');
|
||
|
|
|
||
|
|
$this->assertDatabaseHas('route_articles', [
|
||
|
|
'article_id' => $article->id,
|
||
|
|
'feed_id' => $route->feed_id,
|
||
|
|
'platform_channel_id' => $route->platform_channel_id,
|
||
|
|
]);
|
||
|
|
}
|
||
|
|
|
||
|
|
public function test_it_skips_inactive_routes(): void
|
||
|
|
{
|
||
|
|
$route = $this->route(isActive: false);
|
||
|
|
$article = $this->articleFor($route);
|
||
|
|
|
||
|
|
$this->action()->execute($article, 'body content');
|
||
|
|
|
||
|
|
$this->assertSame(0, RouteArticle::count());
|
||
|
|
}
|
||
|
|
|
||
|
|
public function test_a_route_without_keywords_is_pending(): void
|
||
|
|
{
|
||
|
|
Setting::setBool('enable_publishing_approvals', true);
|
||
|
|
$route = $this->route();
|
||
|
|
$article = $this->articleFor($route);
|
||
|
|
|
||
|
|
$this->action()->execute($article, 'body content');
|
||
|
|
|
||
|
|
$this->assertSame(ApprovalStatusEnum::PENDING, RouteArticle::first()->approval_status);
|
||
|
|
}
|
||
|
|
|
||
|
|
public function test_a_matching_keyword_leaves_it_pending(): void
|
||
|
|
{
|
||
|
|
Setting::setBool('enable_publishing_approvals', true);
|
||
|
|
$route = $this->route();
|
||
|
|
$article = $this->articleFor($route);
|
||
|
|
|
||
|
|
Keyword::create([
|
||
|
|
'feed_id' => $route->feed_id,
|
||
|
|
'platform_channel_id' => $route->platform_channel_id,
|
||
|
|
'keyword' => 'brussels',
|
||
|
|
'is_active' => true,
|
||
|
|
]);
|
||
|
|
|
||
|
|
$this->action()->execute($article, 'news from Brussels today');
|
||
|
|
|
||
|
|
$this->assertSame(ApprovalStatusEnum::PENDING, RouteArticle::first()->approval_status);
|
||
|
|
}
|
||
|
|
|
||
|
|
public function test_a_non_matching_keyword_rejects(): void
|
||
|
|
{
|
||
|
|
$route = $this->route();
|
||
|
|
$article = $this->articleFor($route);
|
||
|
|
|
||
|
|
Keyword::create([
|
||
|
|
'feed_id' => $route->feed_id,
|
||
|
|
'platform_channel_id' => $route->platform_channel_id,
|
||
|
|
'keyword' => 'antwerp',
|
||
|
|
'is_active' => true,
|
||
|
|
]);
|
||
|
|
|
||
|
|
$this->action()->execute($article, 'news from Brussels today');
|
||
|
|
|
||
|
|
$this->assertSame(ApprovalStatusEnum::REJECTED, RouteArticle::first()->approval_status);
|
||
|
|
}
|
||
|
|
|
||
|
|
public function test_keyword_matching_is_case_insensitive(): void
|
||
|
|
{
|
||
|
|
Setting::setBool('enable_publishing_approvals', true);
|
||
|
|
$route = $this->route();
|
||
|
|
$article = $this->articleFor($route);
|
||
|
|
|
||
|
|
Keyword::create([
|
||
|
|
'feed_id' => $route->feed_id,
|
||
|
|
'platform_channel_id' => $route->platform_channel_id,
|
||
|
|
'keyword' => 'BRUSSELS',
|
||
|
|
'is_active' => true,
|
||
|
|
]);
|
||
|
|
|
||
|
|
$this->action()->execute($article, 'news from brussels today');
|
||
|
|
|
||
|
|
$this->assertSame(ApprovalStatusEnum::PENDING, RouteArticle::first()->approval_status);
|
||
|
|
}
|
||
|
|
|
||
|
|
public function test_inactive_keywords_are_ignored(): void
|
||
|
|
{
|
||
|
|
$route = $this->route();
|
||
|
|
$article = $this->articleFor($route);
|
||
|
|
|
||
|
|
Keyword::create([
|
||
|
|
'feed_id' => $route->feed_id,
|
||
|
|
'platform_channel_id' => $route->platform_channel_id,
|
||
|
|
'keyword' => 'antwerp',
|
||
|
|
'is_active' => false,
|
||
|
|
]);
|
||
|
|
|
||
|
|
Setting::setBool('enable_publishing_approvals', true);
|
||
|
|
|
||
|
|
$this->action()->execute($article, 'news from Brussels today');
|
||
|
|
|
||
|
|
$this->assertSame(ApprovalStatusEnum::PENDING, RouteArticle::first()->approval_status);
|
||
|
|
}
|
||
|
|
|
||
|
|
public function test_the_route_auto_approve_flag_overrides_the_global_setting(): void
|
||
|
|
{
|
||
|
|
Setting::setBool('enable_publishing_approvals', true);
|
||
|
|
$route = $this->route(autoApprove: true);
|
||
|
|
$article = $this->articleFor($route);
|
||
|
|
|
||
|
|
$this->action()->execute($article, 'body content');
|
||
|
|
|
||
|
|
$this->assertSame(ApprovalStatusEnum::APPROVED, RouteArticle::first()->approval_status);
|
||
|
|
}
|
||
|
|
|
||
|
|
public function test_a_rejected_article_is_never_auto_approved(): void
|
||
|
|
{
|
||
|
|
$route = $this->route(autoApprove: true);
|
||
|
|
$article = $this->articleFor($route);
|
||
|
|
|
||
|
|
Keyword::create([
|
||
|
|
'feed_id' => $route->feed_id,
|
||
|
|
'platform_channel_id' => $route->platform_channel_id,
|
||
|
|
'keyword' => 'antwerp',
|
||
|
|
'is_active' => true,
|
||
|
|
]);
|
||
|
|
|
||
|
|
$this->action()->execute($article, 'news from Brussels today');
|
||
|
|
|
||
|
|
$this->assertSame(ApprovalStatusEnum::REJECTED, RouteArticle::first()->approval_status);
|
||
|
|
}
|
||
|
|
|
||
|
|
public function test_an_approved_route_article_is_stamped_with_a_decision_time(): void
|
||
|
|
{
|
||
|
|
$route = $this->route(autoApprove: true);
|
||
|
|
$article = $this->articleFor($route);
|
||
|
|
|
||
|
|
$this->action()->execute($article, 'body content');
|
||
|
|
|
||
|
|
$this->assertNotNull(RouteArticle::first()->decided_at);
|
||
|
|
}
|
||
|
|
|
||
|
|
public function test_a_pending_route_article_has_no_decision_time(): void
|
||
|
|
{
|
||
|
|
Setting::setBool('enable_publishing_approvals', true);
|
||
|
|
$route = $this->route();
|
||
|
|
$article = $this->articleFor($route);
|
||
|
|
|
||
|
|
$this->action()->execute($article, 'body content');
|
||
|
|
|
||
|
|
$this->assertNull(RouteArticle::first()->decided_at);
|
||
|
|
}
|
||
|
|
|
||
|
|
public function test_running_twice_does_not_duplicate_route_articles(): void
|
||
|
|
{
|
||
|
|
$route = $this->route();
|
||
|
|
$article = $this->articleFor($route);
|
||
|
|
|
||
|
|
$this->action()->execute($article, 'body content');
|
||
|
|
$this->action()->execute($article, 'body content');
|
||
|
|
|
||
|
|
$this->assertSame(1, RouteArticle::count());
|
||
|
|
}
|
||
|
|
}
|