diff --git a/includes/class-message-formatter.php b/includes/class-message-formatter.php index f052bf8..681a915 100644 --- a/includes/class-message-formatter.php +++ b/includes/class-message-formatter.php @@ -43,9 +43,15 @@ final class MessageFormatter $lines = []; - $label_info = self::LABEL_MAP[$class['label']] ?? self::LABEL_MAP['unclear']; - $header = ($ctx['is_retro'] ? '📂 Ретроспектива · ' : '') - . $label_info['emoji'] . ' ' . $label_info['text']; + $is_dry_run_drop = !empty($ctx['dry_run_drop']); + + if ($is_dry_run_drop) { + $header = '[БЫЛО БЫ DROPPED]'; + } else { + $label_info = self::LABEL_MAP[$class['label']] ?? self::LABEL_MAP['unclear']; + $header = ($ctx['is_retro'] ? '📂 Ретроспектива · ' : '') + . $label_info['emoji'] . ' ' . $label_info['text']; + } $lines[] = $header; $lines[] = '🌐 ' . esc_html($ctx['site_title']) . ' · форма «' . esc_html($ctx['form_title']) . '»'; @@ -60,6 +66,18 @@ final class MessageFormatter $lines[] = '⚙️ Попало в спам: ' . esc_html($ctx['reason']); $lines[] = '📊 Классификация: ' . self::format_reasons($class['reasons']); + if ($is_dry_run_drop) { + $rule = ''; + foreach ($class['reasons'] as $r) { + $text = (string)($r['text'] ?? ''); + if (strpos($text, 'hard-rule:') === 0 || strpos($text, 'score ≤') === 0) { + $rule = $text; + break; + } + } + if ($rule !== '') $lines[] = '🚫 Сработавшее правило: ' . esc_html($rule); + } + $meta_bits = []; if ($ctx['ip'] !== '') $meta_bits[] = 'IP ' . esc_html($ctx['ip']); if ($ctx['user_agent'] !== '') $meta_bits[] = 'UA ' . esc_html(self::shorten_ua($ctx['user_agent'])); diff --git a/tests/MessageFormatterDryRunTest.php b/tests/MessageFormatterDryRunTest.php new file mode 100644 index 0000000..8688d0e --- /dev/null +++ b/tests/MessageFormatterDryRunTest.php @@ -0,0 +1,77 @@ + ['your-name' => 'OB', 'tel' => '+79991234567'], + 'classification' => [ + 'score' => 2, + 'label' => 'drop', + 'reasons' => [ + ['sign' => '+', 'weight' => 3, 'text' => 'телефон RU'], + ['sign' => '×', 'weight' => 0, 'text' => 'hard-rule: короткое латинское имя'], + ], + ], + 'site_title' => 'домработница.рус', + 'form_title' => 'Test', + 'submitted_at' => '21.04.2026 11:52', + 'reason' => 'Akismet', + 'ip' => '91.188.244.33', + 'user_agent' => 'Mozilla/5.0', + 'is_retro' => false, + 'dry_run_drop' => false, + ], $overrides); + } + + public function test_dry_run_drop_prefix_in_first_line(): void + { + $r = MessageFormatter::build($this->ctx(['dry_run_drop' => true])); + $first_line = strtok($r['text'], "\n"); + self::assertStringContainsString('[БЫЛО БЫ DROPPED]', $first_line); + } + + public function test_dry_run_drop_includes_triggered_rule_line(): void + { + $r = MessageFormatter::build($this->ctx(['dry_run_drop' => true])); + self::assertStringContainsString('Сработавшее правило: hard-rule: короткое латинское имя', $r['text']); + } + + public function test_dry_run_drop_includes_score_based_rule_line(): void + { + $ctx = $this->ctx([ + 'dry_run_drop' => true, + 'classification' => [ + 'score' => -7, + 'label' => 'drop', + 'reasons' => [ + ['sign' => '×', 'weight' => 0, 'text' => 'score ≤ -5'], + ], + ], + ]); + $r = MessageFormatter::build($ctx); + self::assertStringContainsString('Сработавшее правило: score ≤ -5', $r['text']); + } + + public function test_non_drop_payload_no_marker(): void + { + $r = MessageFormatter::build($this->ctx(['dry_run_drop' => false])); + self::assertStringNotContainsString('[БЫЛО БЫ DROPPED]', $r['text']); + self::assertStringNotContainsString('Сработавшее правило', $r['text']); + } + + public function test_dry_run_drop_header_replaces_label_emoji(): void + { + // When dry_run_drop is true, the regular ⛔ авто-дроп header must be REPLACED, + // not duplicated. + $r = MessageFormatter::build($this->ctx(['dry_run_drop' => true])); + $first_line = strtok($r['text'], "\n"); + self::assertStringNotContainsString('авто-дроп', $first_line); + } +}