Applied customer/auditor safety layout changes to CustomerReviewWorkspace, EnvironmentReviewResource, EvidenceSnapshotResource, ReviewPackResource, and StoredReportResource as per Spec 372. Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #443
336 lines
12 KiB
PHP
336 lines
12 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use App\Filament\Pages\Reviews\CustomerReviewWorkspace;
|
|
use App\Filament\Resources\EnvironmentReviewResource;
|
|
use App\Filament\Resources\EvidenceSnapshotResource;
|
|
use App\Filament\Resources\ReviewPackResource;
|
|
use App\Filament\Resources\StoredReportResource;
|
|
use App\Models\EnvironmentReview;
|
|
use App\Models\EvidenceSnapshot;
|
|
use App\Models\EvidenceSnapshotItem;
|
|
use App\Models\ManagedEnvironment;
|
|
use App\Models\OperationRun;
|
|
use App\Models\ReviewPack;
|
|
use App\Models\StoredReport;
|
|
use App\Models\User;
|
|
use App\Support\EnvironmentReviewStatus;
|
|
use App\Support\Evidence\EvidenceCompletenessState;
|
|
use App\Support\OperationRunStatus;
|
|
use App\Support\OperationRunType;
|
|
use App\Support\Workspaces\WorkspaceContext;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Illuminate\Support\Facades\Storage;
|
|
use Livewire\Livewire;
|
|
|
|
uses(RefreshDatabase::class);
|
|
|
|
beforeEach(function (): void {
|
|
bindFailHardGraphClient();
|
|
Storage::fake('exports');
|
|
});
|
|
|
|
it('Spec372 keeps the customer review workspace decision-first without operation proof in the default evidence path', function (): void {
|
|
[$user, $tenant] = createUserWithTenant(role: 'owner', workspaceRole: 'manager');
|
|
$tenant->forceFill(['name' => 'Spec372 Customer Workspace'])->save();
|
|
|
|
spec372ReviewOutputFixture($tenant, $user);
|
|
|
|
$component = spec372WorkspaceComponent($user, $tenant)
|
|
->assertSee('What is the current review pack output state?')
|
|
->assertSee('Evidence path')
|
|
->assertSee('Review pack state')
|
|
->assertDontSee('Operation proof')
|
|
->assertDontSee('Spec372 Operator');
|
|
|
|
$html = $component->html();
|
|
|
|
expect(substr_count($html, 'data-testid="customer-review-primary-action"'))->toBe(1)
|
|
->and($html)->toContain('data-testid="customer-review-diagnostics"')
|
|
->and($html)->not->toContain('data-testid="customer-review-diagnostics" open');
|
|
});
|
|
|
|
it('Spec372 renders environment review output guidance before technical metadata', function (): void {
|
|
[$user, $tenant] = createUserWithTenant(role: 'owner');
|
|
['review' => $review] = spec372ReviewOutputFixture($tenant, $user);
|
|
|
|
setAdminEnvironmentContext($tenant);
|
|
|
|
$response = $this->actingAs($user)
|
|
->get(EnvironmentReviewResource::environmentScopedUrl('view', ['record' => $review], $tenant))
|
|
->assertOk();
|
|
|
|
$content = $response->getContent();
|
|
|
|
spec372AssertOrdered($content, [
|
|
'Outcome summary',
|
|
'Output guidance',
|
|
'Executive posture',
|
|
'Evidence basis',
|
|
'Technical details',
|
|
'Sections',
|
|
]);
|
|
|
|
expect(spec372ContentBetween($content, 'Evidence basis', 'Technical details'))
|
|
->not->toContain('spec372-review-fingerprint')
|
|
->not->toContain('Review status');
|
|
});
|
|
|
|
it('Spec372 renders review pack readiness and limitations before storage and operation metadata', function (): void {
|
|
[$user, $tenant] = createUserWithTenant(role: 'owner');
|
|
['pack' => $pack] = spec372ReviewOutputFixture($tenant, $user);
|
|
|
|
setAdminEnvironmentContext($tenant);
|
|
|
|
$response = $this->actingAs($user)
|
|
->get(ReviewPackResource::getUrl('view', ['record' => $pack], tenant: $tenant, panel: 'admin'))
|
|
->assertOk();
|
|
|
|
$content = $response->getContent();
|
|
|
|
spec372AssertOrdered($content, [
|
|
'Outcome summary',
|
|
'Output guidance',
|
|
'Pack readiness and contents',
|
|
'Technical pack details',
|
|
]);
|
|
|
|
expect(spec372ContentBetween($content, 'Pack readiness and contents', 'Technical pack details'))
|
|
->not->toContain('SHA-256')
|
|
->not->toContain('spec372-pack-fingerprint')
|
|
->not->toContain('Spec372 Operator');
|
|
});
|
|
|
|
it('Spec372 renders stored report scope and summary before source descriptors and raw payload', function (): void {
|
|
[$user, $tenant] = createUserWithTenant(role: 'owner');
|
|
$report = spec372StoredReport($tenant);
|
|
|
|
setAdminEnvironmentContext($tenant);
|
|
|
|
$response = $this->actingAs($user)
|
|
->get(StoredReportResource::getUrl('view', ['record' => $report], tenant: $tenant, panel: 'admin'))
|
|
->assertOk();
|
|
|
|
$content = $response->getContent();
|
|
|
|
spec372AssertOrdered($content, [
|
|
'Outcome summary',
|
|
'Report scope and readiness',
|
|
'Permission posture summary',
|
|
'Technical report details',
|
|
'Raw payload',
|
|
]);
|
|
|
|
expect(spec372ContentBetween($content, 'Report scope and readiness', 'Technical report details'))
|
|
->not->toContain('Source family')
|
|
->not->toContain('Integrity anchor')
|
|
->not->toContain('spec372-stored-report-fingerprint');
|
|
});
|
|
|
|
it('Spec372 renders evidence snapshot proof before diagnostics and omits operation links from related context', function (): void {
|
|
[$user, $tenant] = createUserWithTenant(role: 'owner');
|
|
['snapshot' => $snapshot] = spec372ReviewOutputFixture($tenant, $user);
|
|
spec372EvidenceSnapshotItem($snapshot);
|
|
|
|
setAdminEnvironmentContext($tenant);
|
|
|
|
$response = $this->actingAs($user)
|
|
->get(EvidenceSnapshotResource::getUrl('view', ['record' => $snapshot], tenant: $tenant, panel: 'admin'))
|
|
->assertOk();
|
|
|
|
$content = $response->getContent();
|
|
|
|
spec372AssertOrdered($content, [
|
|
'Outcome summary',
|
|
'Evidence basis and readiness',
|
|
'Evidence coverage summary',
|
|
'Related review and report context',
|
|
'Technical evidence details',
|
|
'Evidence dimensions',
|
|
]);
|
|
|
|
expect(collect(EvidenceSnapshotResource::relatedContextEntries($snapshot))->pluck('key')->all())
|
|
->not->toContain('operation_run')
|
|
->and(spec372ContentBetween($content, 'Related review and report context', 'Technical evidence details'))
|
|
->not->toContain('Operation')
|
|
->not->toContain('spec372-evidence-fingerprint');
|
|
});
|
|
|
|
it('Spec372 preserves scoped record access as not found for the wrong environment', function (): void {
|
|
[$user, $tenant] = createUserWithTenant(role: 'owner');
|
|
$foreignTenant = ManagedEnvironment::factory()->active()->create(['name' => 'Spec372 Foreign Environment']);
|
|
['review' => $foreignReview] = spec372ReviewOutputFixture($foreignTenant, $user);
|
|
|
|
setAdminEnvironmentContext($tenant);
|
|
|
|
$this->actingAs($user)
|
|
->get(EnvironmentReviewResource::environmentScopedUrl('view', ['record' => $foreignReview], $tenant))
|
|
->assertNotFound();
|
|
});
|
|
|
|
/**
|
|
* @return array{review: EnvironmentReview, snapshot: EvidenceSnapshot, pack: ReviewPack, run: OperationRun}
|
|
*/
|
|
function spec372ReviewOutputFixture(ManagedEnvironment $tenant, User $user): array
|
|
{
|
|
$snapshot = seedEnvironmentReviewEvidence($tenant, findingCount: 1, driftCount: 0);
|
|
$run = OperationRun::factory()
|
|
->forTenant($tenant)
|
|
->withUser($user)
|
|
->create([
|
|
'type' => OperationRunType::ReviewPackGenerate->value,
|
|
'status' => OperationRunStatus::Completed->value,
|
|
'initiator_name' => 'Spec372 Operator',
|
|
]);
|
|
|
|
$review = composeEnvironmentReviewForTest($tenant, $user, $snapshot);
|
|
$review->forceFill([
|
|
'status' => EnvironmentReviewStatus::Published->value,
|
|
'generated_at' => now()->subHours(2),
|
|
'published_at' => now()->subHour(),
|
|
'published_by_user_id' => (int) $user->getKey(),
|
|
'operation_run_id' => (int) $run->getKey(),
|
|
'fingerprint' => 'spec372-review-fingerprint',
|
|
])->save();
|
|
$review = markEnvironmentReviewCustomerSafeReady($review);
|
|
|
|
Storage::disk('exports')->put('review-packs/spec372-review-pack.zip', 'PK-spec372');
|
|
|
|
$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) $snapshot->getKey(),
|
|
'operation_run_id' => (int) $run->getKey(),
|
|
'initiated_by_user_id' => (int) $user->getKey(),
|
|
'file_path' => 'review-packs/spec372-review-pack.zip',
|
|
'file_disk' => 'exports',
|
|
'fingerprint' => 'spec372-pack-fingerprint',
|
|
'summary' => [
|
|
'finding_count' => 1,
|
|
'report_count' => 2,
|
|
'operation_count' => 1,
|
|
'evidence_resolution' => [
|
|
'outcome' => 'complete',
|
|
'snapshot_fingerprint' => 'spec372-evidence-fingerprint',
|
|
],
|
|
],
|
|
'options' => [
|
|
'include_pii' => false,
|
|
'include_operations' => true,
|
|
],
|
|
]);
|
|
|
|
$review->forceFill(['current_export_review_pack_id' => (int) $pack->getKey()])->save();
|
|
$snapshot->forceFill([
|
|
'operation_run_id' => (int) $run->getKey(),
|
|
'fingerprint' => 'spec372-evidence-fingerprint',
|
|
'summary' => array_replace(is_array($snapshot->summary) ? $snapshot->summary : [], [
|
|
'finding_count' => 1,
|
|
'report_count' => 2,
|
|
'operation_count' => 1,
|
|
'missing_dimensions' => 0,
|
|
'stale_dimensions' => 0,
|
|
]),
|
|
])->save();
|
|
|
|
return [
|
|
'review' => $review->refresh(),
|
|
'snapshot' => $snapshot->refresh(),
|
|
'pack' => $pack->refresh(),
|
|
'run' => $run,
|
|
];
|
|
}
|
|
|
|
function spec372StoredReport(ManagedEnvironment $tenant): StoredReport
|
|
{
|
|
return StoredReport::factory()
|
|
->permissionPosture([
|
|
'posture_score' => 78,
|
|
'required_count' => 4,
|
|
'granted_count' => 3,
|
|
'permissions' => [
|
|
['key' => 'DeviceManagementConfiguration.Read.All', 'status' => 'granted'],
|
|
['key' => 'DeviceManagementApps.ReadWrite.All', 'status' => 'missing'],
|
|
],
|
|
])
|
|
->create([
|
|
'managed_environment_id' => (int) $tenant->getKey(),
|
|
'workspace_id' => (int) $tenant->workspace_id,
|
|
'fingerprint' => 'spec372-stored-report-fingerprint',
|
|
]);
|
|
}
|
|
|
|
function spec372EvidenceSnapshotItem(EvidenceSnapshot $snapshot): EvidenceSnapshotItem
|
|
{
|
|
return EvidenceSnapshotItem::query()->updateOrCreate(
|
|
[
|
|
'evidence_snapshot_id' => (int) $snapshot->getKey(),
|
|
'dimension_key' => 'permission_posture',
|
|
],
|
|
[
|
|
'workspace_id' => (int) $snapshot->workspace_id,
|
|
'managed_environment_id' => (int) $snapshot->managed_environment_id,
|
|
'state' => EvidenceCompletenessState::Complete->value,
|
|
'required' => true,
|
|
'source_kind' => 'stored_report',
|
|
'source_record_type' => 'stored_report',
|
|
'source_record_id' => 'spec372-stored-report',
|
|
'source_fingerprint' => 'spec372-item-source-fingerprint',
|
|
'measured_at' => now(),
|
|
'freshness_at' => now(),
|
|
'summary_payload' => [
|
|
'required_count' => 4,
|
|
'granted_count' => 3,
|
|
'posture_score' => 78,
|
|
'payload' => [
|
|
'missing_permissions' => ['DeviceManagementApps.ReadWrite.All'],
|
|
],
|
|
],
|
|
'sort_order' => 10,
|
|
],
|
|
);
|
|
}
|
|
|
|
function spec372WorkspaceComponent(User $user, ManagedEnvironment $tenant): mixed
|
|
{
|
|
$workspaceId = (int) $tenant->workspace_id;
|
|
|
|
session()->put(WorkspaceContext::SESSION_KEY, $workspaceId);
|
|
setAdminPanelContext();
|
|
|
|
return Livewire::withQueryParams(['environment_id' => (int) $tenant->getKey()])
|
|
->actingAs($user)
|
|
->test(CustomerReviewWorkspace::class);
|
|
}
|
|
|
|
/**
|
|
* @param list<string> $needles
|
|
*/
|
|
function spec372AssertOrdered(string $content, array $needles): void
|
|
{
|
|
$lastPosition = -1;
|
|
|
|
foreach ($needles as $needle) {
|
|
$position = strpos($content, $needle, $lastPosition + 1);
|
|
|
|
expect($position)->not->toBeFalse();
|
|
expect((int) $position)->toBeGreaterThan($lastPosition);
|
|
|
|
$lastPosition = (int) $position;
|
|
}
|
|
}
|
|
|
|
function spec372ContentBetween(string $content, string $startMarker, string $endMarker): string
|
|
{
|
|
$start = strpos($content, $startMarker);
|
|
$end = strpos($content, $endMarker, $start === false ? 0 : (int) $start);
|
|
|
|
expect($start)->not->toBeFalse();
|
|
expect($end)->not->toBeFalse();
|
|
expect((int) $end)->toBeGreaterThan((int) $start);
|
|
|
|
return substr($content, (int) $start, (int) $end - (int) $start);
|
|
}
|