['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); } }