85 - Fix keyword matching to include title and description, add PHPStan type annotations

This commit is contained in:
myrmidex 2026-03-18 16:35:12 +01:00
parent f3406b1713
commit 5e571babda
6 changed files with 40 additions and 3 deletions

View file

@ -62,9 +62,12 @@ private function createRouteArticles(Article $article, string $content): void
->get()
->groupBy('platform_channel_id');
// Match keywords against full article content, title, and description
$searchableContent = $content.' '.$article->title.' '.$article->description;
foreach ($activeRoutes as $route) {
$routeKeywords = $keywordsByChannel->get($route->platform_channel_id, collect());
$status = $this->evaluateKeywords($routeKeywords, $content);
$status = $this->evaluateKeywords($routeKeywords, $searchableContent);
if ($status === ApprovalStatusEnum::PENDING && $this->shouldAutoApprove($route)) {
$status = ApprovalStatusEnum::APPROVED;

View file

@ -179,6 +179,7 @@ public function test_validate_article_listener_processes_new_article(): void
Setting::setBool('enable_publishing_approvals', false);
$feed = Feed::factory()->create();
/** @var \App\Models\Route $route */
$route = \App\Models\Route::factory()->active()->create(['feed_id' => $feed->id]);
\App\Models\Keyword::factory()->active()->create([
'feed_id' => $feed->id,

View file

@ -45,6 +45,7 @@ protected function tearDown(): void
public function test_listener_validates_article_and_creates_route_articles(): void
{
$feed = Feed::factory()->create();
/** @var Route $route */
$route = Route::factory()->active()->create(['feed_id' => $feed->id]);
Keyword::factory()->active()->create([
'feed_id' => $feed->id,

View file

@ -33,15 +33,23 @@ protected function setUp(): void
$this->notificationService = new NotificationService;
}
/**
* @param array<string, mixed> $articleOverrides
* @param array<string, mixed> $routeOverrides
*/
private function createApprovedRouteArticle(array $articleOverrides = [], array $routeOverrides = []): RouteArticle
{
$feed = Feed::factory()->create();
/** @var Route $route */
$route = Route::factory()->active()->create(array_merge(['feed_id' => $feed->id], $routeOverrides));
$article = Article::factory()->create(array_merge(['feed_id' => $feed->id], $articleOverrides));
return RouteArticle::factory()->forRoute($route)->approved()->create([
/** @var RouteArticle $routeArticle */
$routeArticle = RouteArticle::factory()->forRoute($route)->approved()->create([
'article_id' => $article->id,
]);
return $routeArticle;
}
public function test_constructor_sets_correct_queue(): void
@ -112,6 +120,7 @@ public function test_handle_returns_early_when_no_unpublished_approved_route_art
public function test_handle_skips_non_approved_route_articles(): void
{
$feed = Feed::factory()->create();
/** @var Route $route */
$route = Route::factory()->active()->create(['feed_id' => $feed->id]);
$article = Article::factory()->create(['feed_id' => $feed->id]);
@ -129,6 +138,7 @@ public function test_handle_skips_non_approved_route_articles(): void
public function test_handle_publishes_oldest_approved_route_article(): void
{
$feed = Feed::factory()->create();
/** @var Route $route */
$route = Route::factory()->active()->create(['feed_id' => $feed->id]);
$olderArticle = Article::factory()->create(['feed_id' => $feed->id]);

View file

@ -17,6 +17,7 @@ class RouteArticleTest extends TestCase
public function test_route_article_belongs_to_article(): void
{
/** @var RouteArticle $routeArticle */
$routeArticle = RouteArticle::factory()->create();
$this->assertInstanceOf(Article::class, $routeArticle->article);
@ -24,6 +25,7 @@ public function test_route_article_belongs_to_article(): void
public function test_route_article_belongs_to_feed(): void
{
/** @var RouteArticle $routeArticle */
$routeArticle = RouteArticle::factory()->create();
$this->assertInstanceOf(Feed::class, $routeArticle->feed);
@ -31,6 +33,7 @@ public function test_route_article_belongs_to_feed(): void
public function test_route_article_belongs_to_platform_channel(): void
{
/** @var RouteArticle $routeArticle */
$routeArticle = RouteArticle::factory()->create();
$this->assertInstanceOf(PlatformChannel::class, $routeArticle->platformChannel);
@ -38,6 +41,7 @@ public function test_route_article_belongs_to_platform_channel(): void
public function test_route_article_has_default_pending_status(): void
{
/** @var RouteArticle $routeArticle */
$routeArticle = RouteArticle::factory()->create();
$this->assertEquals(ApprovalStatusEnum::PENDING, $routeArticle->approval_status);
@ -48,15 +52,16 @@ public function test_route_article_has_default_pending_status(): void
public function test_route_article_can_be_approved(): void
{
/** @var RouteArticle $routeArticle */
$routeArticle = RouteArticle::factory()->create();
$routeArticle->approve();
$this->assertEquals(ApprovalStatusEnum::APPROVED, $routeArticle->fresh()->approval_status);
}
public function test_route_article_can_be_rejected(): void
{
/** @var RouteArticle $routeArticle */
$routeArticle = RouteArticle::factory()->create();
$routeArticle->reject();
@ -66,7 +71,9 @@ public function test_route_article_can_be_rejected(): void
public function test_article_has_many_route_articles(): void
{
/** @var Route $route1 */
$route1 = Route::factory()->active()->create();
/** @var Route $route2 */
$route2 = Route::factory()->active()->create();
$article = Article::factory()->create(['feed_id' => $route1->feed_id]);
@ -78,6 +85,7 @@ public function test_article_has_many_route_articles(): void
public function test_route_has_many_route_articles(): void
{
/** @var Route $route */
$route = Route::factory()->active()->create();
$article1 = Article::factory()->create(['feed_id' => $route->feed_id]);
$article2 = Article::factory()->create(['feed_id' => $route->feed_id]);
@ -90,6 +98,7 @@ public function test_route_has_many_route_articles(): void
public function test_unique_constraint_prevents_duplicate_route_articles(): void
{
/** @var Route $route */
$route = Route::factory()->active()->create();
$article = Article::factory()->create(['feed_id' => $route->feed_id]);
@ -102,6 +111,7 @@ public function test_unique_constraint_prevents_duplicate_route_articles(): void
public function test_route_article_cascade_deletes_when_article_deleted(): void
{
/** @var RouteArticle $routeArticle */
$routeArticle = RouteArticle::factory()->create();
$articleId = $routeArticle->article_id;
@ -112,6 +122,7 @@ public function test_route_article_cascade_deletes_when_article_deleted(): void
public function test_route_article_cascade_deletes_when_route_deleted(): void
{
/** @var Route $route */
$route = Route::factory()->active()->create();
$article = Article::factory()->create(['feed_id' => $route->feed_id]);
RouteArticle::factory()->forRoute($route)->create(['article_id' => $article->id]);
@ -128,8 +139,10 @@ public function test_route_article_cascade_deletes_when_route_deleted(): void
public function test_route_article_belongs_to_route(): void
{
/** @var Route $route */
$route = Route::factory()->active()->create();
$article = Article::factory()->create(['feed_id' => $route->feed_id]);
/** @var RouteArticle $routeArticle */
$routeArticle = RouteArticle::factory()->forRoute($route)->create(['article_id' => $article->id]);
$loadedRoute = $routeArticle->route;

View file

@ -60,6 +60,7 @@ private function mockFetchReturning(Article $article, ?string $content, ?string
public function test_validate_sets_validated_at_on_article(): void
{
$feed = Feed::factory()->create();
/** @var Route $route */
$route = Route::factory()->active()->create(['feed_id' => $feed->id]);
Keyword::factory()->active()->create([
'feed_id' => $feed->id,
@ -106,6 +107,7 @@ public function test_validate_skips_inactive_routes(): void
public function test_validate_sets_pending_when_keywords_match(): void
{
$feed = Feed::factory()->create();
/** @var Route $route */
$route = Route::factory()->active()->create(['feed_id' => $feed->id]);
Keyword::factory()->active()->create([
'feed_id' => $feed->id,
@ -125,6 +127,7 @@ public function test_validate_sets_pending_when_keywords_match(): void
public function test_validate_sets_rejected_when_no_keywords_match(): void
{
$feed = Feed::factory()->create();
/** @var Route $route */
$route = Route::factory()->active()->create(['feed_id' => $feed->id]);
Keyword::factory()->active()->create([
'feed_id' => $feed->id,
@ -200,6 +203,7 @@ public function test_validate_auto_approves_when_global_setting_off_and_keywords
Setting::setBool('enable_publishing_approvals', false);
$feed = Feed::factory()->create();
/** @var Route $route */
$route = Route::factory()->active()->create(['feed_id' => $feed->id]);
Keyword::factory()->active()->create([
'feed_id' => $feed->id,
@ -221,6 +225,7 @@ public function test_validate_route_auto_approve_overrides_global_setting(): voi
Setting::setBool('enable_publishing_approvals', true);
$feed = Feed::factory()->create();
/** @var Route $route */
$route = Route::factory()->active()->create([
'feed_id' => $feed->id,
'auto_approve' => true,
@ -245,6 +250,7 @@ public function test_validate_route_auto_approve_false_overrides_global_off(): v
Setting::setBool('enable_publishing_approvals', false);
$feed = Feed::factory()->create();
/** @var Route $route */
$route = Route::factory()->active()->create([
'feed_id' => $feed->id,
'auto_approve' => false,
@ -269,6 +275,7 @@ public function test_validate_does_not_auto_approve_rejected_articles(): void
Setting::setBool('enable_publishing_approvals', false);
$feed = Feed::factory()->create();
/** @var Route $route */
$route = Route::factory()->active()->create(['feed_id' => $feed->id]);
Keyword::factory()->active()->create([
'feed_id' => $feed->id,
@ -334,6 +341,7 @@ public function test_validate_sets_validated_at_on_route_articles(): void
public function test_validate_keyword_matching_is_case_insensitive(): void
{
$feed = Feed::factory()->create();
/** @var Route $route */
$route = Route::factory()->active()->create(['feed_id' => $feed->id]);
Keyword::factory()->active()->create([
'feed_id' => $feed->id,
@ -353,6 +361,7 @@ public function test_validate_keyword_matching_is_case_insensitive(): void
public function test_validate_only_uses_active_keywords(): void
{
$feed = Feed::factory()->create();
/** @var Route $route */
$route = Route::factory()->active()->create(['feed_id' => $feed->id]);
Keyword::factory()->inactive()->create([
'feed_id' => $feed->id,