create([ 'tenant_id' => (int) $tenant->getKey(), 'report_type' => StoredReport::REPORT_TYPE_PERMISSION_POSTURE, 'payload' => [ 'required_count' => 1, 'granted_count' => 1, 'permissions' => [ ['key' => 'DeviceManagementConfiguration.ReadWrite.All', 'status' => 'granted'], ], ], ]); StoredReport::factory()->create([ 'tenant_id' => (int) $tenant->getKey(), 'report_type' => StoredReport::REPORT_TYPE_ENTRA_ADMIN_ROLES, 'payload' => [ 'roles' => [ ['displayName' => 'Global Administrator'], ], ], ]); Finding::factory()->create([ 'tenant_id' => (int) $tenant->getKey(), 'workspace_id' => (int) $tenant->workspace_id, ]); Finding::factory()->create([ 'tenant_id' => (int) $tenant->getKey(), 'workspace_id' => (int) $tenant->workspace_id, 'finding_type' => Finding::FINDING_TYPE_DRIFT, ]); OperationRun::factory()->forTenant($tenant)->create(); /** @var EvidenceSnapshotService $service */ $service = app(EvidenceSnapshotService::class); $payload = $service->buildSnapshotPayload($tenant); $snapshot = EvidenceSnapshot::query()->create([ 'tenant_id' => (int) $tenant->getKey(), 'workspace_id' => (int) $tenant->workspace_id, 'status' => EvidenceSnapshotStatus::Active->value, 'fingerprint' => $payload['fingerprint'], 'completeness_state' => $payload['completeness'], 'summary' => $payload['summary'], 'generated_at' => now(), ]); foreach ($payload['items'] as $item) { $snapshot->items()->create([ 'tenant_id' => (int) $tenant->getKey(), 'workspace_id' => (int) $tenant->workspace_id, 'dimension_key' => $item['dimension_key'], 'state' => $item['state'], 'required' => $item['required'], 'source_kind' => $item['source_kind'], 'source_record_type' => $item['source_record_type'], 'source_record_id' => $item['source_record_id'], 'source_fingerprint' => $item['source_fingerprint'], 'measured_at' => $item['measured_at'], 'freshness_at' => $item['freshness_at'], 'summary_payload' => $item['summary_payload'], 'sort_order' => $item['sort_order'], ]); } return $snapshot->load('items'); } // ─── List Page ─────────────────────────────────────────────── it('renders the list page for an authorized user', function (): void { $tenant = Tenant::factory()->create(); [$user, $tenant] = createUserWithTenant(tenant: $tenant, role: 'owner'); $this->actingAs($user) ->get(ReviewPackResource::getUrl('index', tenant: $tenant)) ->assertOk(); }); it('shows review packs belonging to the active tenant', function (): void { $tenant = Tenant::factory()->create(); [$user, $tenant] = createUserWithTenant(tenant: $tenant, role: 'owner'); $otherTenant = Tenant::factory()->create(); $pack = ReviewPack::factory()->ready()->create([ 'tenant_id' => (int) $tenant->getKey(), 'workspace_id' => (int) $tenant->workspace_id, 'initiated_by_user_id' => (int) $user->getKey(), ]); ReviewPack::factory()->ready()->create([ 'tenant_id' => (int) $otherTenant->getKey(), ]); $tenant->makeCurrent(); Filament::setTenant($tenant, true); Livewire::actingAs($user) ->test(ListReviewPacks::class) ->assertCanSeeTableRecords([$pack]); }); it('displays the empty state when no packs exist', function (): void { $tenant = Tenant::factory()->create(); [$user, $tenant] = createUserWithTenant(tenant: $tenant, role: 'owner'); $tenant->makeCurrent(); Filament::setTenant($tenant, true); Livewire::actingAs($user) ->test(ListReviewPacks::class) ->assertSee('No review packs yet'); }); // ─── List Page Header Action ───────────────────────────────── it('shows the generate_pack header action for a MANAGE user', function (): void { $tenant = Tenant::factory()->create(); [$user, $tenant] = createUserWithTenant(tenant: $tenant, role: 'owner'); $tenant->makeCurrent(); Filament::setTenant($tenant, true); Livewire::actingAs($user) ->test(ListReviewPacks::class) ->assertActionVisible('generate_pack'); }); it('disables the generate_pack action for a readonly user', function (): void { $tenant = Tenant::factory()->create(); [$user, $tenant] = createUserWithTenant(tenant: $tenant, role: 'readonly'); $tenant->makeCurrent(); Filament::setTenant($tenant, true); Livewire::actingAs($user) ->test(ListReviewPacks::class) ->assertActionVisible('generate_pack') ->assertActionDisabled('generate_pack') ->assertActionExists('generate_pack', fn ($action): bool => $action->getTooltip() === UiTooltips::insufficientPermission()); }); it('reuses an existing ready pack instead of starting a new run', function (): void { Queue::fake(); $tenant = Tenant::factory()->create(); [$user, $tenant] = createUserWithTenant(tenant: $tenant, role: 'owner'); $snapshot = seedReviewPackEvidence($tenant); $fingerprint = app(ReviewPackService::class)->computeFingerprint($tenant, [ 'include_pii' => true, 'include_operations' => true, ]); ReviewPack::factory()->ready()->create([ 'tenant_id' => (int) $tenant->getKey(), 'workspace_id' => (int) $tenant->workspace_id, 'initiated_by_user_id' => (int) $user->getKey(), 'evidence_snapshot_id' => (int) $snapshot->getKey(), 'fingerprint' => $fingerprint, 'expires_at' => now()->addDay(), ]); $tenant->makeCurrent(); Filament::setTenant($tenant, true); $operationRunsBefore = OperationRun::query()->count(); $reviewPacksBefore = ReviewPack::query()->count(); Livewire::actingAs($user) ->test(ListReviewPacks::class) ->callAction('generate_pack', [ 'include_pii' => true, 'include_operations' => true, ]) ->assertNotified(); expect(OperationRun::query()->count())->toBe($operationRunsBefore); expect(ReviewPack::query()->count())->toBe($reviewPacksBefore); Queue::assertNothingPushed(); }); it('does not queue generation when no eligible evidence snapshot exists', function (): void { Queue::fake(); $tenant = Tenant::factory()->create(); [$user, $tenant] = createUserWithTenant(tenant: $tenant, role: 'owner'); $tenant->makeCurrent(); Filament::setTenant($tenant, true); Livewire::actingAs($user) ->test(ListReviewPacks::class) ->callAction('generate_pack', [ 'include_pii' => true, 'include_operations' => true, ]) ->assertNotified(); expect(ReviewPack::query()->count())->toBe(0); Queue::assertNothingPushed(); }); // ─── Table Row Actions ─────────────────────────────────────── it('shows the download action for a ready pack', function (): void { $tenant = Tenant::factory()->create(); [$user, $tenant] = createUserWithTenant(tenant: $tenant, role: 'owner'); $filePath = 'review-packs/test.zip'; Storage::disk('exports')->put($filePath, 'PK-fake'); $pack = ReviewPack::factory()->ready()->create([ 'tenant_id' => (int) $tenant->getKey(), 'workspace_id' => (int) $tenant->workspace_id, 'initiated_by_user_id' => (int) $user->getKey(), 'file_path' => $filePath, ]); $tenant->makeCurrent(); Filament::setTenant($tenant, true); Livewire::actingAs($user) ->test(ListReviewPacks::class) ->assertTableActionVisible('download', $pack); }); it('shows the expire action for a ready pack with confirmation', function (): void { $tenant = Tenant::factory()->create(); [$user, $tenant] = createUserWithTenant(tenant: $tenant, role: 'owner'); $filePath = 'review-packs/expire-test.zip'; Storage::disk('exports')->put($filePath, 'PK-fake'); $pack = ReviewPack::factory()->ready()->create([ 'tenant_id' => (int) $tenant->getKey(), 'workspace_id' => (int) $tenant->workspace_id, 'initiated_by_user_id' => (int) $user->getKey(), 'file_path' => $filePath, 'file_disk' => 'exports', ]); $tenant->makeCurrent(); Filament::setTenant($tenant, true); Livewire::actingAs($user) ->test(ListReviewPacks::class) ->assertTableActionVisible('expire', $pack) ->callTableAction('expire', $pack); $pack->refresh(); expect($pack->status)->toBe(ReviewPackStatus::Expired->value); Storage::disk('exports')->assertMissing($filePath); }); // ─── View Page ─────────────────────────────────────────────── it('renders the view page for a ready pack', function (): void { $tenant = Tenant::factory()->create(); [$user, $tenant] = createUserWithTenant(tenant: $tenant, role: 'owner'); $snapshot = seedReviewPackEvidence($tenant); $pack = ReviewPack::factory()->ready()->create([ 'tenant_id' => (int) $tenant->getKey(), 'workspace_id' => (int) $tenant->workspace_id, 'initiated_by_user_id' => (int) $user->getKey(), 'evidence_snapshot_id' => (int) $snapshot->getKey(), 'summary' => [ 'finding_count' => 5, 'report_count' => 2, 'operation_count' => 12, 'data_freshness' => [ 'permission_posture' => now()->toIso8601String(), 'entra_admin_roles' => now()->toIso8601String(), 'findings' => now()->toIso8601String(), 'hardening' => now()->toIso8601String(), ], 'evidence_resolution' => [ 'outcome' => 'resolved', 'snapshot_id' => (int) $snapshot->getKey(), 'snapshot_fingerprint' => (string) $snapshot->fingerprint, ], ], 'options' => ['include_pii' => true, 'include_operations' => true], ]); $this->actingAs($user) ->get(ReviewPackResource::getUrl('view', ['record' => $pack], tenant: $tenant)) ->assertOk() ->assertSee('#'.$snapshot->getKey()) ->assertSee('resolved'); }); it('shows download header action on view page for a ready pack', function (): void { $tenant = Tenant::factory()->create(); [$user, $tenant] = createUserWithTenant(tenant: $tenant, role: 'owner'); $pack = ReviewPack::factory()->ready()->create([ 'tenant_id' => (int) $tenant->getKey(), 'workspace_id' => (int) $tenant->workspace_id, 'initiated_by_user_id' => (int) $user->getKey(), ]); $tenant->makeCurrent(); Filament::setTenant($tenant, true); Livewire::actingAs($user) ->test(ViewReviewPack::class, ['record' => $pack->getKey()]) ->assertActionVisible('download'); }); it('shows regenerate header action on view page', function (): void { $tenant = Tenant::factory()->create(); [$user, $tenant] = createUserWithTenant(tenant: $tenant, role: 'owner'); $pack = ReviewPack::factory()->ready()->create([ 'tenant_id' => (int) $tenant->getKey(), 'workspace_id' => (int) $tenant->workspace_id, 'initiated_by_user_id' => (int) $user->getKey(), ]); $tenant->makeCurrent(); Filament::setTenant($tenant, true); Livewire::actingAs($user) ->test(ViewReviewPack::class, ['record' => $pack->getKey()]) ->assertActionVisible('regenerate'); }); // ─── Non-Member Access ─────────────────────────────────────── it('returns 404 for non-members on list page', function (): void { $tenant = Tenant::factory()->create(); $otherTenant = Tenant::factory()->create(); [$user] = createUserWithTenant($otherTenant, role: 'owner'); $this->actingAs($user) ->get(ReviewPackResource::getUrl('index', tenant: $tenant)) ->assertNotFound(); }); it('returns 404 for non-members on view page', function (): void { $tenant = Tenant::factory()->create(); $otherTenant = Tenant::factory()->create(); [$user] = createUserWithTenant($otherTenant, role: 'owner'); $pack = ReviewPack::factory()->ready()->create([ 'tenant_id' => (int) $tenant->getKey(), ]); $this->actingAs($user) ->get(ReviewPackResource::getUrl('view', ['record' => $pack], tenant: $tenant)) ->assertNotFound(); });