184 lines
6.3 KiB
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);
|
|
}
|
|
}
|