162 lines
5.4 KiB
PHP
162 lines
5.4 KiB
PHP
<?php
|
|
|
|
namespace Tests\Feature\Api\V1\Scan;
|
|
|
|
use App\Models\DropOffPoint;
|
|
use App\Models\Household;
|
|
use App\Models\QrCode;
|
|
use App\Models\QrCodeBatch;
|
|
use App\Models\User;
|
|
use App\Services\Qr\BatchGenerator;
|
|
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 ScanTest extends TestCase
|
|
{
|
|
use RefreshDatabase;
|
|
|
|
private User $scanner;
|
|
|
|
private DropOffPoint $dop;
|
|
|
|
private Household $household;
|
|
|
|
private QrCode $activeCode;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
parent::setUp();
|
|
$this->seed([RoleSeeder::class, SamplePsgcSeeder::class, SampleDropOffPointsSeeder::class]);
|
|
$this->scanner = User::factory()->create(['role' => User::ROLE_SCANNER, 'status' => 'active']);
|
|
$this->dop = DropOffPoint::where('code', 'DOP-QC-DLM-01')->firstOrFail();
|
|
$this->household = Household::factory()->create(['barangay_id' => $this->dop->barangay_id]);
|
|
|
|
$batch = app(BatchGenerator::class)->generate(3, QrCodeBatch::PURPOSE_FREE);
|
|
$this->activeCode = $batch->codes()->first();
|
|
$this->activeCode->forceFill([
|
|
'assigned_to_household_id' => $this->household->id,
|
|
'status' => 'active',
|
|
'allocated_at' => now(),
|
|
'activated_at' => now(),
|
|
])->save();
|
|
}
|
|
|
|
public function test_valid_scan_at_dop_marks_code_used(): void
|
|
{
|
|
Sanctum::actingAs($this->scanner);
|
|
|
|
$response = $this->postJson('/api/v1/scanner/scan', [
|
|
'serial' => $this->activeCode->serial,
|
|
'drop_off_point_id' => $this->dop->id,
|
|
'lat' => 14.6539,
|
|
'lng' => 121.0685,
|
|
'weight_kg' => 5,
|
|
'waste_type' => 'general',
|
|
]);
|
|
|
|
$response->assertOk()
|
|
->assertJsonPath('data.status', 'used');
|
|
$this->assertDatabaseHas('collection_logs', [
|
|
'qr_code_id' => $this->activeCode->id,
|
|
'verification_status' => 'valid',
|
|
]);
|
|
}
|
|
|
|
public function test_scan_far_from_dop_rejected(): void
|
|
{
|
|
Sanctum::actingAs($this->scanner);
|
|
|
|
$response = $this->postJson('/api/v1/scanner/scan', [
|
|
'serial' => $this->activeCode->serial,
|
|
'drop_off_point_id' => $this->dop->id,
|
|
'lat' => 8.0,
|
|
'lng' => 124.0,
|
|
]);
|
|
|
|
$response->assertStatus(422);
|
|
$this->assertStringStartsWith('out_of_range', $response->json('errors.reason.0'));
|
|
}
|
|
|
|
public function test_unknown_serial_rejected(): void
|
|
{
|
|
Sanctum::actingAs($this->scanner);
|
|
|
|
$response = $this->postJson('/api/v1/scanner/scan', [
|
|
'serial' => 'PH-XX-9999-9999-999999-XX',
|
|
'drop_off_point_id' => $this->dop->id,
|
|
'lat' => 14.6539, 'lng' => 121.0685,
|
|
]);
|
|
|
|
$response->assertStatus(422)
|
|
->assertJsonPath('errors.reason.0', 'not_found');
|
|
}
|
|
|
|
public function test_already_used_serial_rejected_as_duplicate(): void
|
|
{
|
|
$this->activeCode->forceFill(['status' => 'used', 'used_at' => now()])->save();
|
|
Sanctum::actingAs($this->scanner);
|
|
|
|
$response = $this->postJson('/api/v1/scanner/scan', [
|
|
'serial' => $this->activeCode->serial,
|
|
'drop_off_point_id' => $this->dop->id,
|
|
'lat' => 14.6539, 'lng' => 121.0685,
|
|
]);
|
|
|
|
$response->assertStatus(422)
|
|
->assertJsonPath('errors.reason.0', 'duplicate');
|
|
}
|
|
|
|
public function test_unassigned_code_rejected(): void
|
|
{
|
|
$this->activeCode->forceFill(['status' => 'unassigned'])->save();
|
|
Sanctum::actingAs($this->scanner);
|
|
|
|
$response = $this->postJson('/api/v1/scanner/scan', [
|
|
'serial' => $this->activeCode->serial,
|
|
'drop_off_point_id' => $this->dop->id,
|
|
'lat' => 14.6539, 'lng' => 121.0685,
|
|
]);
|
|
|
|
$response->assertStatus(422);
|
|
}
|
|
|
|
public function test_bulk_scan_processes_each_independently(): void
|
|
{
|
|
$batch = app(BatchGenerator::class)->generate(3, QrCodeBatch::PURPOSE_FREE);
|
|
$batch->codes()->update([
|
|
'assigned_to_household_id' => $this->household->id,
|
|
'status' => 'active',
|
|
]);
|
|
$codes = $batch->codes()->limit(2)->get();
|
|
Sanctum::actingAs($this->scanner);
|
|
|
|
$response = $this->postJson('/api/v1/scanner/scan/bulk', [
|
|
'scans' => [
|
|
['serial' => $codes[0]->serial, 'drop_off_point_id' => $this->dop->id, 'lat' => 14.6539, 'lng' => 121.0685],
|
|
['serial' => $codes[1]->serial, 'drop_off_point_id' => $this->dop->id, 'lat' => 14.6539, 'lng' => 121.0685],
|
|
['serial' => 'PH-bogus', 'drop_off_point_id' => $this->dop->id, 'lat' => 14.6539, 'lng' => 121.0685],
|
|
],
|
|
]);
|
|
|
|
$response->assertOk()
|
|
->assertJsonPath('data.accepted_count', 2)
|
|
->assertJsonPath('data.rejected_count', 1);
|
|
}
|
|
|
|
public function test_resident_blocked_from_scanning(): void
|
|
{
|
|
$resident = User::factory()->create(['role' => User::ROLE_RESIDENT, 'status' => 'active']);
|
|
Sanctum::actingAs($resident);
|
|
|
|
$this->postJson('/api/v1/scanner/scan', [
|
|
'serial' => $this->activeCode->serial,
|
|
'drop_off_point_id' => $this->dop->id,
|
|
'lat' => 14.6539, 'lng' => 121.0685,
|
|
])->assertStatus(403);
|
|
}
|
|
}
|