actingAs($user); Filament::setTenant($tenant, true); $run = OperationRun::factory()->create([ 'tenant_id' => $tenant->getKey(), 'user_id' => $user->getKey(), 'initiator_name' => $user->name, 'type' => 'inventory.sync', 'status' => 'queued', 'outcome' => 'pending', 'context' => ['scope' => 'all'], ]); /** @var OperationRunService $service */ $service = app(OperationRunService::class); $service->updateRun( $run, status: 'completed', outcome: 'succeeded', summaryCounts: [ 'total' => '10', 'processed' => 5.2, 'failed' => 'nope', 'secrets' => 123, ], failures: [], ); $notification = $user->notifications()->latest('id')->first(); expect($notification)->not->toBeNull(); $body = (string) ($notification->data['body'] ?? ''); expect($body)->toContain('Summary:'); expect($body)->toContain('total: 10'); expect($body)->toContain('processed: 5'); expect($body)->not->toContain('secrets'); expect($body)->not->toContain('failed:'); })->group('ops-ux'); it('sanitizes summary_counts values and drops non-whitelisted keys', function (): void { [$user, $tenant] = createUserWithTenant(role: 'owner'); $this->actingAs($user); $run = OperationRun::factory()->create([ 'tenant_id' => $tenant->getKey(), 'user_id' => $user->getKey(), 'initiator_name' => $user->name, 'type' => 'policy.delete', 'status' => 'queued', 'outcome' => 'pending', 'context' => ['operation' => ['type' => 'policy.delete']], 'summary_counts' => [], ]); /** @var OperationRunService $service */ $service = app(OperationRunService::class); $service->updateRun( $run, status: 'running', outcome: null, summaryCounts: [ 'processed' => '2', 'failed' => 1.2, 'secrets' => 999, '' => 5, 'total' => 'not-a-number', ], ); $run->refresh(); expect($run->summary_counts['processed'] ?? null)->toBe(2); expect($run->summary_counts['failed'] ?? null)->toBe(1); expect($run->summary_counts)->not->toHaveKey('secrets'); expect($run->summary_counts)->not->toHaveKey(''); expect($run->summary_counts)->not->toHaveKey('total'); })->group('ops-ux'); it('ignores non-whitelisted keys when incrementing summary_counts', function (): void { [$user, $tenant] = createUserWithTenant(role: 'owner'); $this->actingAs($user); $run = OperationRun::factory()->create([ 'tenant_id' => $tenant->getKey(), 'user_id' => $user->getKey(), 'initiator_name' => $user->name, 'type' => 'policy.delete', 'status' => 'queued', 'outcome' => 'pending', 'context' => ['operation' => ['type' => 'policy.delete']], 'summary_counts' => ['processed' => 1, 'secrets' => 5], ]); /** @var OperationRunService $service */ $service = app(OperationRunService::class); $service->incrementSummaryCounts($run, [ 'processed' => 1, 'secrets' => 10, ]); $run->refresh(); expect($run->summary_counts['processed'] ?? null)->toBe(2); expect($run->summary_counts)->not->toHaveKey('secrets'); })->group('ops-ux');