'John Doe', 'email' => 'john@example.com', 'password' => 'password123', 'password_confirmation' => 'password123', ]; $response = $this->postJson('/api/register', $userData); $response->assertStatus(201) ->assertJsonStructure([ 'success', 'message', 'data' => [ 'user' => [ 'id', 'name', 'email', 'created_at', 'updated_at', ], 'access_token', 'token_type', ] ]) ->assertJson([ 'success' => true, 'message' => 'User registered successfully', 'data' => [ 'user' => [ 'name' => 'John Doe', 'email' => 'john@example.com', ], 'token_type' => 'Bearer', ] ]); // Verify user was created in database $this->assertDatabaseHas('users', [ 'name' => 'John Doe', 'email' => 'john@example.com', ]); // Verify password was hashed $user = User::where('email', 'john@example.com')->first(); $this->assertTrue(Hash::check('password123', $user->password)); } /** * Test registration validation errors. */ public function test_registration_validates_required_fields() { // Test without any data $response = $this->postJson('/api/register', []); $response->assertStatus(422) ->assertJson([ 'success' => false, 'message' => 'Validation errors', ]) ->assertJsonPath('data.name', fn($value) => !empty($value)) ->assertJsonPath('data.email', fn($value) => !empty($value)) ->assertJsonPath('data.password', fn($value) => !empty($value)); // Test with invalid email $response = $this->postJson('/api/register', [ 'name' => 'John Doe', 'email' => 'invalid-email', 'password' => 'password123', 'password_confirmation' => 'password123', ]); $response->assertStatus(422) ->assertJsonPath('data.email', fn($value) => !empty($value)); // Test with password too short $response = $this->postJson('/api/register', [ 'name' => 'John Doe', 'email' => 'john@example.com', 'password' => '123', 'password_confirmation' => '123', ]); $response->assertStatus(422) ->assertJsonPath('data.password', fn($value) => !empty($value)); // Test with password confirmation mismatch $response = $this->postJson('/api/register', [ 'name' => 'John Doe', 'email' => 'john@example.com', 'password' => 'password123', 'password_confirmation' => 'different123', ]); $response->assertStatus(422) ->assertJsonPath('data.password', fn($value) => !empty($value)); } /** * Test registration with duplicate email. */ public function test_registration_prevents_duplicate_email() { // Create an existing user User::factory()->create([ 'email' => 'existing@example.com', ]); $userData = [ 'name' => 'John Doe', 'email' => 'existing@example.com', 'password' => 'password123', 'password_confirmation' => 'password123', ]; $response = $this->postJson('/api/register', $userData); $response->assertStatus(422) ->assertJsonPath('data.email', fn($value) => !empty($value)); } /** * Test user login with valid credentials. */ public function test_user_can_login_with_valid_credentials() { $user = User::factory()->create([ 'email' => 'john@example.com', 'password' => Hash::make('password123'), ]); $loginData = [ 'email' => 'john@example.com', 'password' => 'password123', ]; $response = $this->postJson('/api/login', $loginData); $response->assertStatus(200) ->assertJsonStructure([ 'success', 'message', 'data' => [ 'user' => [ 'id', 'name', 'email', 'created_at', 'updated_at', ], 'access_token', 'token_type', ] ]) ->assertJson([ 'success' => true, 'message' => 'Login successful', 'data' => [ 'user' => [ 'id' => $user->id, 'email' => 'john@example.com', ], 'token_type' => 'Bearer', ] ]); // Verify token is valid $this->assertNotEmpty($response->json('data.access_token')); } /** * Test login with invalid credentials. */ public function test_login_fails_with_invalid_credentials() { $user = User::factory()->create([ 'email' => 'john@example.com', 'password' => Hash::make('correct-password'), ]); // Test with wrong password - This should return 422 with validation exception $response = $this->postJson('/api/login', [ 'email' => 'john@example.com', 'password' => 'wrong-password', ]); $response->assertStatus(422); // Test with non-existent email $response = $this->postJson('/api/login', [ 'email' => 'nonexistent@example.com', 'password' => 'password123', ]); $response->assertStatus(422); } /** * Test login validation errors. */ public function test_login_validates_required_fields() { // Test without any data $response = $this->postJson('/api/login', []); $response->assertStatus(422) ->assertJson([ 'success' => false, 'message' => 'Validation errors', ]) ->assertJsonPath('data.email', fn($value) => !empty($value)) ->assertJsonPath('data.password', fn($value) => !empty($value)); // Test with invalid email format $response = $this->postJson('/api/login', [ 'email' => 'invalid-email', 'password' => 'password123', ]); $response->assertStatus(422) ->assertJsonPath('data.email', fn($value) => !empty($value)); } /** * Test authenticated user can access profile. */ public function test_authenticated_user_can_access_profile() { $user = User::factory()->create([ 'name' => 'John Doe', 'email' => 'john@example.com', ]); Sanctum::actingAs($user); $response = $this->getJson('/api/profile'); $response->assertStatus(200) ->assertJson([ 'success' => true, 'message' => 'Profile retrieved successfully', 'data' => [ 'id' => $user->id, 'name' => 'John Doe', 'email' => 'john@example.com', ] ]); } /** * Test unauthenticated user cannot access profile. */ public function test_unauthenticated_user_cannot_access_profile() { $response = $this->getJson('/api/profile'); $response->assertStatus(401); } /** * Test authenticated user can logout. */ public function test_authenticated_user_can_logout() { $user = User::factory()->create(); $token = $user->createToken('test-token'); $response = $this->withHeaders([ 'Authorization' => 'Bearer ' . $token->plainTextToken, ])->postJson('/api/logout'); $response->assertStatus(200) ->assertJson([ 'success' => true, 'message' => 'Logout successful', ]); // Verify token was deleted $this->assertDatabaseMissing('personal_access_tokens', [ 'id' => $token->accessToken->id, ]); } /** * Test unauthenticated user cannot logout. */ public function test_unauthenticated_user_cannot_logout() { $response = $this->postJson('/api/logout'); $response->assertStatus(401); } /** * Test token authentication works. */ public function test_token_authentication_works() { $user = User::factory()->create(); $token = $user->createToken('test-token'); // Test that the token can be used to access protected routes $response = $this->withHeaders([ 'Authorization' => 'Bearer ' . $token->plainTextToken, ])->getJson('/api/user'); $response->assertStatus(200) ->assertJson([ 'id' => $user->id, 'email' => $user->email, ]); } /** * Test invalid token is rejected. */ public function test_invalid_token_is_rejected() { $response = $this->withHeaders([ 'Authorization' => 'Bearer invalid-token', ])->getJson('/api/user'); $response->assertStatus(401); } /** * Test registration creates valid tokens. */ public function test_registration_creates_valid_token() { $userData = [ 'name' => 'John Doe', 'email' => 'john@example.com', 'password' => 'password123', 'password_confirmation' => 'password123', ]; $response = $this->postJson('/api/register', $userData); $token = $response->json('data.access_token'); // Use the token to access a protected route $profileResponse = $this->withHeaders([ 'Authorization' => 'Bearer ' . $token, ])->getJson('/api/profile'); $profileResponse->assertStatus(200) ->assertJson([ 'success' => true, 'data' => [ 'email' => 'john@example.com', ] ]); } /** * Test login creates valid tokens. */ public function test_login_creates_valid_token() { $user = User::factory()->create([ 'email' => 'john@example.com', 'password' => Hash::make('password123'), ]); $loginResponse = $this->postJson('/api/login', [ 'email' => 'john@example.com', 'password' => 'password123', ]); $token = $loginResponse->json('data.access_token'); // Use the token to access a protected route $profileResponse = $this->withHeaders([ 'Authorization' => 'Bearer ' . $token, ])->getJson('/api/profile'); $profileResponse->assertStatus(200) ->assertJson([ 'success' => true, 'data' => [ 'id' => $user->id, ] ]); } /** * Test registration returns user without password. */ public function test_registration_does_not_return_password() { $userData = [ 'name' => 'John Doe', 'email' => 'john@example.com', 'password' => 'password123', 'password_confirmation' => 'password123', ]; $response = $this->postJson('/api/register', $userData); $response->assertStatus(201) ->assertJsonMissing(['data.user.password']); } /** * Test login returns user without password. */ public function test_login_does_not_return_password() { $user = User::factory()->create([ 'email' => 'john@example.com', 'password' => Hash::make('password123'), ]); $response = $this->postJson('/api/login', [ 'email' => 'john@example.com', 'password' => 'password123', ]); $response->assertStatus(200) ->assertJsonMissing(['data.user.password']); } /** * Test profile returns user without password. */ public function test_profile_does_not_return_password() { $user = User::factory()->create(); Sanctum::actingAs($user); $response = $this->getJson('/api/profile'); $response->assertStatus(200) ->assertJsonMissing(['data.password']); } }