extractUrls('Visit https://spam.example.com and www.short.test/offer now.'); expect($urls)->toContain('https://spam.example.com') ->and($urls)->toContain('www.short.test/offer'); }); it('detects suspicious keywords and domains with a queued status', function (): void { $result = app(ContentModerationService::class)->analyze( 'Buy followers today at https://promo.pornsite.com right now', ['content_type' => 'artwork_comment', 'content_id' => 1] ); expect($result->score)->toBeGreaterThanOrEqual(110) ->and(in_array($result->severity, [ModerationSeverity::High, ModerationSeverity::Critical], true))->toBeTrue() ->and($result->matchedDomains)->toContain('promo.pornsite.com') ->and($result->matchedKeywords)->toContain('buy followers'); }); it('detects unicode obfuscation patterns', function (): void { $result = app(ContentModerationService::class)->analyze( 'раypal giveaway click here', ['content_type' => 'artwork_comment', 'content_id' => 1] ); expect($result->score)->toBeGreaterThan(0) ->and(collect($result->reasons)->implode(' '))->toContain('Unicode'); }); it('produces stable hashes for semantically identical whitespace variants', function (): void { $service = app(ContentModerationService::class); $first = $service->analyze("Visit my site\n\nnow", ['content_type' => 'artwork_comment', 'content_id' => 1]); $second = $service->analyze(' visit my site now ', ['content_type' => 'artwork_comment', 'content_id' => 2]); expect($first->contentHash)->toBe($second->contentHash); }); it('detects keyword stuffing patterns', function (): void { $result = app(ContentModerationService::class)->analyze( 'seo seo seo seo seo seo seo seo seo seo seo seo seo service service service service service cheap cheap cheap traffic traffic traffic', ['content_type' => 'artwork_description', 'content_id' => 1] ); expect($result->score)->toBeGreaterThan(0) ->and($result->matchedKeywords)->toContain('seo'); }); it('maps score thresholds to the expected severities', function (): void { expect(ModerationSeverity::fromScore(0))->toBe(ModerationSeverity::Low) ->and(ModerationSeverity::fromScore(30))->toBe(ModerationSeverity::Medium) ->and(ModerationSeverity::fromScore(60))->toBe(ModerationSeverity::High) ->and(ModerationSeverity::fromScore(90))->toBe(ModerationSeverity::Critical); }); it('applies db managed moderation rules alongside config rules', function (): void { ContentModerationRule::query()->create([ 'type' => ModerationRuleType::SuspiciousKeyword, 'value' => 'rare promo blast', 'enabled' => true, 'weight' => 22, ]); $result = app(ContentModerationService::class)->analyze( 'This rare promo blast just dropped.', ['content_type' => 'artwork_comment', 'content_id' => 1] ); expect($result->matchedKeywords)->toContain('rare promo blast') ->and($result->score)->toBeGreaterThan(0); }); it('uses domain reputation to escalate blocked domains and auto hide recommendations', function (): void { ContentModerationDomain::query()->create([ 'domain' => 'campaign.spam.test', 'status' => ModerationDomainStatus::Blocked, ]); $result = app(ContentModerationService::class)->analyze( 'Buy followers now at https://campaign.spam.test and claim your giveaway', ['content_type' => 'artwork_comment', 'content_id' => 1] ); expect($result->matchedDomains)->toContain('campaign.spam.test') ->and($result->autoHideRecommended)->toBeTrue() ->and($result->score)->toBeGreaterThanOrEqual(95); }); it('applies user risk modifiers conservatively', function (): void { $user = User::factory()->create(); ContentModerationFinding::query()->create([ 'content_type' => 'artwork_comment', 'content_id' => 11, 'user_id' => $user->id, 'status' => ModerationStatus::ConfirmedSpam->value, 'severity' => 'high', 'score' => 85, 'content_hash' => hash('sha256', 'a'), 'scanner_version' => '2.0', 'content_snapshot' => 'spam one', ]); ContentModerationFinding::query()->create([ 'content_type' => 'artwork_comment', 'content_id' => 12, 'user_id' => $user->id, 'status' => ModerationStatus::ConfirmedSpam->value, 'severity' => 'critical', 'score' => 120, 'content_hash' => hash('sha256', 'b'), 'scanner_version' => '2.0', 'content_snapshot' => 'spam two', ]); $base = app(ContentModerationService::class)->analyze( 'Visit https://safe.example.test for more info', ['content_type' => 'artwork_comment', 'content_id' => 1] ); $risky = app(ContentModerationService::class)->analyze( 'Visit https://safe.example.test for more info', ['content_type' => 'artwork_comment', 'content_id' => 2, 'user_id' => $user->id] ); expect($risky->score)->toBeGreaterThan($base->score) ->and($risky->userRiskScore)->toBeGreaterThan(0) ->and($risky->ruleHits)->toHaveKey('user_risk_modifier'); }); it('applies strict seo policy and v3 assistive fields for link-heavy profile content', function (): void { $result = app(ContentModerationService::class)->analyze( 'Visit https://promo.cluster-test.com now for a limited time offer and boost your traffic', [ 'content_type' => ModerationContentType::UserProfileLink->value, 'content_id' => 77, 'content_target_type' => 'user_social_link', 'content_target_id' => 77, 'is_publicly_exposed' => true, ] ); expect($result->policyName)->toBe('strict_seo_protection') ->and($result->status)->toBe(ModerationStatus::Pending) ->and($result->priorityScore)->toBeGreaterThan($result->score) ->and(in_array($result->reviewBucket, ['urgent', 'high'], true))->toBeTrue() ->and($result->aiProvider)->toBe('heuristic_assist') ->and($result->aiLabel)->not->toBeNull() ->and($result->contentTargetType)->toBe('user_social_link') ->and($result->contentTargetId)->toBe(77) ->and($result->scoreBreakdown)->not->toBeEmpty(); }); it('builds v3 moderation source context for profile links and card text', function (): void { $service = app(ContentModerationSourceService::class); $profileLinkContext = $service->buildContext(ModerationContentType::UserProfileLink, (object) [ 'id' => 14, 'user_id' => 5, 'url' => 'https://promo.example.test', ]); $cardTextContext = $service->buildContext(ModerationContentType::CardText, (object) [ 'id' => 9, 'user_id' => 3, 'quote_text' => 'Promo headline', 'description' => 'Additional landing page copy', 'quote_author' => 'Campaign Bot', 'quote_source' => 'promo.example.test', 'visibility' => 'public', ]); expect($profileLinkContext['content_type'])->toBe(ModerationContentType::UserProfileLink->value) ->and($profileLinkContext['content_target_type'])->toBe('user_social_link') ->and($profileLinkContext['content_target_id'])->toBe(14) ->and($profileLinkContext['user_id'])->toBe(5) ->and($profileLinkContext['is_publicly_exposed'])->toBeTrue() ->and($cardTextContext['content_type'])->toBe(ModerationContentType::CardText->value) ->and($cardTextContext['content_target_type'])->toBe('nova_card') ->and($cardTextContext['content_target_id'])->toBe(9) ->and($cardTextContext['user_id'])->toBe(3) ->and($cardTextContext['is_publicly_exposed'])->toBeTrue() ->and($cardTextContext['content_snapshot'])->toContain('Promo headline') ->and($cardTextContext['content_snapshot'])->toContain('promo.example.test'); });