create([ 'tenant_id' => (int) $tenant->getKey(), 'report_type' => StoredReport::REPORT_TYPE_PERMISSION_POSTURE, 'payload' => ['required_count' => 1, 'granted_count' => 1], ]); 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; } function disableReviewPackGenerationForWorkspace(Tenant $tenant, User $user, string $reason): void { $writer = app(SettingsWriter::class); $writer->updateWorkspaceSetting( actor: $user, workspace: $tenant->workspace, domain: 'entitlements', key: 'review_pack_generation_override_value', value: false, ); $writer->updateWorkspaceSetting( actor: $user, workspace: $tenant->workspace, domain: 'entitlements', key: 'review_pack_generation_override_reason', value: $reason, ); } it('blocks new review pack generation before creating a review pack or operation run when the workspace is not entitled', function (): void { [$user, $tenant] = createUserWithTenant(role: 'owner'); seedEntitlementReviewPackSnapshot($tenant); disableReviewPackGenerationForWorkspace($tenant, $user, 'Workspace is temporarily limited to manual reporting only'); $initialRunCount = OperationRun::query() ->where('tenant_id', (int) $tenant->getKey()) ->where('type', OperationRunType::ReviewPackGenerate->value) ->count(); expect(fn (): ReviewPack => app(ReviewPackService::class)->generate($tenant, $user)) ->toThrow(WorkspaceEntitlementBlockedException::class, 'Workspace is temporarily limited to manual reporting only'); expect(ReviewPack::query()->count())->toBe(0) ->and(OperationRun::query() ->where('tenant_id', (int) $tenant->getKey()) ->where('type', OperationRunType::ReviewPackGenerate->value) ->count())->toBe($initialRunCount); }); it('blocks executive pack export before creating a review pack or operation run when the workspace is not entitled', function (): void { [$user, $tenant] = createUserWithTenant(role: 'owner'); $snapshot = seedEntitlementReviewPackSnapshot($tenant); $review = composeTenantReviewForTest($tenant, $user, $snapshot); disableReviewPackGenerationForWorkspace($tenant, $user, 'Workspace is temporarily limited to manual reporting only'); $initialRunCount = OperationRun::query() ->where('tenant_id', (int) $tenant->getKey()) ->where('type', OperationRunType::ReviewPackGenerate->value) ->count(); expect(fn (): ReviewPack => app(ReviewPackService::class)->generateFromReview($review, $user)) ->toThrow(WorkspaceEntitlementBlockedException::class, 'Workspace is temporarily limited to manual reporting only'); expect(ReviewPack::query()->count())->toBe(0) ->and(OperationRun::query() ->where('tenant_id', (int) $tenant->getKey()) ->where('type', OperationRunType::ReviewPackGenerate->value) ->count())->toBe($initialRunCount); }); it('shows the blocked reason on the review pack card and keeps existing pack downloads accessible', function (): void { $tenant = Tenant::factory()->create(); [$user, $tenant] = createUserWithTenant(tenant: $tenant, role: 'owner'); disableReviewPackGenerationForWorkspace($tenant, $user, 'Workspace is temporarily limited to manual reporting only'); $initialRunCount = OperationRun::query() ->where('tenant_id', (int) $tenant->getKey()) ->where('type', OperationRunType::ReviewPackGenerate->value) ->count(); session()->put(WorkspaceContext::SESSION_KEY, (int) $tenant->workspace_id); setTenantPanelContext($tenant); Livewire::actingAs($user) ->test(TenantReviewPackCard::class, ['record' => $tenant]) ->assertSee('Workspace is temporarily limited to manual reporting only') ->assertSee('Generate pack') ->call('generatePack', true, true) ->assertHasNoErrors(); expect(ReviewPack::query()->count())->toBe(0) ->and(OperationRun::query() ->where('tenant_id', (int) $tenant->getKey()) ->where('type', OperationRunType::ReviewPackGenerate->value) ->count())->toBe($initialRunCount); $filePath = 'review-packs/entitlement-download-test.zip'; Storage::disk('exports')->put($filePath, 'PK-test'); $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', ]); $this->actingAs($user) ->get(ReviewPackResource::getUrl('view', ['record' => $pack], tenant: $tenant, panel: 'tenant')) ->assertOk() ->assertSee('Download'); });