Files
Verde-Web/tests/Feature/Api/V1/Admin/AdminHouseholdMemberTest.php

361 lines
12 KiB
PHP

<?php
namespace Tests\Feature\Api\V1\Admin;
use App\Models\Barangay;
use App\Models\Household;
use App\Models\HouseholdMember;
use App\Models\User;
use Database\Seeders\RoleSeeder;
use Database\Seeders\SamplePsgcSeeder;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Laravel\Sanctum\Sanctum;
use MatanYadaev\EloquentSpatial\Objects\LineString;
use MatanYadaev\EloquentSpatial\Objects\Point;
use MatanYadaev\EloquentSpatial\Objects\Polygon;
use Tests\TestCase;
class AdminHouseholdMemberTest extends TestCase
{
use RefreshDatabase;
private User $admin;
private Barangay $barangay;
private Household $household;
protected function setUp(): void
{
parent::setUp();
$this->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,
]);
}
}