php/tests/Feature/ScheduledActionModelTest.php
Snider a0a0727c88 fix(actions): harden scheduled actions — security allowlists, trait verification, scan safety
- Add ALLOWED_NAMESPACES prefix allowlist to ScheduleServiceProvider
- Add ALLOWED_FREQUENCIES method allowlist (prevents arbitrary method dispatch)
- Verify Action trait on scheduled classes before dispatch
- Move try/catch inside foreach for per-action isolation
- Add empty-scan guard to ScheduleSyncCommand (prevents disabling all rows)
- Consolidate ScheduledActionScanner to single tokenisation pass
- Cast numeric frequency args via ctype_digit() in ScheduledAction

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 13:56:14 +00:00

91 lines
2.8 KiB
PHP

<?php
declare(strict_types=1);
namespace Core\Tests\Feature;
use Core\Actions\ScheduledAction;
use Core\Tests\TestCase;
use Illuminate\Foundation\Testing\RefreshDatabase;
class ScheduledActionModelTest extends TestCase
{
use RefreshDatabase;
protected function defineDatabaseMigrations(): void
{
$this->loadMigrationsFrom(__DIR__.'/../../database/migrations');
}
public function test_model_can_be_created(): void
{
$action = ScheduledAction::create([
'action_class' => 'App\\Actions\\TestAction',
'frequency' => 'dailyAt:09:00',
'timezone' => 'Europe/London',
'without_overlapping' => true,
'run_in_background' => true,
'is_enabled' => true,
]);
$this->assertDatabaseHas('scheduled_actions', [
'action_class' => 'App\\Actions\\TestAction',
'frequency' => 'dailyAt:09:00',
]);
}
public function test_enabled_scope(): void
{
ScheduledAction::create([
'action_class' => 'App\\Actions\\Enabled',
'frequency' => 'hourly',
'is_enabled' => true,
]);
ScheduledAction::create([
'action_class' => 'App\\Actions\\Disabled',
'frequency' => 'hourly',
'is_enabled' => false,
]);
$enabled = ScheduledAction::enabled()->get();
$this->assertCount(1, $enabled);
$this->assertSame('App\\Actions\\Enabled', $enabled->first()->action_class);
}
public function test_frequency_method_parses_simple_frequency(): void
{
$action = new ScheduledAction(['frequency' => 'everyMinute']);
$this->assertSame('everyMinute', $action->frequencyMethod());
$this->assertSame([], $action->frequencyArgs());
}
public function test_frequency_method_parses_frequency_with_args(): void
{
$action = new ScheduledAction(['frequency' => 'dailyAt:09:00']);
$this->assertSame('dailyAt', $action->frequencyMethod());
$this->assertSame(['09:00'], $action->frequencyArgs());
}
public function test_frequency_method_parses_multiple_args(): void
{
$action = new ScheduledAction(['frequency' => 'weeklyOn:1,09:00']);
$this->assertSame('weeklyOn', $action->frequencyMethod());
$this->assertSame([1, '09:00'], $action->frequencyArgs());
}
public function test_mark_run_updates_last_run_at(): void
{
$action = ScheduledAction::create([
'action_class' => 'App\\Actions\\Runnable',
'frequency' => 'hourly',
'is_enabled' => true,
]);
$this->assertNull($action->last_run_at);
$action->markRun();
$action->refresh();
$this->assertNotNull($action->last_run_at);
}
}