getJson('/api/settings') ->assertUnauthorized(); }); test('can get empty settings for authenticated user', function () { Passport::actingAs(User::factory()->create()); $this->getJson('/api/settings') ->assertOk() ->assertJsonPath('data', null); }); test('can store and update settings', function () { $user = User::factory()->create(); Passport::actingAs($user); $settings = [ 'appearance' => 'dark', 'accentColor' => 'green', 'language' => 'de', 'timelineZoom' => 1.5, 'timelineScrollLeft' => 420, 'floatingLines' => [ 'speed' => 1.2, 'lineCount' => 12, ], 'presets' => [[ 'id' => 'preset-1', 'name' => 'Praesentation', 'settings' => ['accentColor' => 'green'], 'updatedAt' => 1710000000000, ]], 'activePresetId' => 'preset-1', 'showFps' => false, ]; $this->putJson('/api/settings', ['settings' => $settings]) ->assertOk() ->assertJsonPath('data.appearance', 'dark') ->assertJsonPath('data.floatingLines.speed', 1.2) ->assertJsonPath('data.presets.0.name', 'Praesentation'); expect($user->settings()->count())->toBe(1); $this->putJson('/api/settings', [ 'settings' => [ ...$settings, 'accentColor' => 'blue', ], ]) ->assertOk() ->assertJsonPath('data.accentColor', 'blue'); expect($user->settings()->count())->toBe(1); }); test('settings are isolated per user', function () { $user = User::factory()->create(); $otherUser = User::factory()->create(); UserSetting::factory()->create([ 'user_id' => $otherUser->id, 'settings' => [ 'accentColor' => 'rose', 'language' => 'en', ], ]); Passport::actingAs($user); $this->getJson('/api/settings') ->assertOk() ->assertJsonPath('data', null); $this->putJson('/api/settings', [ 'settings' => [ 'accentColor' => 'green', 'language' => 'de', ], ])->assertOk(); expect($user->settings()->first()->settings['accentColor'])->toBe('green') ->and($otherUser->settings()->first()->settings['accentColor'])->toBe('rose'); }); test('settings payload is required', function () { Passport::actingAs(User::factory()->create()); $this->putJson('/api/settings', []) ->assertUnprocessable() ->assertJsonValidationErrors(['settings']); }); test('can upload and stream own settings background image', function () { Storage::fake('local'); $user = User::factory()->create(); Passport::actingAs($user); $response = $this->postJson('/api/settings/media/background', [ 'file' => UploadedFile::fake()->image('background.png', 2400, 1200), ]); $response ->assertCreated() ->assertJsonPath('data.width', 1600) ->assertJsonPath('data.height', 800) ->assertJsonPath('data.mimeType', 'image/jpeg'); expect($response->json('data.url'))->toStartWith('/settings/media/background?v='); Storage::disk('local')->assertExists("settings-media/{$user->id}/background.jpg"); $this->get('/api/settings/media/background') ->assertOk() ->assertHeader('Content-Type', 'image/jpeg'); }); test('settings background images are isolated per user', function () { Storage::fake('local'); $user = User::factory()->create(); $otherUser = User::factory()->create(); Storage::disk('local')->put("settings-media/{$otherUser->id}/background.jpg", 'other-background'); Passport::actingAs($user); $this->get('/api/settings/media/background') ->assertNotFound(); }); test('can delete own settings background image', function () { Storage::fake('local'); $user = User::factory()->create(); Storage::disk('local')->put("settings-media/{$user->id}/background.jpg", 'background'); Passport::actingAs($user); $this->deleteJson('/api/settings/media/background') ->assertNoContent(); Storage::disk('local')->assertMissing("settings-media/{$user->id}/background.jpg"); });