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); } }