user = User::factory()->create(); } /** * Test that unauthenticated users cannot access trip endpoints. */ public function test_unauthenticated_user_cannot_access_trips() { $response = $this->getJson('/api/trips'); $response->assertStatus(401); $response = $this->postJson('/api/trips', []); $response->assertStatus(401); $response = $this->putJson('/api/trips/1', []); $response->assertStatus(401); $response = $this->deleteJson('/api/trips/1'); $response->assertStatus(401); } /** * Test user can create a trip. */ public function test_user_can_create_trip() { Sanctum::actingAs($this->user); $tripData = [ 'name' => 'Summer Vacation 2025', 'description' => 'A wonderful trip to Europe', 'start_date' => '2025-06-01', 'end_date' => '2025-06-15', ]; $response = $this->postJson('/api/trips', $tripData); $response->assertStatus(201) ->assertJsonStructure([ 'data' => [ 'id', 'name', 'description', 'start_date', 'end_date', 'created_by_user_id', 'created_at', 'updated_at', ] ]) ->assertJsonPath('data.name', 'Summer Vacation 2025') ->assertJsonPath('data.description', 'A wonderful trip to Europe') ->assertJsonPath('data.created_by_user_id', $this->user->id); $this->assertDatabaseHas('trips', [ 'name' => 'Summer Vacation 2025', 'created_by_user_id' => $this->user->id, ]); } /** * Test trip creation validation. */ public function test_trip_creation_validates_required_fields() { Sanctum::actingAs($this->user); // Test without name $response = $this->postJson('/api/trips', [ 'description' => 'A trip without a name', ]); $response->assertStatus(422) ->assertJsonValidationErrors(['name']); // Test with empty name $response = $this->postJson('/api/trips', [ 'name' => '', 'description' => 'A trip with empty name', ]); $response->assertStatus(422) ->assertJsonValidationErrors(['name']); // Test with invalid dates $response = $this->postJson('/api/trips', [ 'name' => 'Trip with invalid dates', 'start_date' => 'not-a-date', 'end_date' => '2025-13-45', ]); $response->assertStatus(422) ->assertJsonValidationErrors(['start_date', 'end_date']); // Test with end date before start date $response = $this->postJson('/api/trips', [ 'name' => 'Trip with reversed dates', 'start_date' => '2025-06-15', 'end_date' => '2025-06-01', ]); $response->assertStatus(422) ->assertJsonValidationErrors(['end_date']); } /** * Test user can list their trips. */ public function test_user_can_list_their_own_trips() { Sanctum::actingAs($this->user); // Create some trips for this user $trips = Trip::factory()->count(3)->create([ 'created_by_user_id' => $this->user->id, ]); // Create trips for another user (should not be visible) $otherUser = User::factory()->create(); Trip::factory()->count(2)->create([ 'created_by_user_id' => $otherUser->id, ]); $response = $this->getJson('/api/trips'); $response->assertStatus(200) ->assertJsonCount(3, 'data') ->assertJsonStructure([ 'data' => [ '*' => [ 'id', 'name', 'description', 'start_date', 'end_date', 'created_by_user_id', 'created_at', 'updated_at', ] ] ]); // Verify all returned trips belong to the authenticated user foreach ($response->json('data') as $trip) { $this->assertEquals($this->user->id, $trip['created_by_user_id']); } } /** * Test user can view a specific trip. */ public function test_user_can_view_their_own_trip() { Sanctum::actingAs($this->user); $trip = Trip::factory()->create([ 'created_by_user_id' => $this->user->id, 'name' => 'My Special Trip', ]); $response = $this->getJson("/api/trips/{$trip->id}"); $response->assertStatus(200) ->assertJsonPath('data.id', $trip->id) ->assertJsonPath('data.name', 'My Special Trip') ->assertJsonPath('data.created_by_user_id', $this->user->id); } /** * Test user cannot view another user's trip. */ public function test_user_cannot_view_another_users_trip() { Sanctum::actingAs($this->user); $otherUser = User::factory()->create(); $otherTrip = Trip::factory()->create([ 'created_by_user_id' => $otherUser->id, ]); $response = $this->getJson("/api/trips/{$otherTrip->id}"); // Controller returns 404 when trip doesn't belong to user $response->assertStatus(404); } /** * Test user can update their trip. */ public function test_user_can_update_their_own_trip() { Sanctum::actingAs($this->user); $trip = Trip::factory()->create([ 'created_by_user_id' => $this->user->id, 'name' => 'Original Name', 'description' => 'Original Description', ]); $updateData = [ 'name' => 'Updated Trip Name', 'description' => 'Updated Description', 'start_date' => '2025-07-01', 'end_date' => '2025-07-15', ]; $response = $this->putJson("/api/trips/{$trip->id}", $updateData); $response->assertStatus(200) ->assertJsonPath('data.id', $trip->id) ->assertJsonPath('data.name', 'Updated Trip Name') ->assertJsonPath('data.description', 'Updated Description'); $this->assertDatabaseHas('trips', [ 'id' => $trip->id, 'name' => 'Updated Trip Name', 'description' => 'Updated Description', ]); } /** * Test user cannot update another user's trip. */ public function test_user_cannot_update_another_users_trip() { Sanctum::actingAs($this->user); $otherUser = User::factory()->create(); $otherTrip = Trip::factory()->create([ 'created_by_user_id' => $otherUser->id, 'name' => 'Other User Trip', ]); $response = $this->putJson("/api/trips/{$otherTrip->id}", [ 'name' => 'Trying to Update', ]); // Controller returns 404 when trip doesn't belong to user $response->assertStatus(404); // Verify the trip wasn't updated $this->assertDatabaseHas('trips', [ 'id' => $otherTrip->id, 'name' => 'Other User Trip', ]); } /** * Test user can delete their trip. */ public function test_user_can_delete_their_own_trip() { Sanctum::actingAs($this->user); $trip = Trip::factory()->create([ 'created_by_user_id' => $this->user->id, ]); $response = $this->deleteJson("/api/trips/{$trip->id}"); // Controller returns 200 with a message $response->assertStatus(200) ->assertJson([ 'message' => 'Trip deleted successfully' ]); $this->assertDatabaseMissing('trips', [ 'id' => $trip->id, ]); } /** * Test user cannot delete another user's trip. */ public function test_user_cannot_delete_another_users_trip() { Sanctum::actingAs($this->user); $otherUser = User::factory()->create(); $otherTrip = Trip::factory()->create([ 'created_by_user_id' => $otherUser->id, ]); $response = $this->deleteJson("/api/trips/{$otherTrip->id}"); // Controller returns 404 when trip doesn't belong to user $response->assertStatus(404); // Verify the trip still exists $this->assertDatabaseHas('trips', [ 'id' => $otherTrip->id, ]); } /** * Test handling non-existent trip. */ public function test_returns_404_for_non_existent_trip() { Sanctum::actingAs($this->user); $response = $this->getJson('/api/trips/99999'); $response->assertStatus(404); $response = $this->putJson('/api/trips/99999', ['name' => 'Updated']); $response->assertStatus(404); $response = $this->deleteJson('/api/trips/99999'); $response->assertStatus(404); } /** * Test trip creation with minimal data. */ public function test_user_can_create_trip_with_minimal_data() { Sanctum::actingAs($this->user); $tripData = [ 'name' => 'Minimal Trip', ]; $response = $this->postJson('/api/trips', $tripData); $response->assertStatus(201) ->assertJsonPath('data.name', 'Minimal Trip') ->assertJsonPath('data.created_by_user_id', $this->user->id) ->assertJsonStructure([ 'data' => [ 'id', 'name', 'created_by_user_id', 'created_at', 'updated_at', ] ]); $this->assertDatabaseHas('trips', [ 'name' => 'Minimal Trip', 'created_by_user_id' => $this->user->id, 'description' => null, 'start_date' => null, 'end_date' => null, ]); } /** * Test trip name length validation. */ public function test_trip_name_length_validation() { Sanctum::actingAs($this->user); // Test with too long name (assuming max is 255) $response = $this->postJson('/api/trips', [ 'name' => str_repeat('a', 256), ]); $response->assertStatus(422) ->assertJsonValidationErrors(['name']); } /** * Test trips are returned in correct order. */ public function test_trips_are_returned_in_descending_order() { Sanctum::actingAs($this->user); // Create trips with specific timestamps $oldTrip = Trip::factory()->create([ 'created_by_user_id' => $this->user->id, 'name' => 'Old Trip', 'created_at' => now()->subDays(2), ]); $newTrip = Trip::factory()->create([ 'created_by_user_id' => $this->user->id, 'name' => 'New Trip', 'created_at' => now(), ]); $middleTrip = Trip::factory()->create([ 'created_by_user_id' => $this->user->id, 'name' => 'Middle Trip', 'created_at' => now()->subDay(), ]); $response = $this->getJson('/api/trips'); $response->assertStatus(200); $trips = $response->json('data'); // Verify trips are in descending order (newest first) $this->assertEquals('New Trip', $trips[0]['name']); $this->assertEquals('Middle Trip', $trips[1]['name']); $this->assertEquals('Old Trip', $trips[2]['name']); } }