Files
Verde-Web/tests/Feature/Api/V1/E2E/GoldenPathTest.php

133 lines
5.7 KiB
PHP

<?php
namespace Tests\Feature\Api\V1\E2E;
use App\Models\DropOffPoint;
use App\Models\PartnerStore;
use App\Models\QrCode;
use App\Models\User;
use App\Models\Truck;
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 GoldenPathTest extends TestCase
{
use RefreshDatabase;
public function test_full_golden_path_e2e_flow(): void
{
// 1. Setup Personas & Environment
$this->seed([RoleSeeder::class, SamplePsgcSeeder::class, SampleDropOffPointsSeeder::class]);
$tenantId = \App\Models\Tenant::first()->id;
$admin = User::factory()->create(['role' => User::ROLE_ADMIN, 'status' => 'active', 'tenant_id' => $tenantId]);
$storeOwner = User::factory()->create(['role' => User::ROLE_STORE_PARTNER, 'status' => 'active', 'tenant_id' => $tenantId]);
$resident = User::factory()->create(['role' => User::ROLE_RESIDENT, 'status' => 'active', 'tenant_id' => $tenantId]);
$scanner = User::factory()->create(['role' => User::ROLE_SCANNER, 'status' => 'active', 'tenant_id' => $tenantId]);
$driver = User::factory()->create(['role' => User::ROLE_DRIVER, 'status' => 'active', 'tenant_id' => $tenantId]);
$dop = DropOffPoint::first();
$store = PartnerStore::factory()->create([
'owner_user_id' => $storeOwner->id,
'status' => 'active',
'commission_rate_percent' => 10,
'tenant_id' => $tenantId,
]);
$truck = Truck::create(['tenant_id' => \App\Models\Tenant::first()->id, 'plate_number' => 'ABC-1234', 'status' => 'active', 'capacity_kg' => 5000]);
// 1b. Create a Household for the batch
$household = \App\Models\Household::factory()->create();
// 2. Admin Generates Wholesale Batch
Sanctum::actingAs($admin);
$batchResponse = $this->postJson('/api/v1/admin/qr-batches', [
'purpose' => \App\Models\QrCodeBatch::PURPOSE_STORE,
'quantity' => 10,
'cost_per_code' => 50.00,
'notes' => 'E2E Golden Path Batch',
'household_id' => $household->uuid,
]);
$batchResponse->assertCreated();
$batchId = $batchResponse->json('data.id');
// 3. Admin Allocates/Issues Batch to Store
$issueResponse = $this->postJson("/api/v1/admin/partner-stores/{$store->uuid}/issue-inventory", [
'quantity' => 10,
'wholesale_price_centavos' => 800,
]);
$issueResponse->assertCreated();
$this->assertEquals(10, QrCode::where('assigned_to_store_id', $store->id)->count());
// 4. Store Sells 5 Codes to Resident
Sanctum::actingAs($storeOwner);
$saleResponse = $this->postJson("/api/v1/store/sales", [
'user_id' => $resident->id,
'quantity' => 5,
]);
$saleResponse->assertCreated();
$activeCodes = QrCode::where('assigned_to_user_id', $resident->id)
->where('status', 'active')
->get();
$this->assertCount(5, $activeCodes);
$route = \App\Models\Route::create(['tenant_id' => \App\Models\Tenant::first()->id, 'name' => 'Test Route', 'code' => 'R-TEST', 'status' => 'active']);
$team = \App\Models\CollectionTeam::create(['tenant_id' => \App\Models\Tenant::first()->id, 'name' => 'Team Alpha', 'status' => 'active', 'driver_id' => $driver->id, 'scanner_id' => $scanner->id]);
// 5. Admin Assigns a Trip to the Driver
Sanctum::actingAs($admin);
$tripResponse = $this->postJson('/api/v1/admin/trips', [
'driver_id' => $driver->id,
'truck_id' => $truck->id,
'route_id' => $route->id,
'team_id' => $team->id,
'scheduled_date' => now()->toDateString(),
'drop_off_point_ids' => [$dop->id],
]);
$tripResponse->assertCreated();
$tripId = $tripResponse->json('data.id');
// 6. Driver Starts Trip & Pings Location
Sanctum::actingAs($driver);
$this->postJson("/api/v1/driver/trips/{$tripId}/start")->assertOk();
$this->postJson("/api/v1/driver/trucks/{$truck->id}/location", [
'lat' => 14.6539,
'lng' => 121.0685,
])->assertOk();
// 7. Scanner Scans the 5 Resident Bags at the DOP
Sanctum::actingAs($scanner);
$scans = $activeCodes->map(function ($code) use ($dop) {
return [
'serial' => $code->serial,
'drop_off_point_id' => $dop->id,
'lat' => 14.6539,
'lng' => 121.0685,
];
})->toArray();
$scanResponse = $this->postJson('/api/v1/scanner/scan/bulk', [
'scans' => $scans,
]);
$scanResponse->assertOk()->assertJsonPath('data.accepted_count', 5);
// 8. Driver Arrives at DOP & Completes Trip
Sanctum::actingAs($driver);
// We need to fetch the stop id first since the route is /api/v1/driver/trips/{trip}/stops/{stop}/arrive
$stopId = \App\Models\TripStop::where('trip_id', $tripId)->where('drop_off_point_id', $dop->id)->first()->id;
$this->postJson("/api/v1/driver/trips/{$tripId}/stops/{$stopId}/arrive")->assertOk();
$this->postJson("/api/v1/driver/trips/{$tripId}/complete")->assertOk();
// 9. Final Database Assertions
$this->assertEquals(5, QrCode::where('assigned_to_user_id', $resident->id)->where('status', 'used')->count());
$this->assertDatabaseHas('trips', ['id' => $tripId, 'status' => 'completed']);
$this->assertDatabaseHas('collection_logs', ['qr_code_id' => $activeCodes->first()->id]);
}
}