create([ 'tenant_id' => (int) $tenant->getKey(), 'workspace_id' => (int) $tenant->workspace_id, 'user_id' => (int) $user->getKey(), 'type' => 'backup_create', ]); $audit = app(AuditRecorder::class)->record( action: 'backup.created', context: [ 'item_count' => 3, 'client_secret' => 'super-secret', ], workspace: $tenant->workspace, tenant: $tenant, actor: AuditActorSnapshot::human($user), target: new AuditTargetSnapshot( type: 'backup_set', id: '42', label: 'Nightly backup', ), outcome: 'completed', summary: null, operationRunId: (int) $run->getKey(), )->refresh(); expect((int) $audit->workspace_id)->toBe((int) $tenant->workspace_id) ->and((int) $audit->tenant_id)->toBe((int) $tenant->getKey()) ->and($audit->actorSnapshot()->type)->toBe(AuditActorType::Human) ->and($audit->actorDisplayLabel())->toBe($user->name) ->and($audit->resource_type)->toBe('backup_set') ->and((string) $audit->resource_id)->toBe('42') ->and($audit->targetDisplayLabel())->toBe('Nightly backup') ->and($audit->summaryText())->toBe('Backup set created for Nightly backup') ->and($audit->normalizedOutcome())->toBe(AuditOutcome::Success) ->and((int) $audit->operation_run_id)->toBe((int) $run->getKey()) ->and(data_get($audit->metadata, 'client_secret'))->toBe('[REDACTED]') ->and(data_get($audit->metadata, 'item_count'))->toBe(3); }); it('infers actor kind, target identity, and normalized outcome when snapshots are omitted', function (): void { [$user, $tenant] = createUserWithTenant(role: 'owner'); $audit = app(AuditRecorder::class)->record( action: 'backup_schedule.run_failed', context: [ '_actor_type' => 'scheduled', 'backup_schedule_id' => 123, 'status' => 'failed', 'token' => 'top-secret', ], workspace: $tenant->workspace, tenant: $tenant, )->refresh(); expect($audit->actorSnapshot()->type)->toBe(AuditActorType::Scheduled) ->and($audit->resource_type)->toBe('backup_schedule') ->and((string) $audit->resource_id)->toBe('123') ->and($audit->targetDisplayLabel())->toBe('Backup schedule #123') ->and($audit->normalizedOutcome())->toBe(AuditOutcome::Failed) ->and(data_get($audit->metadata, 'token'))->toBe('[REDACTED]'); });