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()); } }