set('tenantpilot.hardening.intune_write_gate.enabled', true); config()->set('tenantpilot.hardening.intune_write_gate.freshness_threshold_hours', 24); }); test('execute restore run job marks run failed when rbac_status is not_configured', function () { $tenant = Tenant::factory()->create([ 'rbac_status' => 'not_configured', 'rbac_last_checked_at' => null, ]); $backupSet = BackupSet::create([ 'tenant_id' => $tenant->id, 'name' => 'Backup', 'status' => 'completed', 'item_count' => 0, ]); $restoreRun = RestoreRun::create([ 'tenant_id' => $tenant->id, 'backup_set_id' => $backupSet->id, 'requested_by' => 'actor@example.com', 'is_dry_run' => false, 'status' => RestoreRunStatus::Queued->value, 'requested_items' => null, 'preview' => [], 'results' => null, 'metadata' => [], ]); $this->mock(RestoreService::class, function (MockInterface $mock) { $mock->shouldNotReceive('executeForRun'); }); $operationRun = app(OperationRunService::class)->ensureRun( tenant: $tenant, type: 'restore.execute', inputs: [ 'restore_run_id' => $restoreRun->id, 'backup_set_id' => $backupSet->id, 'is_dry_run' => false, ], ); $job = new ExecuteRestoreRunJob($restoreRun->id, 'actor@example.com', 'Actor', $operationRun); $job->handle(app(RestoreService::class), app(AuditLogger::class)); $restoreRun->refresh(); $operationRun->refresh(); expect($restoreRun->status)->toBe(RestoreRunStatus::Failed->value) ->and($restoreRun->failure_reason)->toContain('not configured') ->and($operationRun->outcome)->toBe(OperationRunOutcome::Failed->value); $failures = is_array($operationRun->failure_summary) ? $operationRun->failure_summary : []; $reasonCodes = array_column($failures, 'reason_code'); expect($reasonCodes)->toContain('intune_rbac.not_configured'); }); test('execute restore run job marks run failed when rbac_status is stale', function () { $tenant = Tenant::factory()->create([ 'rbac_status' => 'ok', 'rbac_last_checked_at' => now()->subHours(48), ]); $backupSet = BackupSet::create([ 'tenant_id' => $tenant->id, 'name' => 'Backup', 'status' => 'completed', 'item_count' => 0, ]); $restoreRun = RestoreRun::create([ 'tenant_id' => $tenant->id, 'backup_set_id' => $backupSet->id, 'requested_by' => 'actor@example.com', 'is_dry_run' => false, 'status' => RestoreRunStatus::Queued->value, 'requested_items' => null, 'preview' => [], 'results' => null, 'metadata' => [], ]); $this->mock(RestoreService::class, function (MockInterface $mock) { $mock->shouldNotReceive('executeForRun'); }); $operationRun = app(OperationRunService::class)->ensureRun( tenant: $tenant, type: 'restore.execute', inputs: [ 'restore_run_id' => $restoreRun->id, 'backup_set_id' => $backupSet->id, 'is_dry_run' => false, ], ); $job = new ExecuteRestoreRunJob($restoreRun->id, 'actor@example.com', 'Actor', $operationRun); $job->handle(app(RestoreService::class), app(AuditLogger::class)); $operationRun->refresh(); $failures = is_array($operationRun->failure_summary) ? $operationRun->failure_summary : []; $reasonCodes = array_column($failures, 'reason_code'); expect($reasonCodes)->toContain('intune_rbac.stale'); });