Apply custom autospam token weights idempotently

Re-running the cached-data pipeline compounded each active token's weight onto the previously persisted value, so the same token kept growing on every run. Set each token's weight as an absolute value so repeated runs produce a stable result.
pull/7849/head
Ashley 1 week ago
parent 4e27e2c410
commit 20ad85e05f

@ -42,11 +42,9 @@ class AutospamUpdateCachedDataPipeline implements ShouldQueue
$newSpam = AutospamCustomTokens::whereCategory('spam')->where('active', true)->get();
foreach ($newSpam as $ns) {
$key = strtolower($ns->token);
if (isset($spam['words']['spam'][$key])) {
$spam['words']['spam'][$key] = $spam['words']['spam'][$key] + $ns->weight;
} else {
$spam['words']['spam'][$key] = $ns->weight;
}
// Set the token's weight as an absolute value so re-running the job
// never compounds the same token's contribution.
$spam['words']['spam'][$key] = $ns->weight;
}
$newSpamCount = count($spam['words']['spam']);
if ($newSpamCount) {
@ -71,11 +69,9 @@ class AutospamUpdateCachedDataPipeline implements ShouldQueue
$newHam = AutospamCustomTokens::whereCategory('ham')->where('active', true)->get();
foreach ($newHam as $ns) {
$key = strtolower($ns->token);
if (isset($ham['words']['ham'][$key])) {
$ham['words']['ham'][$key] = $ham['words']['ham'][$key] + $ns->weight;
} else {
$ham['words']['ham'][$key] = $ns->weight;
}
// Set the token's weight as an absolute value so re-running the job
// never compounds the same token's contribution.
$ham['words']['ham'][$key] = $ns->weight;
}
$newHamCount = count($ham['words']['ham']);

@ -0,0 +1,78 @@
<?php
use App\Jobs\AutospamPipeline\AutospamUpdateCachedDataPipeline;
use App\Services\AutospamService;
use Illuminate\Foundation\Testing\LazilyRefreshDatabase;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Storage;
uses(LazilyRefreshDatabase::class);
const IDEMPOTENT_MERGE_TOKEN_WEIGHT = 10;
function seedIdempotentAutospamToken(string $token, string $category): void
{
DB::table('autospam_custom_tokens')->insert([
'token' => $token,
'weight' => IDEMPOTENT_MERGE_TOKEN_WEIGHT,
'is_spam' => $category === 'spam',
'category' => $category,
'active' => true,
'created_at' => now(),
'updated_at' => now(),
]);
}
/*
|--------------------------------------------------------------------------
| Custom-token merge must be idempotent
|--------------------------------------------------------------------------
|
| Re-running the cached-data pipeline without any database change used to add
| a token's weight onto the previously persisted count, so the same token kept
| growing (10 -> 20 -> 30). The merge now sets each active token's weight as an
| absolute value, so a token with weight 10 contributes exactly 10 no matter
| how many times the pipeline runs.
|
| No spam.json / ham.json is pre-seeded, so each word's value comes solely from
| the custom token under test and is exactly assertable.
|
*/
beforeEach(function () {
Storage::fake('local');
});
it('applies a spam token weight exactly once across repeated runs', function () {
seedIdempotentAutospamToken('idempotentspamword', 'spam');
(new AutospamUpdateCachedDataPipeline)->handle();
$first = json_decode(Storage::get(AutospamService::MODEL_SPAM_PATH), true);
expect($first['words']['spam'])->toHaveKey('idempotentspamword');
expect($first['words']['spam']['idempotentspamword'])->toBe(IDEMPOTENT_MERGE_TOKEN_WEIGHT);
(new AutospamUpdateCachedDataPipeline)->handle();
(new AutospamUpdateCachedDataPipeline)->handle();
$later = json_decode(Storage::get(AutospamService::MODEL_SPAM_PATH), true);
expect($later['words']['spam'])->toHaveKey('idempotentspamword');
expect($later['words']['spam']['idempotentspamword'])->toBe(IDEMPOTENT_MERGE_TOKEN_WEIGHT);
});
it('applies a ham token weight exactly once across repeated runs', function () {
seedIdempotentAutospamToken('idempotenthamword', 'ham');
(new AutospamUpdateCachedDataPipeline)->handle();
$first = json_decode(Storage::get(AutospamService::MODEL_HAM_PATH), true);
expect($first['words']['ham'])->toHaveKey('idempotenthamword');
expect($first['words']['ham']['idempotenthamword'])->toBe(IDEMPOTENT_MERGE_TOKEN_WEIGHT);
(new AutospamUpdateCachedDataPipeline)->handle();
(new AutospamUpdateCachedDataPipeline)->handle();
$later = json_decode(Storage::get(AutospamService::MODEL_HAM_PATH), true);
expect($later['words']['ham'])->toHaveKey('idempotenthamword');
expect($later['words']['ham']['idempotenthamword'])->toBe(IDEMPOTENT_MERGE_TOKEN_WEIGHT);
});
Loading…
Cancel
Save