toContain(AuditActionId::FindingTriaged->value) ->toContain(AuditActionId::FindingInProgress->value) ->toContain(AuditActionId::FindingAssigned->value) ->toContain(AuditActionId::FindingResolved->value) ->toContain(AuditActionId::FindingClosed->value) ->toContain(AuditActionId::FindingRiskAccepted->value) ->toContain(AuditActionId::FindingReopened->value); }); it('keeps only legacy compatibility lifecycle helpers on the model', function (): void { expect(method_exists(Finding::class, 'acknowledge'))->toBeTrue() ->and(method_exists(Finding::class, 'resolve'))->toBeTrue() ->and(method_exists(Finding::class, 'reopen'))->toBeTrue() ->and(method_exists(Finding::class, 'triage'))->toBeFalse() ->and(method_exists(Finding::class, 'startProgress'))->toBeFalse() ->and(method_exists(Finding::class, 'assign'))->toBeFalse() ->and(method_exists(Finding::class, 'close'))->toBeFalse() ->and(method_exists(Finding::class, 'riskAccept'))->toBeFalse(); }); it('rolls back finding workflow persistence when audit logging fails', function (): void { [$user, $tenant] = $this->actingAsFindingOperator('owner'); $finding = $this->makeFindingForWorkflow($tenant, Finding::STATUS_NEW); mock(AuditLogger::class, function (MockInterface $mock): void { $mock->shouldReceive('log') ->once() ->andThrow(new \RuntimeException('Audit write unavailable.')); }); expect(fn () => app(FindingWorkflowService::class)->triage($finding, $tenant, $user)) ->toThrow(\RuntimeException::class, 'Audit write unavailable.'); expect($finding->refresh()->status)->toBe(Finding::STATUS_NEW) ->and(AuditLog::query() ->where('tenant_id', (int) $tenant->getKey()) ->where('resource_type', 'finding') ->where('resource_id', (string) $finding->getKey()) ->count())->toBe(0); });