feat(classifier): label 'drop' via hard-rule + score threshold (-5 default)

- Add DEFAULT_THRESHOLDS['drop'] = -5 and DEFAULT_HARD_DROP_RULES const
- Add evaluate_hard_drop(): fires when no positive signals + short latin name OR ≥3 placeholder fields
- Wire into classify(): drop overrides all other labels; synthetic reason explains trigger
- Add drop_reason_label() and 'drop' => ' авто-дроп' to MessageFormatter::LABEL_MAP
- Update ClassifierTest/MessageFormatterTest to extend Cf7stgTestCase (filter isolation)
- Adjust pre-existing assertions that now correctly yield 'drop' instead of 'spam'
This commit is contained in:
Vladimir Bryzgalov
2026-04-21 16:14:49 +05:00
parent 18a07c9b1b
commit a70785beca
5 changed files with 214 additions and 6 deletions
+25 -2
View File
@@ -5,9 +5,8 @@ namespace Cf7stg\Tests;
use Cf7stg\MessageFormatter;
use Cf7stg\Classifier;
use PHPUnit\Framework\TestCase;
final class MessageFormatterTest extends TestCase
final class MessageFormatterTest extends Cf7stgTestCase
{
public function test_full_client_payload(): void
{
@@ -123,6 +122,8 @@ final class MessageFormatterTest extends TestCase
public function test_spam_label_emoji(): void
{
// score = SEO(-2) + URL(-3) + latin_only(-1) = -6 ≤ drop threshold(-5) → label=drop
// Fields with no valid email and name 'Melissa' (7 chars, not short latin) → score-based drop.
$fields = [
'your-name' => 'Melissa',
'your-message' => 'SEO promotion, visit http://spam.net',
@@ -135,6 +136,28 @@ final class MessageFormatterTest extends TestCase
'ip' => '', 'user_agent' => '', 'is_retro' => false,
]);
self::assertStringContainsString('⛔ авто-дроп', $r['text']);
}
public function test_spam_label_emoji_pure_spam(): void
{
// Score -3 (URL) + -2 (SEO keyword) = -5, but no drop because cyrillic name protects
// from hard-rule, and score -5 is NOT ≤ drop threshold -5 (it equals it, so it IS drop).
// Use URL(-3) only → score=-3, between spam(-2) and drop(-5) → spam label.
$fields = [
'your-name' => 'Nikolay',
'your-email' => 'nick@example.com',
'your-message' => 'visit http://promo.net for deals',
];
$classification = Classifier::classify($fields, ['reason' => 'Akismet']);
// valid email prevents hard-rule; score = URL(-3) + latin_only(-1) = -4 → spam
$r = MessageFormatter::build([
'fields' => $fields, 'classification' => $classification,
'site_title' => 's.ru', 'form_title' => 'f',
'submitted_at' => '17.04.2026', 'reason' => 'Akismet',
'ip' => '', 'user_agent' => '', 'is_retro' => false,
]);
self::assertStringContainsString('🔴 похоже спам', $r['text']);
}