seed([RoleSeeder::class, SamplePsgcSeeder::class]); $this->admin = User::factory()->create([ 'role' => User::ROLE_ADMIN, 'status' => User::STATUS_ACTIVE, ]); $this->admin->assignRole(User::ROLE_ADMIN); // Fetch or create a Barangay with boundary polygon for spatial tests $this->barangay = Barangay::first(); $boundary = new Polygon([ new LineString([ new Point(14.0, 120.0, 4326), new Point(15.0, 120.0, 4326), new Point(15.0, 121.0, 4326), new Point(14.0, 121.0, 4326), new Point(14.0, 120.0, 4326), ]), ], 4326); $this->barangay->update([ 'boundary' => $boundary, 'centroid' => new Point(14.5, 120.5, 4326), ]); $resident = User::factory()->create([ 'role' => User::ROLE_RESIDENT, 'status' => User::STATUS_ACTIVE, ]); $this->household = Household::create([ 'tenant_id' => $this->defaultTenant->id, 'head_user_id' => $resident->id, 'barangay_id' => $this->barangay->id, 'address_line' => '123 Test St', 'coordinates' => new Point(14.5, 120.5, 4326), 'household_size' => 2, 'verification_status' => Household::VERIFICATION_APPROVED, ]); HouseholdMember::create([ 'household_id' => $this->household->id, 'user_id' => $resident->id, 'relationship' => HouseholdMember::RELATIONSHIP_HEAD, 'full_name' => $resident->full_name, ]); } public function test_admin_can_update_household_details_inside_barangay(): void { Sanctum::actingAs($this->admin); $payload = [ 'address_line' => 'Updated Address St', 'lat' => 14.6, 'lng' => 120.6, 'barangay_id' => $this->barangay->id, 'household_size' => 4, ]; $response = $this->patchJson("/api/v1/admin/households/{$this->household->uuid}", $payload); $response->assertStatus(200) ->assertJsonPath('data.address_line', 'Updated Address St') ->assertJsonPath('data.household_size', 4); $this->assertDatabaseHas('households', [ 'id' => $this->household->id, 'address_line' => 'Updated Address St', 'household_size' => 4, ]); } public function test_admin_cannot_update_household_outside_barangay_boundary(): void { Sanctum::actingAs($this->admin); $payload = [ 'address_line' => 'Outside Barangay St', 'lat' => 16.0, // outside boundary 'lng' => 122.0, 'barangay_id' => $this->barangay->id, 'household_size' => 4, ]; $response = $this->patchJson("/api/v1/admin/households/{$this->household->uuid}", $payload); $response->assertStatus(422) ->assertJsonValidationErrors(['barangay_id']); } public function test_admin_can_add_member_profile_guest(): void { Sanctum::actingAs($this->admin); $payload = [ 'relationship' => 'child', 'full_name' => 'Billy Kid', 'date_of_birth' => '2015-05-10', ]; $response = $this->postJson("/api/v1/admin/households/{$this->household->uuid}/members", $payload); $response->assertStatus(201) ->assertJsonFragment([ 'full_name' => 'Billy Kid', 'relationship' => 'child', ]); $this->assertDatabaseHas('household_members', [ 'household_id' => $this->household->id, 'full_name' => 'Billy Kid', 'relationship' => 'child', ]); } public function test_admin_can_add_member_registered_user(): void { Sanctum::actingAs($this->admin); $newResident = User::factory()->create([ 'role' => User::ROLE_RESIDENT, 'status' => User::STATUS_ACTIVE, ]); $payload = [ 'relationship' => 'spouse', 'user_id' => $newResident->id, ]; $response = $this->postJson("/api/v1/admin/households/{$this->household->uuid}/members", $payload); $response->assertStatus(201); $this->assertDatabaseHas('household_members', [ 'household_id' => $this->household->id, 'user_id' => $newResident->id, 'relationship' => 'spouse', ]); } public function test_admin_cannot_add_resident_already_in_household(): void { Sanctum::actingAs($this->admin); // The head user is already in a household. $headUserId = $this->household->head_user_id; $payload = [ 'relationship' => 'sibling', 'user_id' => $headUserId, ]; $response = $this->postJson("/api/v1/admin/households/{$this->household->uuid}/members", $payload); $response->assertStatus(422) ->assertJsonValidationErrors(['user_id']); } public function test_admin_can_edit_household_member(): void { Sanctum::actingAs($this->admin); $member = HouseholdMember::create([ 'household_id' => $this->household->id, 'relationship' => 'other', 'full_name' => 'Uncle Ben', ]); $payload = [ 'relationship' => 'parent', 'full_name' => 'Uncle Benjamin', ]; $response = $this->patchJson("/api/v1/admin/households/{$this->household->uuid}/members/{$member->id}", $payload); $response->assertStatus(200); $this->assertDatabaseHas('household_members', [ 'id' => $member->id, 'relationship' => 'parent', 'full_name' => 'Uncle Benjamin', ]); } public function test_admin_can_delete_member(): void { Sanctum::actingAs($this->admin); $member = HouseholdMember::create([ 'household_id' => $this->household->id, 'relationship' => 'other', 'full_name' => 'To Delete', ]); $response = $this->deleteJson("/api/v1/admin/households/{$this->household->uuid}/members/{$member->id}"); $response->assertStatus(200); $this->assertSoftDeleted('household_members', [ 'id' => $member->id, ]); } public function test_admin_cannot_delete_head(): void { Sanctum::actingAs($this->admin); $headMember = $this->household->members()->where('relationship', HouseholdMember::RELATIONSHIP_HEAD)->first(); $response = $this->deleteJson("/api/v1/admin/households/{$this->household->uuid}/members/{$headMember->id}"); $response->assertStatus(422); } public function test_admin_can_transfer_resident_from_another_household_and_clean_it_up(): void { Sanctum::actingAs($this->admin); // 1. Create a resident user who heads their own household (default signup scenario) $resident = User::factory()->create([ 'role' => User::ROLE_RESIDENT, 'status' => User::STATUS_ACTIVE, ]); $oldHousehold = Household::create([ 'tenant_id' => $this->defaultTenant->id, 'head_user_id' => $resident->id, 'barangay_id' => $this->barangay->id, 'address_line' => '456 Previous St', 'coordinates' => new Point(14.5, 120.5, 4326), 'household_size' => 1, 'verification_status' => Household::VERIFICATION_APPROVED, ]); $oldMember = HouseholdMember::create([ 'household_id' => $oldHousehold->id, 'user_id' => $resident->id, 'relationship' => HouseholdMember::RELATIONSHIP_HEAD, 'full_name' => $resident->full_name, ]); // 2. Transfer them as a child to the new household $payload = [ 'relationship' => 'child', 'user_id' => $resident->id, ]; $response = $this->postJson("/api/v1/admin/households/{$this->household->uuid}/members", $payload); $response->assertStatus(201); // 3. Assert they are now a member of the target household $this->assertDatabaseHas('household_members', [ 'household_id' => $this->household->id, 'user_id' => $resident->id, 'relationship' => 'child', ]); // 4. Assert old membership record is deleted $this->assertSoftDeleted('household_members', [ 'id' => $oldMember->id, ]); // 5. Assert old empty household is deleted $this->assertSoftDeleted('households', [ 'id' => $oldHousehold->id, ]); } public function test_admin_can_transfer_resident_leaving_others_active(): void { Sanctum::actingAs($this->admin); // 1. Create old household with head and spouse $head = User::factory()->create(['role' => User::ROLE_RESIDENT]); $spouse = User::factory()->create(['role' => User::ROLE_RESIDENT]); $oldHousehold = Household::create([ 'tenant_id' => $this->defaultTenant->id, 'head_user_id' => $head->id, 'barangay_id' => $this->barangay->id, 'address_line' => '789 Shared St', 'coordinates' => new Point(14.5, 120.5, 4326), 'household_size' => 2, 'verification_status' => Household::VERIFICATION_APPROVED, ]); $headMember = HouseholdMember::create([ 'household_id' => $oldHousehold->id, 'user_id' => $head->id, 'relationship' => HouseholdMember::RELATIONSHIP_HEAD, 'full_name' => $head->full_name, ]); $spouseMember = HouseholdMember::create([ 'household_id' => $oldHousehold->id, 'user_id' => $spouse->id, 'relationship' => HouseholdMember::RELATIONSHIP_SPOUSE, 'full_name' => $spouse->full_name, ]); // 2. Transfer spouse to target household $payload = [ 'relationship' => 'sibling', 'user_id' => $spouse->id, ]; $response = $this->postJson("/api/v1/admin/households/{$this->household->uuid}/members", $payload); $response->assertStatus(201); // 3. Assert spouse is added to target household $this->assertDatabaseHas('household_members', [ 'household_id' => $this->household->id, 'user_id' => $spouse->id, 'relationship' => 'sibling', ]); // 4. Assert old household is NOT deleted (since head is still there) $this->assertDatabaseHas('households', [ 'id' => $oldHousehold->id, 'deleted_at' => null, ]); // 5. Assert old spouse member is soft deleted $this->assertSoftDeleted('household_members', [ 'id' => $spouseMember->id, ]); } }