11, 'granted_count' => 7, ], operationRunCount: 0, )); expect(app(TenantReviewReadinessGate::class)->canPublish($review))->toBeFalse() ->and($review->publishBlockers())->not->toBeEmpty(); $truth = app(ArtifactTruthPresenter::class)->forTenantReview($review); expect($truth->artifactExistence)->toBe('created') ->and($truth->publicationReadiness)->toBe('blocked') ->and($truth->primaryLabel)->toBe('Blocked') ->and($truth->nextStepText())->toBe('Resolve the review blockers before publication'); expect(fn () => app(TenantReviewLifecycleService::class)->publish($review, $user)) ->toThrow(\InvalidArgumentException::class); }); it('publishes ready tenant reviews and archives them without mutating the published evidence history', function (): void { [$user, $tenant] = createUserWithTenant(role: 'owner'); $review = composeTenantReviewForTest($tenant, $user); $published = app(TenantReviewLifecycleService::class)->publish($review, $user); $publishedAt = $published->published_at?->toIso8601String(); expect($published->status)->toBe(TenantReviewStatus::Published->value) ->and($published->published_by_user_id)->toBe((int) $user->getKey()) ->and($publishedAt)->not->toBeNull(); $publishedTruth = app(ArtifactTruthPresenter::class)->forTenantReview($published); $archived = app(TenantReviewLifecycleService::class)->archive($published, $user); $archivedTruth = app(ArtifactTruthPresenter::class)->forTenantReview($archived); expect($archived->status)->toBe(TenantReviewStatus::Archived->value) ->and($archived->archived_at)->not->toBeNull() ->and($archived->published_at?->toIso8601String())->toBe($publishedAt) ->and($publishedTruth->publicationReadiness)->toBe('publishable') ->and($publishedTruth->nextStepText())->toBe('No action needed') ->and($archivedTruth->artifactExistence)->toBe('historical_only') ->and($archivedTruth->publicationReadiness)->toBe('internal_only') ->and($archivedTruth->nextStepText())->toBe('No action needed'); });