'Анна Петровна', 'your-tel' => '+7 903 123-45-67', 'your-email' => 'anna@mail.ru', 'your-message' => 'Нужна сиделка для бабушки после инсульта, пожалуйста перезвоните.', ]; $r = Classifier::classify($fields, ['reason' => 'Akismet']); self::assertSame('client', $r['label']); self::assertGreaterThanOrEqual(3, $r['score']); } 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('drop', $r['label']); self::assertLessThanOrEqual(-5, $r['score']); } public function test_honeypot_forces_spam_even_with_phone(): void { $fields = [ 'your-name' => 'Анна', 'your-tel' => '+79031234567', 'your-message' => 'сиделка для мамы', ]; $r = Classifier::classify($fields, ['reason' => 'Honeypot']); self::assertSame('spam', $r['label']); } public function test_empty_fields_are_unclear(): void { $fields = [ 'your-email' => 'a@b.com', ]; $r = Classifier::classify($fields, ['reason' => 'CF7 rules']); self::assertSame('unclear', $r['label']); } public function test_multiple_keywords_capped_at_minus_six(): void { $fields = [ 'your-message' => 'SEO, SEO, SEO, backlinks, crypto, casino, займ, bitcoin', ]; $r = Classifier::classify($fields, ['reason' => 'Akismet']); // 8 hits × -2 would be -16; cap is -6 self::assertGreaterThanOrEqual(-10, $r['score']); // keyword part >= -6, plus -3 for URL? none here } public function test_ru_domain_email_adds_one(): void { $fields = [ 'your-name' => 'Иван', 'your-email' => 'ivan@yandex.ru', 'your-message' => 'Здравствуйте, интересует услуга сиделки', ]; $r = Classifier::classify($fields, ['reason' => 'Akismet']); $hasRuMail = false; foreach ($r['reasons'] as $reason) { if (strpos($reason['text'], 'ру-домен') !== false) { $hasRuMail = true; } } self::assertTrue($hasRuMail); } public function test_reasons_list_contains_sign_and_weight(): void { $fields = [ 'your-tel' => '+79031234567', ]; $r = Classifier::classify($fields, ['reason' => 'Akismet']); self::assertNotEmpty($r['reasons']); self::assertArrayHasKey('sign', $r['reasons'][0]); self::assertArrayHasKey('weight', $r['reasons'][0]); self::assertArrayHasKey('text', $r['reasons'][0]); } public function test_latin_only_name_penalty(): void { $fields = [ 'your-name' => 'John Smith', 'your-message' => 'please help me with website ranking', ]; $r = Classifier::classify($fields, ['reason' => 'Akismet']); $hasLatinPenalty = false; foreach ($r['reasons'] as $reason) { if ($reason['sign'] === '-' && strpos($reason['text'], 'латиница') !== false) { $hasLatinPenalty = true; } } self::assertTrue($hasLatinPenalty); } public function test_url_in_any_field_triggers_minus_three(): void { $fields = [ 'your-name' => 'Ольга', 'your-message' => 'посмотрите https://мойсайт.рф', ]; $r = Classifier::classify($fields, ['reason' => 'Akismet']); $hasUrl = false; foreach ($r['reasons'] as $reason) { if (strpos($reason['text'], 'URL') !== false) { $hasUrl = true; self::assertSame('-', $reason['sign']); self::assertSame(3, $reason['weight']); } } self::assertTrue($hasUrl); } public function test_thresholds_client_edge(): void { // Score exactly 3 → client $fields = ['your-tel' => '+79031234567']; $r = Classifier::classify($fields, ['reason' => 'Akismet']); self::assertSame(3, $r['score']); self::assertSame('client', $r['label']); } public function test_thresholds_unclear_between_neg_one_and_two(): void { $fields = ['your-name' => 'Олег', 'your-message' => 'короткий']; $r = Classifier::classify($fields, ['reason' => 'Akismet']); self::assertGreaterThan(-2, $r['score']); self::assertLessThan(3, $r['score']); self::assertSame('unclear', $r['label']); } public function test_keyword_matching_case_insensitive(): void { $fields = ['your-message' => 'Продаём ВИАГРА с доставкой']; $r = Classifier::classify($fields, ['reason' => 'Akismet']); $matched = false; foreach ($r['reasons'] as $reason) { if (strpos($reason['text'], 'виагра') !== false) $matched = true; } self::assertTrue($matched); } public function test_detects_cyrillic_name_under_custom_field_key(): void { // Real form on washanyanya.ru uses keys like 'text-211' / 'text-212' // instead of 'your-name' / 'your-tel'. Classifier must still award // +1 for a cyrillic name via the value-based heuristic. $fields = [ 'text-211' => 'Александр', 'text-212' => '+79776277470', ]; $r = Classifier::classify($fields, ['reason' => 'Akismet']); $has_name = false; foreach ($r['reasons'] as $reason) { if (strpos($reason['text'], 'кириллическое имя') !== false) $has_name = true; } self::assertTrue($has_name, 'expected +1 кириллическое имя via value heuristic'); } }