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
+5 -4
View File
@@ -4,9 +4,8 @@ declare(strict_types=1);
namespace Cf7stg\Tests;
use Cf7stg\Classifier;
use PHPUnit\Framework\TestCase;
final class ClassifierTest extends TestCase
final class ClassifierTest extends Cf7stgTestCase
{
public function test_real_client_inquiry_is_green(): void
{
@@ -23,14 +22,16 @@ final class ClassifierTest extends TestCase
public function test_seo_spam_with_url_is_red(): void
{
// score = SEO(-2) + URL(-3) + latin_only(-1) = -6, which is ≤ drop threshold(-5)
// → label promoted from spam to drop (score-based drop, no hard-rule since email is valid)
$fields = [
'your-name' => 'Melissa Johnson',
'your-email' => 'promo@example.com',
'your-message' => 'Hi, we boost SEO positions, visit http://cheap-seo.net',
];
$r = Classifier::classify($fields, ['reason' => 'Akismet']);
self::assertSame('spam', $r['label']);
self::assertLessThanOrEqual(-2, $r['score']);
self::assertSame('drop', $r['label']);
self::assertLessThanOrEqual(-5, $r['score']);
}
public function test_honeypot_forces_spam_even_with_phone(): void