thats-me/backend/tests/Feature/Api/SettingsValidationTest.php
Kevin Adametz b262a6771f Backend: Medien-Typen, Sprachnotizen, Event-Felder und Sync-Härtung
- Event-Medien tragen Typ (image/video/audio), Dauer und Notiz; Videos
  bekommen ein Poster, Audio wird per ffmpeg nach AAC (audio/mp4)
  gewandelt, Originale werden mit Range-Unterstützung gestreamt.
- Upload nimmt eine Notiz (caption) direkt mit, PATCH ändert sie später.
- Neue Event-Felder: Zeitraum (end_date), Kategorie, Personen, Meilenstein.
  Regeln zentral in EventFields, damit CRUD und Sync gleich prüfen.
- Titel darf leer sein: ein Event entsteht mit dem ersten Eintrag.
- Artisan-Befehl media:transcode-voice-notes wandelt alte WebM/Ogg-Notizen,
  die Safari nicht abspielen kann.
- Login-Throttling, Settings-Validierung, Sync-Fehlercodes ohne Rohtext.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-25 07:00:35 +00:00

119 lines
4.3 KiB
PHP

<?php
use App\Models\User;
use Laravel\Passport\Passport;
beforeEach(function () {
$this->user = User::factory()->create();
Passport::actingAs($this->user);
});
test('a preset that is not an object is rejected', function () {
// `presets: [null]` used to satisfy the bare `array` rule and then blew up
// in the frontend when it read `preset.id`.
$this->putJson('/api/settings', [
'settings' => ['presets' => [null]],
])->assertStatus(422)->assertJsonValidationErrors('settings.presets.0');
});
test('a preset without an id or name is rejected', function () {
$this->putJson('/api/settings', [
'settings' => ['presets' => [['settings' => []]]],
])->assertStatus(422)
->assertJsonValidationErrors(['settings.presets.0.id', 'settings.presets.0.name']);
});
test('a valid preset is accepted', function () {
$this->putJson('/api/settings', [
'settings' => [
'presets' => [[
'id' => 'e0a1b2c3',
'name' => 'Nachtmodus',
'updatedAt' => 1726560000000,
'settings' => ['floatingLines' => ['speed' => 1.5, 'lineMode' => 'glow']],
]],
'activePresetId' => 'e0a1b2c3',
],
])->assertOk()->assertJsonPath('data.activePresetId', 'e0a1b2c3');
});
test('a preset keeps every snapshot field when stored', function () {
// Nested rules make validated() drop any key without an explicit rule, so a
// preset used to come back as `{floatingLines: ...}` only.
$snapshot = [
'theme' => 'dark',
'appearance' => 'dark',
'accentColor' => 'coral',
'language' => 'en',
'emotionGradientStart' => '#112233',
'emotionGradientEnd' => '#445566',
'timelineZoom' => 1.75,
'showFps' => true,
'floatingLines' => ['speed' => 1.5, 'lineMode' => 'glow'],
];
$this->putJson('/api/settings', [
'settings' => [
'presets' => [['id' => 'p1', 'name' => 'Nachtmodus', 'updatedAt' => 1726560000000, 'settings' => $snapshot]],
],
])->assertOk();
expect($this->user->fresh()->settings->settings['presets'][0]['settings'])->toBe($snapshot);
});
test('unknown top level settings keys are rejected instead of stored', function () {
$this->putJson('/api/settings', [
'settings' => ['theme' => 'dark', 'isAdmin' => true],
])->assertStatus(422)->assertJsonValidationErrors('settings');
expect($this->user->fresh()->settings)->toBeNull();
});
test('unknown floating line keys are rejected', function () {
$this->putJson('/api/settings', [
'settings' => ['floatingLines' => ['speed' => 1.0, 'somethingElse' => 5]],
])->assertStatus(422)->assertJsonValidationErrors('settings.floatingLines');
});
test('a nested structure inside floating lines is rejected', function () {
$this->putJson('/api/settings', [
'settings' => ['floatingLines' => ['speed' => ['nested' => 'payload']]],
])->assertStatus(422)->assertJsonValidationErrors('settings.floatingLines.speed');
});
test('an oversized string inside floating lines is rejected', function () {
$this->putJson('/api/settings', [
'settings' => ['floatingLines' => ['gradientStops' => str_repeat('x', 5000)]],
])->assertStatus(422)->assertJsonValidationErrors('settings.floatingLines.gradientStops');
});
test('more presets than the schema allows are rejected', function () {
$presets = collect(range(1, 51))->map(fn (int $n) => [
'id' => "preset-{$n}",
'name' => "Preset {$n}",
'settings' => [],
])->all();
$this->putJson('/api/settings', ['settings' => ['presets' => $presets]])
->assertStatus(422)
->assertJsonValidationErrors('settings.presets');
});
test('known floating line values still pass through unchanged', function () {
$this->putJson('/api/settings', [
'settings' => [
'theme' => 'dark',
'language' => 'de',
'timelineZoom' => 2.5,
'showFps' => false,
'floatingLines' => [
'speed' => 1.25,
'lineCount' => 10,
'bgCenter' => '#0a0514',
'lineMode' => 'glow',
],
],
])->assertOk()
->assertJsonPath('data.floatingLines.speed', 1.25)
->assertJsonPath('data.floatingLines.lineMode', 'glow');
});