seed(RoleSeeder::class); } public function test_qr_expire_transitions_past_due_codes_to_expired(): void { $batch = app(BatchGenerator::class)->generate(3, QrCodeBatch::PURPOSE_FREE); QrCode::query()->where('batch_id', $batch->id)->update([ 'status' => 'active', 'expires_at' => now()->subDay(), ]); $this->artisan('qr:expire')->assertSuccessful(); $this->assertSame(3, QrCode::where('status', 'expired')->count()); } public function test_qr_expire_dry_run_does_not_change_state(): void { $batch = app(BatchGenerator::class)->generate(2, QrCodeBatch::PURPOSE_FREE); QrCode::query()->where('batch_id', $batch->id)->update([ 'status' => 'active', 'expires_at' => now()->subDay(), ]); $this->artisan('qr:expire', ['--dry' => true])->assertSuccessful(); $this->assertSame(0, QrCode::where('status', 'expired')->count()); } public function test_qr_expire_does_not_touch_unexpired_or_used_codes(): void { $batch = app(BatchGenerator::class)->generate(2, QrCodeBatch::PURPOSE_FREE); $codes = $batch->codes()->get(); $codes[0]->forceFill(['status' => 'used', 'expires_at' => now()->subYear()])->save(); $codes[1]->forceFill(['status' => 'active', 'expires_at' => now()->addYear()])->save(); $this->artisan('qr:expire')->assertSuccessful(); $this->assertSame(0, QrCode::where('status', 'expired')->count()); } public function test_trucks_prune_locations_drops_old_rows(): void { $truck = Truck::create(['plate_number' => 'NCR-1', 'status' => 'active']); TruckLocationHistory::create([ 'truck_id' => $truck->id, 'coordinates' => new Point(14.6, 121.0, 4326), 'recorded_at' => now()->subDays(10), ]); TruckLocationHistory::create([ 'truck_id' => $truck->id, 'coordinates' => new Point(14.6, 121.0, 4326), 'recorded_at' => now()->subHours(2), ]); $this->artisan('trucks:prune-locations', ['--days' => 7])->assertSuccessful(); $this->assertSame(1, TruckLocationHistory::count()); } }