Files
Verde-Web/tests/Feature/Api/V1/DropOff/DropOffPointTest.php

184 lines
6.3 KiB
PHP

<?php
namespace Tests\Feature\Api\V1\DropOff;
use App\Models\DropOffPoint;
use App\Models\User;
use Database\Seeders\RoleSeeder;
use Database\Seeders\SampleDropOffPointsSeeder;
use Database\Seeders\SamplePsgcSeeder;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Laravel\Sanctum\Sanctum;
use Tests\TestCase;
class DropOffPointTest extends TestCase
{
use RefreshDatabase;
protected function setUp(): void
{
parent::setUp();
$this->seed([RoleSeeder::class, SamplePsgcSeeder::class, SampleDropOffPointsSeeder::class]);
}
public function test_nearby_returns_sorted_by_distance(): void
{
$response = $this->getJson('/api/v1/drop-off-points/nearby?lat=14.6539&lng=121.0685&radius_km=5');
$response->assertOk();
$items = $response->json('data');
$this->assertGreaterThanOrEqual(2, count($items));
$first = $items[0];
$this->assertSame('Diliman Triangle DOP', $first['name']);
$this->assertEqualsWithDelta(0, $first['distance_meters'], 50.0);
$distances = array_column($items, 'distance_meters');
$sorted = $distances;
sort($sorted);
$this->assertSame($sorted, $distances);
}
public function test_nearby_excludes_inactive_dops(): void
{
DropOffPoint::factory()->at(14.6539, 121.0685)->create([
'name' => 'Closed DOP',
'status' => DropOffPoint::STATUS_CLOSED,
]);
$response = $this->getJson('/api/v1/drop-off-points/nearby?lat=14.6539&lng=121.0685&radius_km=1');
$response->assertOk();
$names = array_column($response->json('data'), 'name');
$this->assertNotContains('Closed DOP', $names);
}
public function test_nearby_respects_radius(): void
{
$response = $this->getJson('/api/v1/drop-off-points/nearby?lat=14.6539&lng=121.0685&radius_km=0.1');
$response->assertOk();
// Only Diliman itself is within 100m
$this->assertCount(1, $response->json('data'));
}
public function test_nearby_validates_coordinates(): void
{
$this->getJson('/api/v1/drop-off-points/nearby?lat=999&lng=121')
->assertStatus(422)
->assertJsonValidationErrors(['lat']);
}
public function test_show_returns_dop_details(): void
{
$dop = DropOffPoint::first();
$response = $this->getJson("/api/v1/drop-off-points/{$dop->uuid}");
$response->assertOk()
->assertJsonPath('data.id', $dop->uuid)
->assertJsonPath('data.code', $dop->code);
}
public function test_admin_can_create_dop(): void
{
$admin = User::factory()->create(['role' => User::ROLE_ADMIN, 'status' => User::STATUS_ACTIVE]);
Sanctum::actingAs($admin);
$response = $this->postJson('/api/v1/admin/drop-off-points', [
'name' => 'New DOP',
'code' => 'DOP-NEW-01',
'lat' => 14.5,
'lng' => 121.0,
'address_line' => '1 New St',
'capacity_kg' => 500,
'accepted_waste_types' => ['general'],
]);
$response->assertCreated()
->assertJsonPath('data.code', 'DOP-NEW-01');
$this->assertDatabaseHas('drop_off_points', ['code' => 'DOP-NEW-01']);
}
public function test_admin_can_update_dop(): void
{
$admin = User::factory()->create(['role' => User::ROLE_ADMIN, 'status' => User::STATUS_ACTIVE]);
Sanctum::actingAs($admin);
$dop = DropOffPoint::first();
$response = $this->patchJson("/api/v1/admin/drop-off-points/{$dop->uuid}", [
'status' => DropOffPoint::STATUS_MAINTENANCE,
]);
$response->assertOk()
->assertJsonPath('data.status', 'maintenance');
}
public function test_admin_can_log_capacity(): void
{
$admin = User::factory()->create(['role' => User::ROLE_ADMIN, 'status' => User::STATUS_ACTIVE]);
Sanctum::actingAs($admin);
$dop = DropOffPoint::first();
$response = $this->postJson("/api/v1/admin/drop-off-points/{$dop->uuid}/capacity", [
'fill_percent' => 75,
'notes' => 'Friday afternoon',
]);
$response->assertCreated()
->assertJsonPath('data.fill_percent', 75);
$this->assertDatabaseHas('drop_off_capacity_logs', [
'drop_off_point_id' => $dop->id,
'fill_percent' => 75,
]);
}
public function test_resident_blocked_from_admin_dop_endpoints(): void
{
$resident = User::factory()->create(['role' => User::ROLE_RESIDENT, 'status' => User::STATUS_ACTIVE]);
Sanctum::actingAs($resident);
$this->postJson('/api/v1/admin/drop-off-points', [
'name' => 'X', 'code' => 'X', 'lat' => 0, 'lng' => 0, 'address_line' => 'X',
])->assertStatus(403);
}
public function test_admin_can_delete_dop(): void
{
$admin = User::factory()->create(['role' => User::ROLE_ADMIN, 'status' => User::STATUS_ACTIVE]);
Sanctum::actingAs($admin);
$dop = DropOffPoint::first();
$this->deleteJson("/api/v1/admin/drop-off-points/{$dop->uuid}")->assertOk();
$this->assertSoftDeleted('drop_off_points', ['id' => $dop->id]);
}
public function test_dop_status_transition_reassigns_households(): void
{
$admin = User::factory()->create(['role' => User::ROLE_ADMIN, 'status' => User::STATUS_ACTIVE]);
Sanctum::actingAs($admin);
$dop1 = DropOffPoint::where('status', DropOffPoint::STATUS_ACTIVE)->first();
$household = \App\Models\Household::create([
'tenant_id' => $dop1->tenant_id,
'head_user_id' => User::factory()->create()->id,
'barangay_id' => $dop1->barangay_id,
'address_line' => '123 Test St',
'coordinates' => new \MatanYadaev\EloquentSpatial\Objects\Point(14.6539, 121.0685, 4326),
'household_size' => 3,
'assigned_drop_off_point_id' => $dop1->id,
'verification_status' => 'approved',
]);
$response = $this->patchJson("/api/v1/admin/drop-off-points/{$dop1->uuid}", [
'status' => DropOffPoint::STATUS_MAINTENANCE,
]);
$response->assertOk();
$household->refresh();
$this->assertNotEquals($dop1->id, $household->assigned_drop_off_point_id);
}
}