tenant = Tenant::factory()->create(); $this->user = User::factory()->create(['tenant_id' => $this->tenant->id]); $this->actingAs($this->user); }); test('PATCH без auth: 401', function () { auth()->logout(); $this->patchJson('/api/auth/me/notification-preferences', [ 'prefs' => ['new_lead' => ['email' => true]], ])->assertStatus(401); }); test('PATCH успех: сохраняет prefs + возвращает user', function () { $response = $this->patchJson('/api/auth/me/notification-preferences', [ 'prefs' => [ 'new_lead' => ['inapp' => true, 'push' => false, 'email' => true], 'reminder' => ['inapp' => true, 'email' => true], ], 'sound_enabled' => false, ]); $response->assertOk(); $userResp = $response->json('user'); expect($userResp['notification_preferences']['new_lead']['email'])->toBeTrue(); expect($userResp['notification_preferences']['new_lead']['inapp'])->toBeTrue(); expect($userResp['notification_preferences']['new_lead']['push'])->toBeFalse(); expect($userResp['sound_enabled'])->toBeFalse(); }); test('PATCH: неизвестные events отбрасываются', function () { $response = $this->patchJson('/api/auth/me/notification-preferences', [ 'prefs' => [ 'new_lead' => ['email' => true], 'fake_event' => ['email' => true], // не в ALL_EVENTS — должно отброситься ], ]); $response->assertOk(); $prefs = $response->json('user.notification_preferences'); expect($prefs)->toHaveKey('new_lead'); expect($prefs)->not->toHaveKey('fake_event'); }); test('PATCH: неизвестные каналы отбрасываются', function () { $response = $this->patchJson('/api/auth/me/notification-preferences', [ 'prefs' => [ 'new_lead' => [ 'email' => true, 'sms' => true, // SMS — не в нашей schema 'webhook' => true, // тоже не из schema ], ], ]); $response->assertOk(); $prefs = $response->json('user.notification_preferences.new_lead'); expect($prefs)->toHaveKey('email'); expect($prefs)->not->toHaveKey('sms'); expect($prefs)->not->toHaveKey('webhook'); }); test('PATCH: 422 без prefs', function () { $this->patchJson('/api/auth/me/notification-preferences', [ 'sound_enabled' => true, ])->assertStatus(422); }); test('PATCH: sound_enabled опционален (без него не меняется)', function () { $this->user->update(['sound_enabled' => true]); $response = $this->patchJson('/api/auth/me/notification-preferences', [ 'prefs' => ['new_lead' => ['email' => true]], ]); $response->assertOk(); expect($response->json('user.sound_enabled'))->toBeTrue(); }); test('GET /api/auth/me возвращает notification_preferences + sound_enabled', function () { $response = $this->getJson('/api/auth/me'); $response->assertOk(); $user = $response->json('user'); expect($user)->toHaveKey('notification_preferences'); expect($user)->toHaveKey('sound_enabled'); // schema-default: 8 events. expect($user['notification_preferences'])->toHaveKey('new_lead'); expect($user['notification_preferences'])->toHaveKey('reminder'); expect($user['notification_preferences'])->toHaveKey('low_balance'); }); test('PATCH: prefs.* должен быть объект (не строка)', function () { $this->patchJson('/api/auth/me/notification-preferences', [ 'prefs' => [ 'new_lead' => 'true', // строка вместо объекта ], ])->assertStatus(422); }); test('PATCH: bool-значения каналов кастятся', function () { $response = $this->patchJson('/api/auth/me/notification-preferences', [ 'prefs' => [ 'new_lead' => ['email' => 1, 'inapp' => 0, 'push' => '1'], ], ]); $response->assertOk(); $prefs = $response->json('user.notification_preferences.new_lead'); expect($prefs['email'])->toBeTrue(); expect($prefs['inapp'])->toBeFalse(); expect($prefs['push'])->toBeTrue(); }); test('PATCH полностью замещает prefs (не merge): ранее сохранённые отсутствующие events исчезают', function () { // Дано: user имеет полный default-набор (schema-default 8 events). $this->user->update(['notification_preferences' => [ 'new_lead' => ['email' => true], 'reminder' => ['email' => true], 'low_balance' => ['email' => true], ]]); // Update только new_lead — остальные пропадают (replace-семантика). $response = $this->patchJson('/api/auth/me/notification-preferences', [ 'prefs' => ['new_lead' => ['email' => false]], ]); $response->assertOk(); $prefs = $response->json('user.notification_preferences'); expect($prefs)->toHaveKey('new_lead'); expect($prefs)->not->toHaveKey('reminder'); expect($prefs)->not->toHaveKey('low_balance'); });