|
5 | 5 | use App\Models\WorkflowNamespace; |
6 | 6 | use App\Support\WorkflowStartService; |
7 | 7 | use Illuminate\Foundation\Testing\RefreshDatabase; |
| 8 | +use Illuminate\Support\Facades\DB; |
8 | 9 | use Mockery\MockInterface; |
9 | 10 | use Tests\TestCase; |
10 | | -use Workflow\V2\Contracts\WorkflowControlPlane; |
11 | 11 | use Workflow\V2\Models\WorkflowInstance; |
12 | 12 | use Workflow\V2\Models\WorkflowRun; |
13 | 13 | use Workflow\V2\Models\WorkflowSchedule; |
@@ -188,6 +188,41 @@ public function test_it_records_failures_and_returns_exit_code_1(): void |
188 | 188 | $this->assertStringContainsString('Workflow type not found', $lastAction['reason']); |
189 | 189 | } |
190 | 190 |
|
| 191 | + public function test_it_records_rollout_safety_start_rejections_as_skips(): void |
| 192 | + { |
| 193 | + $this->fakeStartService(result: [ |
| 194 | + 'started' => false, |
| 195 | + 'workflow_id' => 'wf-skip-compatibility-blocked', |
| 196 | + 'run_id' => null, |
| 197 | + 'workflow_type' => 'TestWorkflow', |
| 198 | + 'outcome' => 'rejected_compatibility_blocked', |
| 199 | + 'reason' => 'compatibility_blocked', |
| 200 | + 'rejection_reason' => 'compatibility_blocked', |
| 201 | + 'message' => 'Workflow instance [wf-skip-compatibility-blocked] cannot start.', |
| 202 | + ]); |
| 203 | + |
| 204 | + WorkflowSchedule::create([ |
| 205 | + 'schedule_id' => 'skip-compatibility-blocked', |
| 206 | + 'namespace' => 'default', |
| 207 | + 'spec' => ['cron_expressions' => ['* * * * *']], |
| 208 | + 'action' => ['workflow_type' => 'TestWorkflow'], |
| 209 | + 'next_fire_at' => now()->subMinute(), |
| 210 | + ]); |
| 211 | + |
| 212 | + $this->artisan('schedule:evaluate') |
| 213 | + ->assertExitCode(0) |
| 214 | + ->expectsOutputToContain('skipped'); |
| 215 | + |
| 216 | + $schedule = WorkflowSchedule::where('schedule_id', 'skip-compatibility-blocked')->firstOrFail(); |
| 217 | + $this->assertSame('compatibility_blocked', $schedule->last_skip_reason); |
| 218 | + $this->assertSame(1, (int) $schedule->skipped_trigger_count); |
| 219 | + $this->assertSame(0, (int) $schedule->fires_count); |
| 220 | + $this->assertSame(0, (int) $schedule->failures_count); |
| 221 | + $this->assertNull($schedule->last_fired_at); |
| 222 | + $this->assertNull($schedule->latest_workflow_instance_id); |
| 223 | + $this->assertSame([], $schedule->recent_actions ?? []); |
| 224 | + } |
| 225 | + |
191 | 226 | // ── next_fire_at advancement ──────────────────────────────────── |
192 | 227 |
|
193 | 228 | public function test_it_advances_next_fire_at_after_successful_fire(): void |
@@ -552,7 +587,7 @@ public function test_it_normalizes_legacy_timeout_fields_when_firing(): void |
552 | 587 | ]); |
553 | 588 |
|
554 | 589 | // Bypass the model accessor to write raw legacy JSON |
555 | | - \Illuminate\Support\Facades\DB::table('workflow_schedules') |
| 590 | + DB::table('workflow_schedules') |
556 | 591 | ->where('id', $schedule->id) |
557 | 592 | ->update(['action' => json_encode([ |
558 | 593 | 'workflow_type' => 'TestWorkflow', |
@@ -605,7 +640,7 @@ public function test_it_normalizes_legacy_timeout_fields_when_draining_buffer(): |
605 | 640 | ]); |
606 | 641 |
|
607 | 642 | // Write raw legacy JSON directly |
608 | | - \Illuminate\Support\Facades\DB::table('workflow_schedules') |
| 643 | + DB::table('workflow_schedules') |
609 | 644 | ->where('id', $schedule->id) |
610 | 645 | ->update(['action' => json_encode([ |
611 | 646 | 'workflow_type' => 'TestWorkflow', |
|
0 commit comments