TenantAtlas/apps/platform/tests/Unit/Support/ReviewPublicationResolution/ResolutionProofEvaluationTest.php
Ahmed Darrazi 314a157233
Some checks failed
PR Fast Feedback / fast-feedback (pull_request) Failing after 1m18s
feat: add review publication proof currentness contract
2026-06-19 20:59:05 +02:00

148 lines
5.6 KiB
PHP

<?php
declare(strict_types=1);
use App\Models\EnvironmentReview;
use App\Support\ReviewPublicationResolution\ResolutionProofCurrentness;
use App\Support\ReviewPublicationResolution\ResolutionProofEvaluation;
use App\Support\ReviewPublicationResolution\ResolutionProofReference;
use App\Support\ReviewPublicationResolution\ResolutionProofStatus;
use App\Support\ReviewPublicationResolution\ResolutionProofUsability;
use App\Support\ReviewPublicationResolution\ResolutionProofVisibility;
use App\Support\ReviewPublicationResolution\ReviewPublicationResolutionStepKey;
it('fails missing proof closed with safe summary only', function (): void {
$review = new EnvironmentReview(['id' => 123]);
$payload = ResolutionProofEvaluation::missing(
ReviewPublicationResolutionStepKey::CompleteRequiredReports,
$review,
)->toStepPayload();
expect($payload['proof_type'])->toBeNull()
->and($payload['proof_id'])->toBeNull()
->and($payload['proof_status'])->toBe(ResolutionProofStatus::Missing->value)
->and($payload['proof_currentness'])->toBe(ResolutionProofCurrentness::Unknown->value)
->and($payload['proof_usability'])->toBe(ResolutionProofUsability::NotUsable->value)
->and($payload['proof_visibility'])->toBe(ResolutionProofVisibility::OperatorVisible->value);
});
it('sanitizes unsafe proof summaries before persistence or audit', function (): void {
$evaluation = new ResolutionProofEvaluation(
actionKey: ReviewPublicationResolutionStepKey::CompleteRequiredReports,
subjectType: EnvironmentReview::class,
subjectId: 321,
status: ResolutionProofStatus::Available,
currentness: ResolutionProofCurrentness::Current,
usability: ResolutionProofUsability::Usable,
visibility: ResolutionProofVisibility::OperatorVisible,
reasonCode: 'proof.required_reports_current',
reference: new ResolutionProofReference('stored_report', 55, 'ready', now()),
evaluatedAt: now(),
safeSummary: [
'label' => 'Current proof',
'message' => 'ClientException: Graph response body says Access token expired.',
'raw_graph_response' => ['access_token' => 'abc'],
'exception_message' => 'secret-token=abc123 rawGraphPayload {"access_token":"xyz"}',
'nested' => [
'safe_reason' => 'Report was evaluated.',
'error' => 'Authorization: Bearer abc.def.ghi',
'payload' => ['full_report' => true],
],
],
);
$payload = $evaluation->toStepPayload();
$encoded = json_encode($payload['proof_summary'], JSON_THROW_ON_ERROR);
expect($payload['proof_summary'])->toHaveKey('label')
->and($payload['proof_summary'])->toHaveKey('nested')
->and($encoded)->not->toContain('raw_graph_response', 'access_token', 'Access token', 'ClientException', 'Authorization', 'Bearer', 'secret-token', 'rawGraphPayload', 'full_report');
});
it('only lets current usable operator-visible proof complete a step', function (
ResolutionProofCurrentness $currentness,
ResolutionProofUsability $usability,
ResolutionProofVisibility $visibility,
bool $canComplete,
): void {
$evaluation = new ResolutionProofEvaluation(
actionKey: ReviewPublicationResolutionStepKey::GenerateReviewPack,
subjectType: EnvironmentReview::class,
subjectId: 123,
status: ResolutionProofStatus::Available,
currentness: $currentness,
usability: $usability,
visibility: $visibility,
reasonCode: 'proof.test',
);
expect($evaluation->canCompleteStep())->toBe($canComplete);
})->with([
'current usable visible' => [
ResolutionProofCurrentness::Current,
ResolutionProofUsability::Usable,
ResolutionProofVisibility::OperatorVisible,
true,
],
'unknown usable visible' => [
ResolutionProofCurrentness::Unknown,
ResolutionProofUsability::Usable,
ResolutionProofVisibility::OperatorVisible,
false,
],
'current inspection visible' => [
ResolutionProofCurrentness::Current,
ResolutionProofUsability::InspectionOnly,
ResolutionProofVisibility::OperatorVisible,
false,
],
'current usable hidden' => [
ResolutionProofCurrentness::Current,
ResolutionProofUsability::Usable,
ResolutionProofVisibility::Hidden,
false,
],
'current usable operator-limited' => [
ResolutionProofCurrentness::Current,
ResolutionProofUsability::Usable,
ResolutionProofVisibility::OperatorLimited,
false,
],
]);
it('requires available proof status before completing a step', function (
ResolutionProofStatus $status,
bool $canComplete,
): void {
$evaluation = new ResolutionProofEvaluation(
actionKey: ReviewPublicationResolutionStepKey::GenerateReviewPack,
subjectType: EnvironmentReview::class,
subjectId: 123,
status: $status,
currentness: ResolutionProofCurrentness::Current,
usability: ResolutionProofUsability::Usable,
visibility: ResolutionProofVisibility::OperatorVisible,
reasonCode: 'proof.test',
);
expect($evaluation->canCompleteStep())->toBe($canComplete);
})->with([
'available proof completes' => [
ResolutionProofStatus::Available,
true,
],
'running proof does not complete' => [
ResolutionProofStatus::Running,
false,
],
'succeeded operation remains inspection-only' => [
ResolutionProofStatus::Succeeded,
false,
],
'failed proof does not complete' => [
ResolutionProofStatus::Failed,
false,
],
]);