companies: Kurzfelder des Profils ohne Links, Adressen und Spam (NoPromotionalText)

Firmenname, Adresse und Pressekontakt erscheinen oeffentlich bzw. in
Mails, wurden aber nicht geprueft — nur die Beschreibung lief durch die
Rot-Ebene. Neue deterministische Regel: keine Links, keine E-Mail-
Adressen, keine Zeichen-/Emoji-Ketten, kein Blacklist-Treffer; nackte
Domains ohne Pfad bleiben erlaubt (Marke). Adresse zusaetzlich durch die
Rot-Ebene wie die Beschreibung.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Kevin Adametz 2026-09-17 07:55:07 +00:00
parent 4fd1db7825
commit 87ca9082a9
5 changed files with 246 additions and 7 deletions

View file

@ -0,0 +1,118 @@
<?php
use App\Models\Company;
use App\Models\Contact;
use App\Models\User;
use App\Rules\NoPromotionalText;
use Illuminate\Support\Facades\Validator;
use Livewire\Volt\Volt as LivewireVolt;
use Tests\TestCase;
/**
* Öffentliche Kurzfelder des Newsrooms (Firmenname, Adresse, Pressekontakt)
* sind keine Werbefläche — deterministische Regel, ohne KI (17.09.2026).
*/
function promotionalReason(string $value): ?string
{
$validator = Validator::make(['field' => $value], ['field' => ['nullable', 'string', app(NoPromotionalText::class)]]);
return $validator->fails() ? $validator->errors()->first('field') : null;
}
beforeEach(function (): void {
config()->set('blacklist.words', ['spam-keyword', 'casino royale']);
config()->set('scoring.classification.provider', 'deterministic');
});
test('ordinary company names, addresses and roles pass', function (string $value) {
expect(promotionalReason($value))->toBeNull();
})->with([
'Muster GmbH & Co. KG',
'meinshop.de GmbH',
'Hörwelt Basel e.V.',
'Musterstraße 12, 12345 Musterstadt',
'Leiterin Unternehmenskommunikation',
'Pressesprecher (Region Süd) – erreichbar Mo–Fr!',
'SAP SE',
'',
]);
test('links, addresses, noise and blacklist hits are rejected with a specific reason', function (string $value, string $expected) {
expect(promotionalReason($value))->toContain($expected);
})->with([
['Casino Bonus https://spam.example/bonus', 'Links'],
['Gewinne jetzt www.spam.example', 'Links'],
['Angebote auf spam.example/deal', 'Links'],
['Bestellen unter kontakt@spam.example', 'E-Mail-Adressen'],
['JETZT KAUFEN!!!', 'Sonderzeichen'],
['Gratis 🎰🎰🎰 Bonus', 'Sonderzeichen'],
['Ein spam-keyword Anbieter', 'unzulässigen Begriff'],
['Willkommen im Casino Royale', 'unzulässigen Begriff'],
]);
function newsroomEditor(TestCase $test): Company
{
$customer = User::factory()->create(['is_active' => true]);
$company = Company::factory()->businessportal24()->create(['name' => 'Muster GmbH', 'slug' => 'muster-gmbh', 'boilerplate' => null, 'address' => null]);
$customer->companies()->attach($company->id, ['role' => 'responsible']);
$test->actingAs($customer);
return $company;
}
test('a company name or address with a link is not saved', function () {
/** @var TestCase $this */
$company = newsroomEditor($this);
LivewireVolt::test('customer.press-kits.show', ['id' => $company->id])
->call('startEditCompany')
->set('companyName', 'Muster GmbH – https://spam.example')
->set('companyAddress', "Musterstraße 1\nwww.spam.example")
->call('saveCompany')
->assertHasErrors(['companyName', 'companyAddress']);
expect($company->fresh()->name)->toBe('Muster GmbH')
->and($company->fresh()->address)->toBeNull();
});
test('an address that hits the red level of the release check is not saved', function () {
/** @var TestCase $this */
$company = newsroomEditor($this);
LivewireVolt::test('customer.press-kits.show', ['id' => $company->id])
->call('startEditCompany')
->set('companyAddress', 'Musterstraße 1, spam-keyword Musterstadt')
->call('saveCompany')
->assertHasErrors(['companyAddress']);
expect($company->fresh()->address)->toBeNull();
});
test('a press contact with a link in the role is not saved', function () {
/** @var TestCase $this */
$company = newsroomEditor($this);
LivewireVolt::test('customer.press-kits.show', ['id' => $company->id])
->call('startCreateContact')
->set('contactLastName', 'Muster')
->set('contactResponsibility', 'Pressesprecher – Angebote unter https://spam.example')
->set('contactEmail', 'presse@muster.example')
->call('saveContact')
->assertHasErrors(['contactResponsibility']);
expect(Contact::query()->where('company_id', $company->id)->count())->toBe(0);
});
test('creating a company with a promotional name is refused', function () {
/** @var TestCase $this */
$customer = User::factory()->create(['is_active' => true]);
$this->actingAs($customer);
LivewireVolt::test('customer.press-kits.create')
->set('name', 'Casino Royale Bonus')
->set('portal', 'businessportal24')
->call('save')
->assertHasErrors(['name']);
expect(Company::query()->where('name', 'Casino Royale Bonus')->exists())->toBeFalse();
});