create([ 'username' => 'settings_editor', 'name' => 'Settings Editor', 'email' => null, 'password' => Hash::make('secret-strong'), 'status' => 0, ]); $role = AdminRole::query()->create([ 'slug' => 'settings_role', 'name' => 'Settings Role', ]); $role->syncLegacyPermissionSlugs(['prd.payout.manage']); $admin->roles()->sync([ (int) $role->id => [ 'site_id' => AdminUser::defaultAdminSiteId(), 'granted_at' => now(), ], ]); return $admin->createToken('test', ['*'], now()->addDay())->plainTextToken; } function settingsReadOnlyToken(): string { $admin = AdminUser::query()->create([ 'username' => 'settings_readonly', 'name' => 'Settings Readonly', 'email' => null, 'password' => Hash::make('secret-strong'), 'status' => 0, ]); $role = AdminRole::query()->create([ 'slug' => 'settings_readonly_role', 'name' => 'Settings Readonly Role', ]); $role->syncLegacyPermissionSlugs(['prd.rebate.manage']); $admin->roles()->sync([ (int) $role->id => [ 'site_id' => AdminUser::defaultAdminSiteId(), 'granted_at' => now(), ], ]); return $admin->createToken('test', ['*'], now()->addDay())->plainTextToken; } test('admin can batch update settings in one request', function (): void { LotterySettings::put('draw.interval_minutes', 5, 'draw'); LotterySettings::put('draw.cooldown_minutes', 15, 'draw'); $token = settingsAdminToken(); $this->withHeader('Authorization', 'Bearer '.$token) ->putJson('/api/v1/admin/settings/batch', [ 'items' => [ ['key' => 'draw.interval_minutes', 'value' => 10], ['key' => 'draw.cooldown_minutes', 'value' => 20], ], ]) ->assertOk() ->assertJsonPath('data.items.0.key', 'draw.cooldown_minutes'); expect(LotterySetting::query()->where('setting_key', 'draw.interval_minutes')->value('value_json'))->toBe(10) ->and(LotterySetting::query()->where('setting_key', 'draw.cooldown_minutes')->value('value_json'))->toBe(20); }); test('admin settings batch update rejects empty items', function (): void { $token = settingsAdminToken(); $this->withHeader('Authorization', 'Bearer '.$token) ->putJson('/api/v1/admin/settings/batch', ['items' => []]) ->assertUnprocessable(); }); test('admin can batch update settings with false and empty string values', function (): void { LotterySettings::put('settlement.auto_run_on_tick', true, 'settlement'); LotterySettings::put('frontend.play_rules_html_zh', '
old
', 'frontend'); $token = settingsAdminToken(); $this->withHeader('Authorization', 'Bearer '.$token) ->putJson('/api/v1/admin/settings/batch', [ 'items' => [ ['key' => 'settlement.auto_run_on_tick', 'value' => false], ['key' => 'frontend.play_rules_html_zh', 'value' => ''], ], ]) ->assertOk() ->assertJsonPath('data.items.0.key', 'frontend.play_rules_html_zh') ->assertJsonPath('data.items.0.value', '') ->assertJsonPath('data.items.1.key', 'settlement.auto_run_on_tick') ->assertJsonPath('data.items.1.value', false); expect(LotterySetting::query()->where('setting_key', 'settlement.auto_run_on_tick')->value('value_json'))->toBeFalse() ->and(LotterySetting::query()->where('setting_key', 'frontend.play_rules_html_zh')->value('value_json'))->toBe(''); }); test('admin can update single setting with false value', function (): void { LotterySettings::put('settlement.apply_rebate_to_payout', true, 'settlement'); $token = settingsAdminToken(); $this->withHeader('Authorization', 'Bearer '.$token) ->putJson('/api/v1/admin/settings/settlement.apply_rebate_to_payout', [ 'value' => false, ]) ->assertOk() ->assertJsonPath('data.key', 'settlement.apply_rebate_to_payout') ->assertJsonPath('data.value', false); expect(LotterySetting::query()->where('setting_key', 'settlement.apply_rebate_to_payout')->value('value_json'))->toBeFalse(); }); test('non payout manager cannot batch update settlement settings', function (): void { LotterySettings::put('settlement.auto_payout_on_tick', true, 'settlement'); $token = settingsReadOnlyToken(); $this->withHeader('Authorization', 'Bearer '.$token) ->putJson('/api/v1/admin/settings/batch', [ 'items' => [ ['key' => 'settlement.auto_payout_on_tick', 'value' => false], ], ]) ->assertForbidden(); expect(LotterySetting::query()->where('setting_key', 'settlement.auto_payout_on_tick')->value('value_json'))->toBeTrue(); }); test('non payout manager cannot update single settlement setting', function (): void { LotterySettings::put('settlement.auto_approve_on_tick', true, 'settlement'); $token = settingsReadOnlyToken(); $this->withHeader('Authorization', 'Bearer '.$token) ->putJson('/api/v1/admin/settings/settlement.auto_approve_on_tick', [ 'value' => false, ]) ->assertForbidden(); expect(LotterySetting::query()->where('setting_key', 'settlement.auto_approve_on_tick')->value('value_json'))->toBeTrue(); });