Automated PR created by Codex via Gitea API. Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #464
272 lines
12 KiB
PHP
272 lines
12 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use App\Filament\Resources\EvidenceSnapshotResource;
|
|
use App\Models\EnvironmentReview;
|
|
use App\Models\EvidenceSnapshot;
|
|
use App\Models\ManagedEnvironment;
|
|
use App\Models\ReviewPack;
|
|
use App\Models\User;
|
|
use App\Models\Workspace;
|
|
use App\Services\Evidence\EvidenceAnchorResolver;
|
|
use App\Services\Evidence\EvidenceAnchorResult;
|
|
use App\Support\Auth\Capabilities;
|
|
use App\Support\EnvironmentReviewStatus;
|
|
use App\Support\Evidence\EvidenceCompletenessState;
|
|
use App\Support\Evidence\EvidenceSnapshotStatus;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Illuminate\Support\Facades\Gate;
|
|
|
|
uses(RefreshDatabase::class);
|
|
|
|
it('does not promote partial active evidence as the current scope anchor', function (): void {
|
|
$tenant = ManagedEnvironment::factory()->create();
|
|
[$user, $tenant] = createUserWithTenant(tenant: $tenant, role: 'owner');
|
|
setAdminPanelContext($tenant);
|
|
|
|
spec393EvidenceSnapshot($tenant, [
|
|
'completeness_state' => EvidenceCompletenessState::Partial->value,
|
|
'summary' => ['missing_dimensions' => 1, 'stale_dimensions' => 0],
|
|
]);
|
|
|
|
$anchor = app(EvidenceAnchorResolver::class)->currentForScope($tenant->workspace, $tenant, $user);
|
|
|
|
expect($anchor->anchorType)->toBe(EvidenceAnchorResult::TYPE_NO_VALID_EVIDENCE)
|
|
->and($anchor->state)->toBe(EvidenceAnchorResult::STATE_NEEDS_ATTENTION)
|
|
->and($anchor->evidenceSnapshotId)->toBeNull()
|
|
->and($anchor->targetRoute)->toBeNull()
|
|
->and($anchor->canLink)->toBeFalse();
|
|
});
|
|
|
|
it('does not fall back to evidence from another environment in the workspace', function (): void {
|
|
$tenantA = ManagedEnvironment::factory()->create();
|
|
[$user, $tenantA] = createUserWithTenant(tenant: $tenantA, role: 'owner');
|
|
|
|
$tenantB = ManagedEnvironment::factory()->create(['workspace_id' => (int) $tenantA->workspace_id]);
|
|
createUserWithTenant(tenant: $tenantB, user: $user, role: 'owner');
|
|
setAdminPanelContext($tenantA);
|
|
|
|
spec393EvidenceSnapshot($tenantB);
|
|
|
|
$anchor = app(EvidenceAnchorResolver::class)->currentForScope($tenantA->workspace, $tenantA, $user);
|
|
|
|
expect($anchor->anchorType)->toBe(EvidenceAnchorResult::TYPE_NO_VALID_EVIDENCE)
|
|
->and($anchor->state)->toBe(EvidenceAnchorResult::STATE_NOT_CONFIGURED)
|
|
->and($anchor->targetRoute)->toBeNull();
|
|
});
|
|
|
|
it('excludes expired current evidence from current scope anchors', function (): void {
|
|
$tenant = ManagedEnvironment::factory()->create();
|
|
[$user, $tenant] = createUserWithTenant(tenant: $tenant, role: 'owner');
|
|
setAdminPanelContext($tenant);
|
|
|
|
spec393EvidenceSnapshot($tenant, [
|
|
'expires_at' => now()->subMinute(),
|
|
]);
|
|
|
|
$anchor = app(EvidenceAnchorResolver::class)->currentForScope($tenant->workspace, $tenant, $user);
|
|
|
|
expect($anchor->anchorType)->toBe(EvidenceAnchorResult::TYPE_NO_VALID_EVIDENCE)
|
|
->and($anchor->state)->toBe(EvidenceAnchorResult::STATE_NEEDS_ATTENTION)
|
|
->and($anchor->evidenceSnapshotId)->toBeNull()
|
|
->and($anchor->targetRoute)->toBeNull()
|
|
->and($anchor->canLink)->toBeFalse();
|
|
});
|
|
|
|
it('excludes superseded evidence from current scope anchors', function (): void {
|
|
$tenant = ManagedEnvironment::factory()->create();
|
|
[$user, $tenant] = createUserWithTenant(tenant: $tenant, role: 'owner');
|
|
setAdminPanelContext($tenant);
|
|
|
|
spec393EvidenceSnapshot($tenant, [
|
|
'status' => EvidenceSnapshotStatus::Superseded->value,
|
|
]);
|
|
|
|
$anchor = app(EvidenceAnchorResolver::class)->currentForScope($tenant->workspace, $tenant, $user);
|
|
|
|
expect($anchor->anchorType)->toBe(EvidenceAnchorResult::TYPE_NO_VALID_EVIDENCE)
|
|
->and($anchor->state)->toBe(EvidenceAnchorResult::STATE_NEEDS_ATTENTION)
|
|
->and($anchor->evidenceSnapshotId)->toBeNull()
|
|
->and($anchor->targetRoute)->toBeNull();
|
|
});
|
|
|
|
it('excludes wrong workspace evidence even when the environment id matches', function (): void {
|
|
$tenant = ManagedEnvironment::factory()->create();
|
|
[$user, $tenant] = createUserWithTenant(tenant: $tenant, role: 'owner');
|
|
$foreignWorkspace = Workspace::factory()->create();
|
|
setAdminPanelContext($tenant);
|
|
|
|
$snapshot = spec393EvidenceSnapshot($tenant);
|
|
EvidenceSnapshot::withoutEvents(function () use ($snapshot, $foreignWorkspace): void {
|
|
$snapshot->forceFill([
|
|
'workspace_id' => (int) $foreignWorkspace->getKey(),
|
|
])->save();
|
|
});
|
|
|
|
$anchor = app(EvidenceAnchorResolver::class)->currentForScope($tenant->workspace, $tenant, $user);
|
|
|
|
expect($anchor->anchorType)->toBe(EvidenceAnchorResult::TYPE_NO_VALID_EVIDENCE)
|
|
->and($anchor->state)->toBe(EvidenceAnchorResult::STATE_NOT_CONFIGURED)
|
|
->and($anchor->evidenceSnapshotId)->toBeNull()
|
|
->and($anchor->targetRoute)->toBeNull();
|
|
});
|
|
|
|
it('keeps released review evidence pinned instead of falling forward to current evidence', function (): void {
|
|
$tenant = ManagedEnvironment::factory()->create();
|
|
[$user, $tenant] = createUserWithTenant(tenant: $tenant, role: 'owner');
|
|
setAdminPanelContext($tenant);
|
|
|
|
$releasedSnapshot = spec393EvidenceSnapshot($tenant, [
|
|
'fingerprint' => 'released-snapshot',
|
|
'generated_at' => now()->subDays(3),
|
|
]);
|
|
|
|
$review = EnvironmentReview::factory()->create([
|
|
'managed_environment_id' => (int) $tenant->getKey(),
|
|
'workspace_id' => (int) $tenant->workspace_id,
|
|
'evidence_snapshot_id' => (int) $releasedSnapshot->getKey(),
|
|
'status' => EnvironmentReviewStatus::Published->value,
|
|
'published_at' => now()->subDays(2),
|
|
'published_by_user_id' => (int) $user->getKey(),
|
|
]);
|
|
|
|
$pack = ReviewPack::factory()->ready()->create([
|
|
'managed_environment_id' => (int) $tenant->getKey(),
|
|
'workspace_id' => (int) $tenant->workspace_id,
|
|
'environment_review_id' => (int) $review->getKey(),
|
|
'evidence_snapshot_id' => (int) $releasedSnapshot->getKey(),
|
|
'initiated_by_user_id' => (int) $user->getKey(),
|
|
]);
|
|
|
|
$releasedSnapshot->forceFill([
|
|
'status' => EvidenceSnapshotStatus::Superseded->value,
|
|
])->save();
|
|
|
|
spec393EvidenceSnapshot($tenant, [
|
|
'fingerprint' => 'new-current-snapshot',
|
|
'generated_at' => now(),
|
|
]);
|
|
|
|
$releaseAnchor = app(EvidenceAnchorResolver::class)->forReviewPackRelease($pack->fresh(), $user);
|
|
$customerAnchor = app(EvidenceAnchorResolver::class)->forCustomerWorkspace($review->fresh(), $user);
|
|
|
|
expect($releaseAnchor->anchorType)->toBe(EvidenceAnchorResult::TYPE_REVIEW_RELEASED_EVIDENCE)
|
|
->and($releaseAnchor->evidenceSnapshotId)->toBe((int) $releasedSnapshot->getKey())
|
|
->and($releaseAnchor->isReleaseBound)->toBeTrue()
|
|
->and($releaseAnchor->isCurrent)->toBeFalse()
|
|
->and($releaseAnchor->targetRoute)->toBe(EvidenceSnapshotResource::getUrl('view', ['record' => $releasedSnapshot], tenant: $tenant, panel: 'admin'))
|
|
->and($customerAnchor->anchorType)->toBe(EvidenceAnchorResult::TYPE_CUSTOMER_SAFE_EVIDENCE_SUMMARY)
|
|
->and($customerAnchor->evidenceSnapshotId)->toBeNull()
|
|
->and($customerAnchor->targetRoute)->toBeNull()
|
|
->and($customerAnchor->isCustomerSafe)->toBeTrue();
|
|
});
|
|
|
|
it('fails closed when customer workspace review pack evidence does not match the pack scope', function (): void {
|
|
$tenantA = ManagedEnvironment::factory()->create();
|
|
[$user, $tenantA] = createUserWithTenant(tenant: $tenantA, role: 'owner');
|
|
$tenantB = ManagedEnvironment::factory()->create(['workspace_id' => (int) $tenantA->workspace_id]);
|
|
createUserWithTenant(tenant: $tenantB, user: $user, role: 'owner');
|
|
setAdminPanelContext($tenantA);
|
|
|
|
$wrongScopeSnapshot = spec393EvidenceSnapshot($tenantB);
|
|
$review = EnvironmentReview::factory()->create([
|
|
'managed_environment_id' => (int) $tenantA->getKey(),
|
|
'workspace_id' => (int) $tenantA->workspace_id,
|
|
'evidence_snapshot_id' => (int) $wrongScopeSnapshot->getKey(),
|
|
'status' => EnvironmentReviewStatus::Published->value,
|
|
'published_at' => now(),
|
|
'published_by_user_id' => (int) $user->getKey(),
|
|
]);
|
|
$pack = ReviewPack::factory()->ready()->create([
|
|
'managed_environment_id' => (int) $tenantA->getKey(),
|
|
'workspace_id' => (int) $tenantA->workspace_id,
|
|
'environment_review_id' => (int) $review->getKey(),
|
|
'evidence_snapshot_id' => (int) $wrongScopeSnapshot->getKey(),
|
|
'initiated_by_user_id' => (int) $user->getKey(),
|
|
]);
|
|
|
|
$anchor = app(EvidenceAnchorResolver::class)->forCustomerWorkspace($pack->fresh(), $user);
|
|
|
|
expect($anchor->anchorType)->toBe(EvidenceAnchorResult::TYPE_NO_VALID_EVIDENCE)
|
|
->and($anchor->state)->toBe(EvidenceAnchorResult::STATE_BLOCKED)
|
|
->and($anchor->isCustomerSafe)->toBeFalse()
|
|
->and($anchor->evidenceSnapshotId)->toBeNull()
|
|
->and($anchor->targetRoute)->toBeNull()
|
|
->and($anchor->canLink)->toBeFalse()
|
|
->and($anchor->primaryReason)->toContain('scope does not match');
|
|
});
|
|
|
|
it('keeps draft review pack evidence internal and not customer safe', function (): void {
|
|
$tenant = ManagedEnvironment::factory()->create();
|
|
[$user, $tenant] = createUserWithTenant(tenant: $tenant, role: 'owner');
|
|
setAdminPanelContext($tenant);
|
|
|
|
$snapshot = spec393EvidenceSnapshot($tenant);
|
|
$pack = ReviewPack::factory()->create([
|
|
'managed_environment_id' => (int) $tenant->getKey(),
|
|
'workspace_id' => (int) $tenant->workspace_id,
|
|
'evidence_snapshot_id' => (int) $snapshot->getKey(),
|
|
'initiated_by_user_id' => (int) $user->getKey(),
|
|
]);
|
|
|
|
$anchor = app(EvidenceAnchorResolver::class)->forReviewPackDraft($pack->fresh(), $user);
|
|
|
|
expect($anchor->anchorType)->toBe(EvidenceAnchorResult::TYPE_REVIEW_DRAFT_EVIDENCE)
|
|
->and($anchor->isCustomerSafe)->toBeFalse()
|
|
->and($anchor->isTechnicalOnly)->toBeTrue()
|
|
->and($anchor->displayLabel)->toBe('Draft review evidence');
|
|
});
|
|
|
|
it('gates technical evidence detail links by evidence view authorization', function (): void {
|
|
$tenant = ManagedEnvironment::factory()->create();
|
|
[$user, $tenant] = createUserWithTenant(tenant: $tenant, role: 'owner');
|
|
$snapshot = spec393EvidenceSnapshot($tenant);
|
|
setAdminPanelContext($tenant);
|
|
|
|
$authorizedAnchor = app(EvidenceAnchorResolver::class)->technicalDetail($snapshot, $user);
|
|
|
|
expect($authorizedAnchor->anchorType)->toBe(EvidenceAnchorResult::TYPE_TECHNICAL_EVIDENCE_DETAIL)
|
|
->and($authorizedAnchor->displayLabel)->toBe('View internal evidence details')
|
|
->and($authorizedAnchor->canViewTechnicalDetail)->toBeTrue()
|
|
->and($authorizedAnchor->targetRoute)->toBe(EvidenceSnapshotResource::getUrl('view', ['record' => $snapshot], tenant: $tenant, panel: 'admin'));
|
|
|
|
Gate::define(Capabilities::EVIDENCE_VIEW, fn (User $actor, ManagedEnvironment $environment): bool => false);
|
|
|
|
$deniedAnchor = app(EvidenceAnchorResolver::class)->technicalDetail($snapshot, $user);
|
|
|
|
expect($deniedAnchor->canViewTechnicalDetail)->toBeFalse()
|
|
->and($deniedAnchor->targetRoute)->toBeNull()
|
|
->and($deniedAnchor->canLink)->toBeFalse();
|
|
});
|
|
|
|
it('keeps current evidence selection order explicit and deterministic', function (): void {
|
|
$tenant = ManagedEnvironment::factory()->create();
|
|
$resolver = app(EvidenceAnchorResolver::class);
|
|
|
|
$orders = $resolver->currentSnapshotQuery($tenant->workspace, $tenant)->getQuery()->orders;
|
|
|
|
expect($orders[0]['type'] ?? null)->toBe('Raw')
|
|
->and($orders[0]['sql'] ?? null)->toBe('generated_at IS NULL')
|
|
->and($orders[1]['column'] ?? null)->toBe('generated_at')
|
|
->and($orders[1]['direction'] ?? null)->toBe('desc')
|
|
->and($orders[2]['column'] ?? null)->toBe('id')
|
|
->and($orders[2]['direction'] ?? null)->toBe('desc');
|
|
});
|
|
|
|
/**
|
|
* @param array<string, mixed> $attributes
|
|
*/
|
|
function spec393EvidenceSnapshot(ManagedEnvironment $tenant, array $attributes = []): EvidenceSnapshot
|
|
{
|
|
return EvidenceSnapshot::query()->create(array_replace([
|
|
'managed_environment_id' => (int) $tenant->getKey(),
|
|
'workspace_id' => (int) $tenant->workspace_id,
|
|
'status' => EvidenceSnapshotStatus::Active->value,
|
|
'completeness_state' => EvidenceCompletenessState::Complete->value,
|
|
'summary' => ['missing_dimensions' => 0, 'stale_dimensions' => 0],
|
|
'fingerprint' => 'spec393-'.str()->uuid()->toString(),
|
|
'generated_at' => now(),
|
|
], $attributes));
|
|
}
|