create([ 'email' => 'test.admin@example.com', 'is_admin' => true ]); Sanctum::actingAs($user, ['*']); } /** * Test creating a migrant with multiple photos and setting one as profile. */ public function test_can_create_migrant_with_multiple_photos_and_set_profile(): void { // Create fake storage disk for testing Storage::fake('public'); // Create test photo files (using create instead of image to avoid GD dependency) $photo1 = UploadedFile::fake()->create('photo1.jpg', 100, 'image/jpeg'); $photo2 = UploadedFile::fake()->create('photo2.jpg', 100, 'image/jpeg'); $photo3 = UploadedFile::fake()->create('photo3.jpg', 100, 'image/jpeg'); // Create migrant data $migrantData = [ 'surname' => $this->faker->lastName, 'christian_name' => $this->faker->firstName, 'date_of_birth' => $this->faker->date(), 'place_of_birth' => $this->faker->city, 'occupation' => $this->faker->jobTitle, 'id_card_no' => (string)$this->faker->unique()->randomNumber(6), 'migration' => [ 'date_of_arrival_aus' => $this->faker->date(), 'date_of_arrival_nt' => $this->faker->date(), 'arrival_period' => '1950-1960', ], // Add photos to the request 'photos' => [$photo1, $photo2, $photo3], 'captions' => ['First photo', 'Second photo', 'Third photo'], 'set_as_profile' => 1, // Set the first photo as profile ]; // Make the API request to create a migrant with photos $response = $this->postJson('/api/migrants', $migrantData); // Assert the response is successful $response->assertStatus(201) ->assertJsonStructure([ 'success', 'data' => [ 'person', 'uploaded_photos', ], 'message', ]) ->assertJson([ 'success' => true, 'message' => 'Person created successfully', ]); // Get the created person ID $personId = $response->json('data.person.person_id'); // Assert that the person was created in the database $this->assertDatabaseHas('person', [ 'surname' => $migrantData['surname'], 'christian_name' => $migrantData['christian_name'], ]); // Assert that 3 photos were created for this person $this->assertEquals(3, Photo::where('person_id', $personId)->count()); // Assert that exactly one photo is set as profile $this->assertEquals(1, Photo::where('person_id', $personId) ->where('is_profile_photo', true) ->count()); // Get the profile photo $profilePhoto = Photo::where('person_id', $personId) ->where('is_profile_photo', true) ->first(); // Assert that the first photo is the profile (based on caption) $this->assertEquals('First photo', $profilePhoto->caption); // Assert that the files were stored in the storage foreach (Photo::where('person_id', $personId)->get() as $photo) { Storage::disk('public')->assertExists('photos/' . $personId . '/' . $photo->filename); } } /** * Test updating the profile photo of an existing migrant. */ public function test_can_change_profile_photo(): void { // Create fake storage disk for testing Storage::fake('public'); // Create a migrant $person = Person::factory()->create(); // Create three photos for this migrant $photos = []; $captions = ['Photo 1', 'Photo 2', 'Photo 3']; for ($i = 0; $i < 3; $i++) { $filename = "photo{$i}.jpg"; $file = UploadedFile::fake()->create($filename, 100, 'image/jpeg'); // Upload the file to the storage $path = $file->storeAs("photos/{$person->person_id}", $filename, 'public'); // Create the photo record $photo = Photo::create([ 'person_id' => $person->person_id, 'filename' => $filename, 'original_filename' => $filename, 'file_path' => Storage::url($path), 'mime_type' => 'image/jpeg', 'file_size' => $file->getSize() / 1024, 'caption' => $captions[$i], 'is_profile_photo' => $i === 0 // Make the first one the profile initially ]); $photos[] = $photo; } // Now try to set the second photo as the profile $response = $this->postJson("/api/migrants/photos/{$photos[1]->id}/set-as-profile"); // Assert response is successful $response->assertStatus(200) ->assertJson([ 'success' => true, 'message' => 'Profile photo set successfully', ]); // Assert that now the second photo is the profile $this->assertDatabaseHas('photos', [ 'id' => $photos[1]->id, 'is_profile_photo' => true ]); // Assert that the first photo is no longer the profile $this->assertDatabaseHas('photos', [ 'id' => $photos[0]->id, 'is_profile_photo' => false ]); // Assert that only one photo is set as profile $this->assertEquals(1, Photo::where('person_id', $person->person_id) ->where('is_profile_photo', true) ->count()); } /** * Test updating a migrant with photo operations (upload, set as profile, remove). */ public function test_can_update_migrant_with_photo_operations(): void { // Create fake storage disk for testing Storage::fake('public'); // Create a migrant $person = Person::factory()->create([ 'surname' => 'Smith', 'christian_name' => 'John', 'occupation' => 'Teacher' ]); // Create two existing photos for this migrant $photos = []; $captions = ['Existing Photo 1', 'Existing Photo 2']; for ($i = 0; $i < 2; $i++) { $filename = "existing_photo{$i}.jpg"; $file = UploadedFile::fake()->create($filename, 100, 'image/jpeg'); // Upload the file to the storage $path = $file->storeAs("photos/{$person->person_id}", $filename, 'public'); // Create the photo record $photo = Photo::create([ 'person_id' => $person->person_id, 'filename' => $filename, 'original_filename' => $filename, 'file_path' => Storage::url($path), 'mime_type' => 'image/jpeg', 'file_size' => $file->getSize() / 1024, 'caption' => $captions[$i], 'is_profile_photo' => $i === 0 // Make the first one the profile initially ]); $photos[] = $photo; } // Create a new photo to upload during the update $newPhoto = UploadedFile::fake()->create('new_photo.jpg', 100, 'image/jpeg'); // Prepare update data $updateData = [ 'surname' => 'Smith-Jones', // Update the surname 'occupation' => 'Professor', // Update the occupation 'photos' => [$newPhoto], // Upload a new photo 'captions' => ['New Profile Photo'], // Caption for the new photo 'set_as_profile' => true, // Set the new photo as profile 'profile_photo_index' => 0, // Set the first (and only) new photo as profile 'existing_photos' => [ // Update caption of the first photo [ 'id' => $photos[0]->id, 'caption' => 'Updated Caption' ], // Remove the second photo (we'll handle this after the update) ] ]; // Make the API request to update the migrant $response = $this->putJson("/api/migrants/{$person->person_id}", $updateData); // Assert the response is successful $response->assertStatus(200) ->assertJsonStructure([ 'success', 'data' => [ 'person', 'uploaded_photos', ], 'message', ]) ->assertJson([ 'success' => true, 'message' => 'Person updated successfully', ]); // Assert the person data was updated $this->assertDatabaseHas('person', [ 'person_id' => $person->person_id, 'surname' => 'Smith-Jones', 'occupation' => 'Professor' ]); // Assert that we now have 3 photos (2 existing + 1 new) $this->assertEquals(3, Photo::where('person_id', $person->person_id)->count()); // Assert that the caption of the first photo was updated $this->assertDatabaseHas('photos', [ 'id' => $photos[0]->id, 'caption' => 'Updated Caption' ]); // Assert that the new photo was set as profile $profilePhoto = Photo::where('person_id', $person->person_id) ->where('is_profile_photo', true) ->first(); $this->assertEquals('New Profile Photo', $profilePhoto->caption); // Assert that the old profile photo is no longer the profile $this->assertDatabaseHas('photos', [ 'id' => $photos[0]->id, 'is_profile_photo' => false ]); // Now test removing a photo $photoToRemove = $photos[1]->id; $response = $this->deleteJson("/api/migrants/photos/{$photoToRemove}"); $response->assertStatus(200) ->assertJson([ 'success' => true, 'message' => 'Photo deleted successfully', ]); // Assert that the photo was removed from the database $this->assertDatabaseMissing('photos', [ 'id' => $photoToRemove ]); // Assert that the file was removed from storage Storage::disk('public')->assertMissing('photos/' . $person->person_id . '/' . $photos[1]->filename); // Assert that we now have 2 photos (originally 2 + 1 new - 1 deleted) $this->assertEquals(2, Photo::where('person_id', $person->person_id)->count()); } /** * Test adding a single photo, then multiple photos, and finally deleting photos */ public function test_can_add_single_and_multiple_photos_and_delete_them(): void { // Create fake storage disk for testing Storage::fake('public'); // Create a migrant without photos first $migrantData = [ 'surname' => $this->faker->lastName, 'christian_name' => $this->faker->firstName, 'date_of_birth' => $this->faker->date(), 'place_of_birth' => $this->faker->city, 'occupation' => $this->faker->jobTitle, 'id_card_no' => (string)$this->faker->unique()->randomNumber(6), 'migration' => [ 'date_of_arrival_aus' => $this->faker->date(), 'date_of_arrival_nt' => $this->faker->date(), 'arrival_period' => '1950-1960', ] ]; // 1. Create a migrant without any photos $response = $this->postJson('/api/migrants', $migrantData); // Assert the response is successful $response->assertStatus(201) ->assertJson([ 'success' => true, 'message' => 'Person created successfully', ]); // Get the created person ID $personId = $response->json('data.person.person_id'); $person = Person::find($personId); // Assert that no photos exist for this person yet $this->assertEquals(0, Photo::where('person_id', $personId)->count()); // 2. Now add a single photo and set it as profile $singlePhoto = UploadedFile::fake()->create('single_photo.jpg', 100, 'image/jpeg'); $updateData = [ 'photos' => [$singlePhoto], 'captions' => ['My First Photo'], 'set_as_profile' => true, 'profile_photo_index' => 0 ]; $response = $this->putJson("/api/migrants/{$personId}", $updateData); // Assert the response is successful $response->assertStatus(200) ->assertJson([ 'success' => true, 'message' => 'Person updated successfully', ]); // Assert that 1 photo was added $this->assertEquals(1, Photo::where('person_id', $personId)->count()); // Assert that the photo is set as profile $this->assertEquals(1, Photo::where('person_id', $personId) ->where('is_profile_photo', true) ->count()); // Get the photo details $firstPhoto = Photo::where('person_id', $personId)->first(); $this->assertEquals('My First Photo', $firstPhoto->caption); $this->assertTrue($firstPhoto->is_profile_photo); // Assert the file was stored Storage::disk('public')->assertExists('photos/' . $personId . '/' . $firstPhoto->filename); // 3. Now add multiple additional photos and set one as profile $photo1 = UploadedFile::fake()->create('additional1.jpg', 100, 'image/jpeg'); $photo2 = UploadedFile::fake()->create('additional2.jpg', 100, 'image/jpeg'); $photo3 = UploadedFile::fake()->create('additional3.jpg', 100, 'image/jpeg'); $updateData = [ 'photos' => [$photo1, $photo2, $photo3], 'captions' => ['Additional 1', 'Additional 2', 'Additional 3'], 'set_as_profile' => true, 'profile_photo_index' => 1 // Set the second new photo as profile ]; $response = $this->putJson("/api/migrants/{$personId}", $updateData); // Assert the response is successful $response->assertStatus(200) ->assertJson([ 'success' => true, 'message' => 'Person updated successfully', ]); // Assert that we now have 4 photos (1 existing + 3 new) $this->assertEquals(4, Photo::where('person_id', $personId)->count()); // Get all photos for this person $photos = Photo::where('person_id', $personId)->get(); // Assert that exactly one photo is set as profile $this->assertEquals(1, $photos->where('is_profile_photo', true)->count()); // Find the profile photo $profilePhoto = $photos->where('is_profile_photo', true)->first(); // Assert that the second new photo was set as profile $this->assertEquals('Additional 2', $profilePhoto->caption); // Assert the first photo is no longer the profile $this->assertFalse($firstPhoto->fresh()->is_profile_photo); // 4. Change the profile photo using profile_photo_id (existing photo) $nonProfilePhoto = $photos->where('caption', 'Additional 1')->first(); $updateData = [ 'set_as_profile' => true, 'profile_photo_id' => $nonProfilePhoto->id ]; $response = $this->putJson("/api/migrants/{$personId}", $updateData); // Assert the response is successful $response->assertStatus(200); // Assert that the specified photo is now the profile $this->assertTrue($nonProfilePhoto->fresh()->is_profile_photo); $this->assertFalse($profilePhoto->fresh()->is_profile_photo); // 5. Delete photos one by one $photosToDelete = $photos->whereNotIn('caption', ['Additional 1'])->pluck('id'); foreach ($photosToDelete as $photoId) { $response = $this->deleteJson("/api/migrants/photos/{$photoId}"); $response->assertStatus(200) ->assertJson([ 'success' => true, 'message' => 'Photo deleted successfully', ]); // Assert the photo was removed from the database $this->assertDatabaseMissing('photos', [ 'id' => $photoId ]); } // Assert that only one photo remains (the profile photo) $this->assertEquals(1, Photo::where('person_id', $personId)->count()); $this->assertEquals('Additional 1', Photo::where('person_id', $personId)->first()->caption); $this->assertTrue(Photo::where('person_id', $personId)->first()->is_profile_photo); } }