create($tenant, $initialSnapshot, $user); $review = $reviewService->compose($review); EvidenceSnapshot::query() ->where('tenant_id', (int) $tenant->getKey()) ->where('status', 'active') ->update([ 'status' => 'expired', 'expires_at' => now(), ]); $refreshSnapshot = seedTenantReviewEvidence( tenant: $tenant, findingCount: 6, driftCount: 2, operationRunCount: 2, ); $review = $reviewService->refresh($review, $user, $refreshSnapshot); $review = $reviewService->compose($review->fresh()); $published = $lifecycle->publish($review, $user, 'Publishing the current review pack.'); EvidenceSnapshot::query() ->where('tenant_id', (int) $tenant->getKey()) ->where('status', 'active') ->update([ 'status' => 'expired', 'expires_at' => now(), ]); $pack = app(ReviewPackService::class)->generateFromReview($published, $user, [ 'include_pii' => true, 'include_operations' => true, ]); $job = new GenerateReviewPackJob( reviewPackId: (int) $pack->getKey(), operationRunId: (int) $pack->operation_run_id, ); app()->call([$job, 'handle']); $nextReview = $lifecycle->createNextReview($published->fresh(), $user, seedTenantReviewEvidence( tenant: $tenant, findingCount: 7, driftCount: 1, operationRunCount: 3, )); $lifecycle->archive($nextReview, $user, 'Replacing with a newer governance review.'); expect(AuditLog::query()->where('action', AuditActionId::TenantReviewCreated->value)->exists())->toBeTrue() ->and(AuditLog::query()->where('action', AuditActionId::TenantReviewRefreshed->value)->exists())->toBeTrue() ->and(AuditLog::query()->where('action', AuditActionId::TenantReviewPublished->value)->exists())->toBeTrue() ->and(AuditLog::query()->where('action', AuditActionId::TenantReviewExported->value)->exists())->toBeTrue() ->and(AuditLog::query()->where('action', AuditActionId::TenantReviewSuccessorCreated->value)->exists())->toBeTrue() ->and(AuditLog::query()->where('action', AuditActionId::TenantReviewArchived->value)->exists())->toBeTrue(); $exportAudit = AuditLog::query() ->where('action', AuditActionId::TenantReviewExported->value) ->latest('id') ->first(); $publishAudit = AuditLog::query() ->where('action', AuditActionId::TenantReviewPublished->value) ->latest('id') ->first(); $archiveAudit = AuditLog::query() ->where('action', AuditActionId::TenantReviewArchived->value) ->latest('id') ->first(); expect($exportAudit)->not->toBeNull() ->and($exportAudit?->resource_type)->toBe('tenant_review') ->and(data_get($exportAudit?->metadata, 'review_pack_id'))->toBe((int) $pack->getKey()) ->and(data_get($publishAudit?->metadata, 'reason'))->toBe('Publishing the current review pack.') ->and(data_get($archiveAudit?->metadata, 'reason'))->toBe('Replacing with a newer governance review.'); }); it('records customer workspace interpretation metadata when a tenant review is opened', function (): void { [$user, $tenant] = createUserWithTenant(role: 'readonly'); $snapshot = seedTenantReviewEvidence($tenant); $review = composeTenantReviewForTest($tenant, $user, $snapshot); $review->forceFill([ 'status' => 'published', 'published_at' => now(), 'published_by_user_id' => (int) $user->getKey(), ])->save(); setTenantPanelContext($tenant); $this->actingAs($user) ->get(TenantReviewResource::tenantScopedUrl('view', ['record' => $review], $tenant).'?'.http_build_query([ CustomerReviewWorkspace::DETAIL_CONTEXT_QUERY_KEY => 1, 'source_surface' => CustomerReviewWorkspace::SOURCE_SURFACE, 'tenant_filter_id' => (string) $tenant->getKey(), 'interpretation_version' => $review->controlInterpretationVersion(), ])) ->assertOk(); $audit = AuditLog::query() ->where('action', AuditActionId::TenantReviewOpened->value) ->latest('id') ->first(); expect($audit)->not->toBeNull() ->and($audit?->resource_type)->toBe('tenant_review') ->and(data_get($audit?->metadata, 'review_id'))->toBe((int) $review->getKey()) ->and(data_get($audit?->metadata, 'source_surface'))->toBe(CustomerReviewWorkspace::SOURCE_SURFACE) ->and(data_get($audit?->metadata, 'tenant_filter_id'))->toBe((string) $tenant->getKey()) ->and(data_get($audit?->metadata, 'interpretation_version'))->toBe($review->controlInterpretationVersion()); });