fix: wire is_hard_drop_enabled + drop_score_threshold settings into Classifier filter chain

Settings::register_classifier_filters() registers priority-5 callbacks on
cf7stg_classifier_thresholds and cf7stg_hard_drop_rules so the UI options
actually influence Classifier behaviour. Called from Plugin::boot(). User
filters at priority 10 override these defaults as expected. Adds 4 tests
covering the wired behaviour and override priority. Also adds dry_run_drop?
to MessageFormatter::build() @param docblock.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Vladimir Bryzgalov
2026-04-21 17:25:20 +05:00
parent 030eaca408
commit 714b4712ad
4 changed files with 80 additions and 1 deletions
+52
View File
@@ -4,6 +4,7 @@ declare(strict_types=1);
namespace Cf7stg\Tests;
use Cf7stg\Classifier;
use Cf7stg\Settings;
final class ClassifierDropTest extends Cf7stgTestCase
{
@@ -283,4 +284,55 @@ final class ClassifierDropTest extends Cf7stgTestCase
$r = Classifier::classify($fields, ['reason' => 'Akismet']);
self::assertSame('drop', $r['label']);
}
public function test_hard_drop_disabled_via_settings_skips_drop(): void
{
Settings::set_hard_drop_enabled(false);
Settings::register_classifier_filters();
$fields = require __DIR__ . '/fixtures/spam-screenshot-1.php';
$r = Classifier::classify($fields, ['reason' => 'Akismet']);
// With hard-rule disabled, screenshot-1 has no positive signals AND score isn't ≤ -5
// (placeholders by themselves give no score penalty), so label should NOT be 'drop'.
self::assertNotSame('drop', $r['label']);
}
public function test_drop_score_threshold_from_settings_applied(): void
{
Settings::set_drop_score_threshold(-3);
Settings::register_classifier_filters();
// URL gives -3 score, no positive signals, no hard-rule trigger.
// With threshold lowered to -3, this should fire score-based drop.
$fields = ['msg' => 'http://x.com'];
$r = Classifier::classify($fields, ['reason' => 'Akismet']);
self::assertSame('drop', $r['label']);
}
public function test_settings_threshold_can_be_overridden_by_user_filter(): void
{
Settings::set_drop_score_threshold(-3);
Settings::register_classifier_filters();
// User filter at priority 10 should override Settings default at priority 5.
add_filter('cf7stg_classifier_thresholds', static function ($t) {
$t['drop'] = -10;
return $t;
}, 10);
$fields = ['msg' => 'http://x.com']; // score = -3
$r = Classifier::classify($fields, ['reason' => 'Akismet']);
// -3 is NOT ≤ -10, so no drop
self::assertNotSame('drop', $r['label']);
}
public function test_filter_disables_placeholder_fields_rule(): void
{
add_filter('cf7stg_hard_drop_rules', static function ($rules) {
$rules['placeholder_fields'] = false;
return $rules;
});
// Form WITHOUT name field — digit-only values are valid placeholders (^[A-Za-z0-9]{1,2}$)
// but FieldDetector::find_name rejects them (has digits), so short_latin_name cannot fire.
// With placeholder_fields rule also disabled → no hard-rule fires → no drop.
$fields = ['a' => '11', 'b' => '22', 'c' => '33', 'd' => '44'];
$r = Classifier::classify($fields, ['reason' => 'Akismet']);
self::assertNotSame('drop', $r['label']);
}
}