createUser('us01@example.com'); $client = $this->loginAs($user); $client->request('GET', '/api/user/search?q=a'); $this->assertResponseIsSuccessful(); $data = json_decode($client->getResponse()->getContent(), true); $this->assertSame([], $data); } // US-02: search valid q → results containing matching users public function test_search_valid_query_returns_results(): void { $user = $this->createUser('us02_me@example.com'); $other = $this->createUser('us02_other@example.com'); $client = $this->loginAs($user); $client->request('GET', '/api/user/search?q=us02_other'); $this->assertResponseIsSuccessful(); $data = json_decode($client->getResponse()->getContent(), true); $this->assertNotEmpty($data); $emails = array_column($data, 'email'); $this->assertContains('us02_other@example.com', $emails); // Own email should not appear $this->assertNotContains('us02_me@example.com', $emails); } // US-03: updateTheme valid → 200, returns {theme} public function test_update_theme_valid_returns_200(): void { $user = $this->createUser('us03@example.com'); $client = $this->loginAs($user); $client->request('PATCH', '/api/user/theme', [], [], [ 'CONTENT_TYPE' => 'application/json', ], json_encode(['theme' => 'warm-craft'])); $this->assertResponseIsSuccessful(); $data = json_decode($client->getResponse()->getContent(), true); $this->assertSame('warm-craft', $data['theme']); } // US-04: updateTheme invalid → 422 public function test_update_theme_invalid_returns_422(): void { $user = $this->createUser('us04@example.com'); $client = $this->loginAs($user); $client->request('PATCH', '/api/user/theme', [], [], [ 'CONTENT_TYPE' => 'application/json', ], json_encode(['theme' => 'not-a-valid-theme'])); $this->assertResponseStatusCodeSame(422); } // US-05: updateTimezone valid → 200, returns {timezone} public function test_update_timezone_valid_returns_200(): void { $user = $this->createUser('us05@example.com'); $client = $this->loginAs($user); $client->request('PATCH', '/api/user/timezone', [], [], [ 'CONTENT_TYPE' => 'application/json', ], json_encode(['timezone' => 'America/New_York'])); $this->assertResponseIsSuccessful(); $data = json_decode($client->getResponse()->getContent(), true); $this->assertSame('America/New_York', $data['timezone']); } // US-06: updateTimezone missing/null body → 422 public function test_update_timezone_missing_returns_422(): void { $user = $this->createUser('us06@example.com'); $client = $this->loginAs($user); $client->request('PATCH', '/api/user/timezone', [], [], [ 'CONTENT_TYPE' => 'application/json', ], json_encode(['other' => 'value'])); $this->assertResponseStatusCodeSame(422); } // US-07: updateTimezone invalid string → 422 public function test_update_timezone_invalid_string_returns_422(): void { $user = $this->createUser('us07@example.com'); $client = $this->loginAs($user); $client->request('PATCH', '/api/user/timezone', [], [], [ 'CONTENT_TYPE' => 'application/json', ], json_encode(['timezone' => 'Not/A/Valid/Timezone'])); $this->assertResponseStatusCodeSame(422); } // US-08: unauthenticated → redirects to /login public function test_unauthenticated_redirects_to_login(): void { $this->client->request('GET', '/api/user/search?q=test'); $this->assertResponseRedirects('/login'); } // US-09: updatePassword valid → 204 and the new password works for re-login public function test_update_password_valid_returns_204_and_rehashes(): void { $user = $this->createUser('us09@example.com', 'currentPass1'); $client = $this->loginAs($user); $client->request('PATCH', '/api/user/password', [], [], [ 'CONTENT_TYPE' => 'application/json', ], json_encode(['currentPassword' => 'currentPass1', 'newPassword' => 'brandNew2025'])); $this->assertResponseStatusCodeSame(204); // Re-fetch the user; the stored hash must accept the new password and // reject the old one. $hasher = static::getContainer()->get('security.user_password_hasher'); $em = static::getContainer()->get('doctrine.orm.entity_manager'); $em->clear(); $reloaded = $em->getRepository(\App\Entity\User::class)->findOneBy(['email' => 'us09@example.com']); $this->assertNotNull($reloaded); $this->assertTrue($hasher->isPasswordValid($reloaded, 'brandNew2025')); $this->assertFalse($hasher->isPasswordValid($reloaded, 'currentPass1')); } // US-10: updatePassword wrong current → 422 public function test_update_password_wrong_current_returns_422(): void { $user = $this->createUser('us10@example.com', 'rightPass1'); $client = $this->loginAs($user); $client->request('PATCH', '/api/user/password', [], [], [ 'CONTENT_TYPE' => 'application/json', ], json_encode(['currentPassword' => 'wrongPass1', 'newPassword' => 'brandNew2025'])); $this->assertResponseStatusCodeSame(422); } // US-11: updatePassword too-short new → 422 public function test_update_password_short_new_returns_422(): void { $user = $this->createUser('us11@example.com', 'currentPass1'); $client = $this->loginAs($user); $client->request('PATCH', '/api/user/password', [], [], [ 'CONTENT_TYPE' => 'application/json', ], json_encode(['currentPassword' => 'currentPass1', 'newPassword' => 'short'])); $this->assertResponseStatusCodeSame(422); } // US-12: updatePassword missing fields → 422 public function test_update_password_missing_fields_returns_422(): void { $user = $this->createUser('us12@example.com', 'currentPass1'); $client = $this->loginAs($user); $client->request('PATCH', '/api/user/password', [], [], [ 'CONTENT_TYPE' => 'application/json', ], json_encode(['newPassword' => 'brandNew2025'])); $this->assertResponseStatusCodeSame(422); } }