seed(DatabaseSeeder::class); $admin = User::query()->where('email', 'admin@prn.local')->firstOrFail(); $election = Election::query()->where('code', 'PRN2026')->firstOrFail(); $this->actingAs($admin) ->post(route('admin.setup.bahagian.store'), [ 'election_id' => $election->id, 'code' => 'B999', 'name' => 'Bahagian Ujian', ]) ->assertRedirect(route('admin.setup.bahagian.index')); $bahagian = BahagianPilihanraya::query()->where('code', 'B999')->firstOrFail(); $this->actingAs($admin) ->post(route('admin.setup.daerah.store'), [ 'election_id' => $election->id, 'bahagian_pilihanraya_id' => $bahagian->id, 'code' => 'D999', 'name' => 'Daerah Ujian', ]) ->assertRedirect(route('admin.setup.daerah.index')); $daerah = DaerahMengundi::query()->where('code', 'D999')->firstOrFail(); $this->actingAs($admin) ->post(route('admin.setup.pusat.store'), [ 'election_id' => $election->id, 'daerah_mengundi_id' => $daerah->id, 'code' => 'PM999', 'name' => 'SK Ujian', 'address' => 'Jalan Ujian', 'saluran_count' => 2, 'ktm_count' => 1, 'polis_iring_count' => 1, 'kp_count' => 4, 'ppm_count' => 1, 'kpdp_count' => 1, 'papm_count' => 3, 'jkm_count' => 1, 'kkm_count' => 1, 'calon_tambahan_count' => 1, ]) ->assertRedirect(route('admin.setup.pusat.index')); $pusat = PusatMengundi::query()->where('code', 'PM999')->firstOrFail(); $this->assertNotNull($pusat->public_uuid); $this->assertStringContainsString('/pohon/'.$pusat->public_uuid, (string) $pusat->registration_url); $this->assertNotNull($pusat->qr_code_path); Storage::disk('local')->assertExists($pusat->qr_code_path); $this->actingAs($admin) ->post(route('admin.setup.saluran.store'), [ 'election_id' => $election->id, 'pusat_mengundi_id' => $pusat->id, 'number' => '3', 'voter_count' => 500, ]) ->assertRedirect(route('admin.setup.saluran.index')); $this->assertDatabaseHas('saluran_mengundis', [ 'pusat_mengundi_id' => $pusat->id, 'number' => '1', ]); } public function test_admin_can_create_position_quota_and_assign_ppm(): void { $this->seed(DatabaseSeeder::class); $admin = User::query()->where('email', 'admin@prn.local')->firstOrFail(); $election = Election::query()->where('code', 'PRN2026')->firstOrFail(); $pusat = PusatMengundi::query()->where('code', 'PM001')->firstOrFail(); $saluran = SaluranMengundi::query()->where('pusat_mengundi_id', $pusat->id)->firstOrFail(); $position = Position::query()->where('code', 'KP')->firstOrFail(); $newPpm = User::factory()->create(); $this->actingAs($admin) ->post(route('admin.setup.quotas.store'), [ 'election_id' => $election->id, 'pusat_mengundi_id' => $pusat->id, 'saluran_mengundi_id' => $saluran->id, 'position_id' => $position->id, 'quota' => 6, ]) ->assertRedirect(route('admin.setup.quotas.index')); $quota = PositionQuota::query() ->where('election_id', $election->id) ->where('pusat_mengundi_id', $pusat->id) ->where('saluran_mengundi_id', $saluran->id) ->where('position_id', $position->id) ->firstOrFail(); $this->assertSame(6, $quota->quota); $this->actingAs($admin) ->post(route('admin.setup.pusat.assign-ppm', $pusat), [ 'user_id' => $newPpm->id, ]) ->assertRedirect(route('admin.setup.pusat.show', $pusat)); $newPpm->refresh(); $this->assertTrue($newPpm->hasRole('PPM')); $ppmPosition = Position::query()->where('code', 'PPM')->firstOrFail(); $this->assertTrue(StaffAssignment::query() ->where('election_id', $election->id) ->where('pusat_mengundi_id', $pusat->id) ->where('position_id', $ppmPosition->id) ->where('user_id', $newPpm->id) ->where('status', 'active') ->exists()); } public function test_non_admin_cannot_access_admin_setup_routes(): void { $this->seed(DatabaseSeeder::class); $ktm = User::query()->where('email', 'ktm@prn.local')->firstOrFail(); $this->actingAs($ktm) ->get(route('admin.setup.index')) ->assertForbidden(); } public function test_public_qr_placeholder_uses_public_uuid_without_authentication(): void { $this->seed(DatabaseSeeder::class); $pusat = PusatMengundi::query()->where('code', 'PM001')->firstOrFail(); $this->get(route('public.applications.create', [ 'pusatMengundi' => $pusat->public_uuid, ])) ->assertOk() ->assertSee($pusat->name) ->assertSee($pusat->code); } }