create(); $user = User::factory()->create(); $policies = Policy::factory()->count(3)->create(['tenant_id' => $tenant->id]); // Create versions for policies so they can be backed up $policies->each(function ($policy) use ($tenant) { PolicyVersion::create([ 'tenant_id' => $tenant->id, 'policy_id' => $policy->id, 'policy_type' => $policy->policy_type, 'version_number' => 1, 'snapshot' => ['name' => $policy->display_name], 'captured_at' => now(), ]); }); $policyIds = $policies->pluck('id')->toArray(); $run = OperationRun::factory()->create([ 'tenant_id' => $tenant->getKey(), 'user_id' => $user->getKey(), 'initiator_name' => $user->name, 'type' => 'policy.export', 'status' => 'queued', 'outcome' => 'pending', 'context' => ['scope' => 'subset', 'policy_ids' => $policyIds], 'summary_counts' => [], 'failure_summary' => [], ]); $job = new BulkPolicyExportJob( tenantId: (int) $tenant->getKey(), userId: (int) $user->getKey(), policyIds: $policyIds, backupName: 'My Bulk Backup', operationRun: $run, ); $job->handle(app(OperationRunService::class)); $run->refresh(); expect($run->status)->toBe('completed') ->and($run->outcome)->toBe('succeeded') ->and((int) ($run->summary_counts['processed'] ?? 0))->toBe(3) ->and((int) ($run->summary_counts['succeeded'] ?? 0))->toBe(3); // Verify BackupSet created $backupSet = BackupSet::where('name', 'My Bulk Backup')->first(); expect($backupSet)->not->toBeNull() ->and($backupSet->tenant_id)->toBe($tenant->id); // Verify BackupItems created expect(BackupItem::where('backup_set_id', $backupSet->id)->count())->toBe(3); }); test('job handles policies without versions gracefully', function () { $tenant = Tenant::factory()->create(); $user = User::factory()->create(); $policyWithVersion = Policy::factory()->create(['tenant_id' => $tenant->id]); PolicyVersion::create([ 'tenant_id' => $tenant->id, 'policy_id' => $policyWithVersion->id, 'policy_type' => $policyWithVersion->policy_type, 'version_number' => 1, 'snapshot' => ['name' => 'ok'], 'captured_at' => now(), ]); $policyNoVersion = Policy::factory()->create(['tenant_id' => $tenant->id]); $policyIds = [$policyWithVersion->id, $policyNoVersion->id]; $run = OperationRun::factory()->create([ 'tenant_id' => $tenant->getKey(), 'user_id' => $user->getKey(), 'initiator_name' => $user->name, 'type' => 'policy.export', 'status' => 'queued', 'outcome' => 'pending', 'context' => ['scope' => 'subset', 'policy_ids' => $policyIds], 'summary_counts' => [], 'failure_summary' => [], ]); $job = new BulkPolicyExportJob( tenantId: (int) $tenant->getKey(), userId: (int) $user->getKey(), policyIds: $policyIds, backupName: 'Partial Backup', operationRun: $run, ); $job->handle(app(OperationRunService::class)); $run->refresh(); expect($run->status)->toBe('completed'); expect($run->outcome)->toBe('partially_succeeded'); expect((int) ($run->summary_counts['processed'] ?? 0))->toBe(2); expect((int) ($run->summary_counts['succeeded'] ?? 0))->toBe(1); expect((int) ($run->summary_counts['failed'] ?? 0))->toBe(1); $failure = collect($run->failure_summary ?? [])->first(fn ($entry) => is_array($entry) && ($entry['code'] ?? null) === 'policy.no_versions'); expect($failure)->not->toBeNull(); });