seed(AdminRbacAndUserSeeder::class); $this->artisan('lottery:admin-auth-sync')->assertExitCode(0); $playStore = resourceLegacySlugs('admin.config.play-versions.store'); expect($playStore)->toBe(['prd.play_switch.manage']); $oddsStore = resourceLegacySlugs('admin.config.odds-versions.store'); expect($oddsStore)->toContain('prd.odds.manage') ->and($oddsStore)->not->toContain('prd.play_switch.manage'); $riskCapStore = resourceLegacySlugs('admin.config.risk-cap-versions.store'); expect($riskCapStore)->toBe(['prd.risk_cap.manage']); }); test('user with report view only cannot create report export job', function (): void { $this->seed(AdminRbacAndUserSeeder::class); $this->artisan('lottery:admin-auth-sync')->assertExitCode(0); $admin = AdminUser::query()->create([ 'username' => 'report_view_only', 'name' => 'Tester', 'email' => null, 'password' => Hash::make('secret-strong'), 'status' => 0, ]); $role = AdminRole::query()->create(['slug' => 'report_view_only', 'name' => 'Report view only']); $role->syncLegacyPermissionSlugs(['prd.report.view', 'prd.dashboard.view']); $siteId = AdminUser::defaultAdminSiteId(); $admin->roles()->sync([ (int) $role->id => ['site_id' => $siteId, 'granted_at' => now()], ]); $token = $admin->createToken('test', ['*'], now()->addDay())->plainTextToken; $this->withHeader('Authorization', 'Bearer '.$token) ->getJson('/api/v1/admin/reports/daily-profit') ->assertOk(); $this->withHeader('Authorization', 'Bearer '.$token) ->postJson('/api/v1/admin/report-jobs', [ 'report_type' => 'daily_profit_summary', 'export_format' => 'csv', 'parameters' => [ 'date_from' => '2026-05-01', 'date_to' => '2026-05-07', ], ]) ->assertForbidden(); }); /** @return list */ function resourceLegacySlugs(string $code): array { $resource = collect(AdminAuthorizationRegistry::resourceDefinitions()) ->firstWhere('code', $code); expect($resource)->not->toBeNull(); return $resource['legacy_permission_slugs'] ?? []; }