get(route('settings.index'))->assertRedirect(route('login')); $user = User::factory()->create(); $this->actingAs($user)->get(route('settings.index'))->assertForbidden(); $this->actingAs($user)->put(route('settings.schedules.update'), [ 'schedules' => ['etsy_sync' => 'hourly'], ])->assertForbidden(); } public function test_admin_can_view_scheduler_section(): void { $response = $this ->actingAs(User::factory()->create(['role' => 'admin'])) ->get(route('settings.index')); $response->assertOk(); $response->assertSee('Scheduler'); $response->assertSee('etsy:sync'); $response->assertSee('etsy:refresh-token'); $response->assertSee('integrations:refresh'); $response->assertSee('Run now'); } public function test_admin_can_change_job_frequencies(): void { $payload = collect(DashboardSchedule::jobs()) ->mapWithKeys(fn (array $job, string $key): array => [$key => $job['default_frequency']]) ->all(); $payload['etsy_sync'] = 'hourly'; $payload['etsy_token_refresh'] = 'disabled'; $response = $this ->actingAs(User::factory()->create(['role' => 'admin'])) ->put(route('settings.schedules.update'), ['schedules' => $payload]); $response->assertRedirect(route('settings.index')); $this->assertSame('hourly', DashboardSchedule::frequencyFor('etsy_sync')); $this->assertSame('disabled', DashboardSchedule::frequencyFor('etsy_token_refresh')); $this->assertFileExists(DashboardSchedule::runtimeSettingsPath()); } public function test_partial_settings_updates_preserve_other_jobs(): void { DashboardSchedule::persistRuntimeFrequencies(['alerts_scan' => 'hourly']); DashboardSchedule::persistRuntimeFrequencies(['etsy_sync' => 'disabled']); $this->assertSame('hourly', DashboardSchedule::frequencyFor('alerts_scan')); $this->assertSame('disabled', DashboardSchedule::frequencyFor('etsy_sync')); } public function test_admin_cannot_save_unknown_jobs(): void { $this->actingAs(User::factory()->create(['role' => 'admin'])) ->put(route('settings.schedules.update'), ['schedules' => ['unknown' => 'hourly']]) ->assertSessionHasErrors('schedules'); $this->assertFileDoesNotExist(DashboardSchedule::runtimeSettingsPath()); } public function test_non_admin_cannot_run_jobs(): void { $this->actingAs(User::factory()->create()) ->post(route('settings.schedules.run', 'etsy_sync'))->assertForbidden(); } public function test_admin_can_manually_run_a_job(): void { $response = $this ->actingAs(User::factory()->create(['role' => 'admin'])) ->post(route('settings.schedules.run', 'alerts_scan')); $response->assertRedirect(route('settings.index')); $response->assertSessionHas('status'); } }