176 lines
6.8 KiB
PHP
176 lines
6.8 KiB
PHP
<?php
|
|
|
|
namespace Tests\Feature\Api\V1\Customer;
|
|
|
|
use App\Models\CollectionLog;
|
|
use App\Models\CollectionTeam;
|
|
use App\Models\DropOffPoint;
|
|
use App\Models\Household;
|
|
use App\Models\PartnerStore;
|
|
use App\Models\QrCodeBatch;
|
|
use App\Models\Route;
|
|
use App\Models\RouteStop;
|
|
use App\Models\Trip;
|
|
use App\Models\User;
|
|
use App\Notifications\HouseholdApproved;
|
|
use App\Services\Qr\BatchGenerator;
|
|
use Database\Seeders\RoleSeeder;
|
|
use Database\Seeders\SampleDropOffPointsSeeder;
|
|
use Database\Seeders\SamplePsgcSeeder;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Illuminate\Support\Facades\Notification;
|
|
use Laravel\Sanctum\Sanctum;
|
|
use MatanYadaev\EloquentSpatial\Objects\Point;
|
|
use Tests\TestCase;
|
|
|
|
class CustomerEndpointsTest extends TestCase
|
|
{
|
|
use RefreshDatabase;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
parent::setUp();
|
|
$this->seed([RoleSeeder::class, SamplePsgcSeeder::class, SampleDropOffPointsSeeder::class]);
|
|
}
|
|
|
|
public function test_partner_stores_nearby_returns_active_stores_sorted_by_distance(): void
|
|
{
|
|
$closer = PartnerStore::factory()->create([
|
|
'business_name' => 'Closer',
|
|
'status' => 'active',
|
|
'coordinates' => new Point(14.6539, 121.0685, 4326),
|
|
]);
|
|
$farther = PartnerStore::factory()->create([
|
|
'business_name' => 'Farther',
|
|
'status' => 'active',
|
|
'coordinates' => new Point(14.7, 121.1, 4326),
|
|
]);
|
|
PartnerStore::factory()->create([
|
|
'business_name' => 'Suspended',
|
|
'status' => 'suspended',
|
|
'coordinates' => new Point(14.6539, 121.0685, 4326),
|
|
]);
|
|
|
|
$response = $this->getJson('/api/v1/partner-stores/nearby?lat=14.6539&lng=121.0685&radius_km=10');
|
|
|
|
$response->assertOk();
|
|
$names = collect($response->json('data'))->pluck('business_name')->all();
|
|
$this->assertSame(['Closer', 'Farther'], $names);
|
|
$this->assertNotContains('Suspended', $names);
|
|
}
|
|
|
|
public function test_me_collections_returns_only_my_household_logs(): void
|
|
{
|
|
$resident = User::factory()->create(['role' => User::ROLE_RESIDENT, 'status' => 'active']);
|
|
$myHousehold = Household::factory()->create(['head_user_id' => $resident->id]);
|
|
$otherHousehold = Household::factory()->create();
|
|
$dop = DropOffPoint::first();
|
|
$batch = app(BatchGenerator::class)->generate(2, QrCodeBatch::PURPOSE_FREE);
|
|
$codes = $batch->codes()->get();
|
|
|
|
CollectionLog::create([
|
|
'qr_code_id' => $codes[0]->id,
|
|
'household_id' => $myHousehold->id,
|
|
'drop_off_point_id' => $dop->id,
|
|
'scanned_at' => now()->subHour(),
|
|
'coordinates_at_scan' => new Point(14.6, 121.0, 4326),
|
|
'weight_kg' => 5,
|
|
'verification_status' => 'valid',
|
|
]);
|
|
CollectionLog::create([
|
|
'qr_code_id' => $codes[1]->id,
|
|
'household_id' => $otherHousehold->id,
|
|
'drop_off_point_id' => $dop->id,
|
|
'scanned_at' => now(),
|
|
'coordinates_at_scan' => new Point(14.6, 121.0, 4326),
|
|
'weight_kg' => 7,
|
|
'verification_status' => 'valid',
|
|
]);
|
|
|
|
Sanctum::actingAs($resident);
|
|
$response = $this->getJson('/api/v1/me/collections');
|
|
|
|
$response->assertOk();
|
|
$items = $response->json('data');
|
|
$this->assertCount(1, $items);
|
|
$this->assertSame(5, $items[0]['weight_kg']);
|
|
}
|
|
|
|
public function test_upcoming_pickups_finds_trips_for_my_dop(): void
|
|
{
|
|
$resident = User::factory()->create(['role' => User::ROLE_RESIDENT, 'status' => 'active']);
|
|
$dop = DropOffPoint::where('code', 'DOP-QC-DLM-01')->firstOrFail();
|
|
Household::factory()->create([
|
|
'head_user_id' => $resident->id,
|
|
'assigned_drop_off_point_id' => $dop->id,
|
|
]);
|
|
|
|
$driver = User::factory()->create(['role' => User::ROLE_DRIVER]);
|
|
$team = CollectionTeam::create(['name' => 'T', 'driver_id' => $driver->id, 'status' => 'active']);
|
|
$route = Route::create(['name' => 'R', 'code' => 'RT-UC', 'status' => 'active']);
|
|
RouteStop::create(['route_id' => $route->id, 'drop_off_point_id' => $dop->id, 'sequence' => 1]);
|
|
|
|
// Tomorrow trip — should appear
|
|
Trip::create([
|
|
'trip_number' => 'TRIP-T1', 'route_id' => $route->id, 'team_id' => $team->id,
|
|
'scheduled_date' => now()->addDay()->toDateString(), 'status' => 'scheduled',
|
|
]);
|
|
// Past completed — should not
|
|
Trip::create([
|
|
'trip_number' => 'TRIP-T0', 'route_id' => $route->id, 'team_id' => $team->id,
|
|
'scheduled_date' => now()->subDay()->toDateString(), 'status' => 'completed',
|
|
]);
|
|
|
|
Sanctum::actingAs($resident);
|
|
$response = $this->getJson('/api/v1/me/upcoming-pickups');
|
|
|
|
$response->assertOk()
|
|
->assertJsonPath('data.household_assigned', true)
|
|
->assertJsonPath('data.drop_off_point.name', $dop->name);
|
|
$this->assertCount(1, $response->json('data.pickups'));
|
|
}
|
|
|
|
public function test_upcoming_pickups_empty_when_no_household(): void
|
|
{
|
|
$resident = User::factory()->create(['role' => User::ROLE_RESIDENT, 'status' => 'active']);
|
|
Sanctum::actingAs($resident);
|
|
|
|
$this->getJson('/api/v1/me/upcoming-pickups')
|
|
->assertOk()
|
|
->assertJsonPath('data.household_assigned', false);
|
|
}
|
|
|
|
public function test_my_notifications_inbox_paginates_and_marks_read(): void
|
|
{
|
|
$resident = User::factory()->create(['role' => User::ROLE_RESIDENT, 'status' => 'active']);
|
|
$h = Household::factory()->create(['head_user_id' => $resident->id]);
|
|
Notification::send($resident, new HouseholdApproved($h, 10));
|
|
|
|
Sanctum::actingAs($resident);
|
|
$list = $this->getJson('/api/v1/me/notifications');
|
|
$list->assertOk();
|
|
$this->assertCount(1, $list->json('data'));
|
|
$this->assertSame(1, $list->json('meta.unread_count'));
|
|
|
|
$id = $list->json('data.0.id');
|
|
$this->postJson("/api/v1/me/notifications/{$id}/read")
|
|
->assertOk();
|
|
|
|
$this->getJson('/api/v1/me/notifications/unread-count')
|
|
->assertOk()
|
|
->assertJsonPath('data.unread_count', 0);
|
|
}
|
|
|
|
public function test_mark_all_read(): void
|
|
{
|
|
$resident = User::factory()->create(['role' => User::ROLE_RESIDENT, 'status' => 'active']);
|
|
$h = Household::factory()->create(['head_user_id' => $resident->id]);
|
|
Notification::send($resident, new HouseholdApproved($h, 5));
|
|
Notification::send($resident, new HouseholdApproved($h, 7));
|
|
|
|
Sanctum::actingAs($resident);
|
|
$r = $this->postJson('/api/v1/me/notifications/mark-all-read');
|
|
$r->assertOk()->assertJsonPath('data.marked_read', 2);
|
|
}
|
|
}
|