first(fn (string $candidate): bool => is_file($candidate)); expect($path)->not->toBeNull(); $contents = file_get_contents($path); expect($contents) ->toContain('Environment Dashboard') ->toContain('Baseline Compare') ->toContain('Provider connection/readiness') ->toContain('Required permissions') ->toContain('Backup sets') ->toContain('Restore runs') ->toContain('Baseline profile assignment') ->toContain('Baseline compare result') ->toContain('OperationRuns'); }); it('renders the Environment Dashboard as a decision-first readiness workbench', function (): void { [$user, $environment] = createUserWithTenant(role: 'owner', workspaceRole: 'manager'); foreach (range(1, 3) as $index) { OperationRun::factory()->create([ 'managed_environment_id' => (int) $environment->getKey(), 'workspace_id' => (int) $environment->workspace_id, 'type' => 'inventory_sync', 'status' => OperationRunStatus::Completed->value, 'outcome' => OperationRunOutcome::Failed->value, 'completed_at' => now()->subHours($index), ]); } $this->actingAs($user); setAdminPanelContext($environment); $component = Livewire::test(EnvironmentDashboardOverview::class) ->assertSee('Is this environment ready, blocked, stale, or requiring review?') ->assertSee('Status') ->assertSee('Reason') ->assertSee('Impact') ->assertSee('Readiness proof') ->assertSee('Recommended next action') ->assertSee('Readiness dimensions') ->assertSee('Recommended next actions') ->assertSee('Supporting signals') ->assertSee('Additional readiness signals used to explain the current recommendation.') ->assertSee('Signal') ->assertSee('State') ->assertSee('Action') ->assertSee('Unavailable') ->assertSee('Not ready') ->assertSee('Absent') ->assertSee('No active review') ->assertSee('No customer-safe output') ->assertSee('Baseline missing') ->assertSee('Operations follow-up') ->assertSee('3 require review') ->assertSee('Open operations hub') ->assertSee('Diagnostics - Collapsed') ->assertDontSee('Secondary context') ->assertDontSee('Show operation details') ->assertDontSee('View operation details') ->assertDontSee('Ab...') ->assertDontSee('Unava...') ->assertDontSee('Not re...') ->assertDontSee('No customer-saf...') ->assertDontSee('Baseline assig...') ->assertDontSee('Fully ready') ->assertDontSee('Protected') ->assertDontSee('Compliant') ->assertDontSee('raw payload') ->assertDontSee('raw diff') ->assertDontSee('provider secret') ->assertDontSee('stack trace') ->assertDontSee('debug metadata') ->assertDontSee('internal exception') ->assertDontSee('provider response'); $content = $component->html(); $readinessDecisionPosition = strpos($content, 'data-testid="tenant-dashboard-readiness-decision"'); $supportingSignalsPosition = strpos($content, 'data-testid="tenant-dashboard-supporting-signals"'); $operationFollowUpPosition = strpos($content, 'data-signal-key="operations_follow_up"'); $diagnosticsPosition = strpos($content, 'data-testid="tenant-dashboard-diagnostics"'); expect(substr_count($content, 'data-testid="tenant-dashboard-readiness-decision"'))->toBe(1) ->and(substr_count($content, 'data-testid="tenant-dashboard-readiness-proof-panel"'))->toBe(1) ->and(substr_count($content, 'data-testid="tenant-dashboard-readiness-card"'))->toBe(0) ->and(substr_count($content, 'data-testid="tenant-dashboard-supporting-signals"'))->toBe(1) ->and(substr_count($content, 'data-testid="tenant-dashboard-supporting-signal"'))->toBe(6) ->and(substr_count($content, 'data-testid="tenant-dashboard-operations-attention-summary"'))->toBe(0) ->and(substr_count($content, 'data-testid="tenant-dashboard-operations-attention-item"'))->toBe(0) ->and(substr_count($content, 'data-testid="tenant-dashboard-operation-details"'))->toBe(0) ->and(substr_count($content, 'data-testid="tenant-dashboard-diagnostics"'))->toBe(1) ->and(substr_count($content, 'data-testid="tenant-dashboard-status-badge"'))->toBeGreaterThan(0) ->and($content)->not->toContain('tenant-dashboard-secondary-context') ->and($content)->not->toContain('data-testid="tenant-dashboard-diagnostics" open') ->and($content)->not->toContain('truncate') ->and($content)->not->toContain('text-overflow') ->and($readinessDecisionPosition)->not->toBeFalse() ->and($supportingSignalsPosition)->not->toBeFalse() ->and($operationFollowUpPosition)->not->toBeFalse() ->and($diagnosticsPosition)->not->toBeFalse() ->and($supportingSignalsPosition)->toBeGreaterThan($readinessDecisionPosition) ->and($operationFollowUpPosition)->toBeGreaterThan($supportingSignalsPosition) ->and($diagnosticsPosition)->toBeGreaterThan($supportingSignalsPosition); }); it('keeps dynamic Tenant display names while avoiding static tenant copy on the dashboard', function (): void { [$user, $environment] = createUserWithTenant(role: 'owner', workspaceRole: 'manager'); $environment->forceFill(['name' => 'Spec330 Tenant Named Environment'])->save(); $this->actingAs($user); $this->get(ManagedEnvironmentLinks::viewUrl($environment)) ->assertOk() ->assertSeeText('Spec330 Tenant Named Environment') ->assertDontSeeText('MANAGED_ENVIRONMENT') ->assertDontSeeText('current tenant') ->assertDontSeeText('tenant filter') ->assertDontSeeText('all tenants') ->assertDontSeeText('choose tenant') ->assertDontSeeText('tenant scope'); }); it('renders Baseline Compare no-assignment as an actionable unavailable decision state', function (): void { [$user, $environment] = createUserWithTenant(role: 'owner', workspaceRole: 'manager'); createInventorySyncOperationRunWithCoverage($environment, ['deviceConfiguration' => 'succeeded']); $this->actingAs($user); setAdminPanelContext($environment); $component = baselineCompareLandingLivewire($environment) ->assertSee('Which baseline drift requires action?') ->assertSee('Baseline not assigned') ->assertSee('Baseline drift cannot be used for governance decisions until a baseline assignment exists.') ->assertSee('Evidence unavailable') ->assertSee('Open baseline profiles to assign a baseline to this environment.') ->assertSee('Evidence path') ->assertSee('Next action') ->assertSee('Compare readiness flow') ->assertSee('Baseline comparison needs an assigned baseline, linked snapshots, a compare run, and a decision output.') ->assertSee('Baseline assigned') ->assertSee('Missing') ->assertSee('No baseline is assigned.') ->assertSee('Baseline snapshot') ->assertSee('No snapshot linked.') ->assertSee('Environment snapshot') ->assertSee('Environment snapshot evidence is present.') ->assertSee('Compare run') ->assertSee('Blocked by missing inputs.') ->assertSee('Decision output') ->assertSee('No decision output.') ->assertSee('Available inputs') ->assertSee('Operation proof') ->assertSee('Unavailable because no baseline assigned.') ->assertSee('What this unlocks after assignment') ->assertSee('Actionable drift categories') ->assertSee('Evidence-backed compare') ->assertSee('Governance decision path') ->assertSee('Diagnostics - Collapsed') ->assertDontSee('No Baseline Assigned') ->assertDontSee('This environment does not have an assigned baseline yet.') ->assertDontSee('No coverage warning is currently reported for the latest compare.') ->assertDontSee('Readiness overview') ->assertDontSee('Recent baseline activity') ->assertDontSee('0% Ready') ->assertDontSee('Assigned baseline') ->assertDontSee('Drift impact') ->assertDontSee('Evidence gap details') ->assertDontSee('Search recorded gap subjects') ->assertDontSee('raw payload') ->assertDontSee('raw diff') ->assertDontSee('provider secret') ->assertDontSee('stack trace') ->assertDontSee('debug metadata') ->assertDontSee('internal exception') ->assertDontSee('provider response'); $content = $component->html(); $visibleLabelCount = static fn (string $label): int => preg_match_all('/>\s*'.preg_quote($label, '/').'\s*toBe(1) ->and(substr_count($content, 'data-testid="baseline-compare-decision-summary"'))->toBe(0) ->and(substr_count($content, 'data-testid="baseline-compare-proof-item"'))->toBe(0) ->and(substr_count($content, 'data-testid="baseline-compare-readiness-flow"'))->toBe(1) ->and(substr_count($content, 'data-testid="baseline-compare-readiness-step"'))->toBe(5) ->and(substr_count($content, 'data-testid="baseline-compare-readiness-connector"'))->toBe(4) ->and(substr_count($content, 'data-testid="baseline-compare-available-input"'))->toBe(3) ->and(substr_count($content, 'data-testid="baseline-compare-assignment-unlocks"'))->toBe(1) ->and($content)->toContain('aria-label="Compare readiness pipeline"') ->and($content)->toContain('data-connector-label="Baseline assigned to Baseline snapshot"') ->and($content)->toContain('data-connector-label="Baseline snapshot to Environment snapshot"') ->and($content)->toContain('data-connector-label="Environment snapshot to Compare run"') ->and($content)->toContain('data-connector-label="Compare run to Decision output"') ->and($content)->toMatch('/data-step-label="Baseline assigned"[\s\S]*?data-step-state="Missing"[\s\S]*?data-step-current-blocker="true"[\s\S]*?>\s*Missing\s*and($content)->toMatch('/data-step-label="Environment snapshot"[\s\S]*?>\s*Available\s*and($content)->toMatch('/data-step-label="Compare run"[\s\S]*?>\s*Unavailable\s*and($content)->toMatch('/data-input-label="Environment snapshot"[\s\S]*?>\s*Available\s*and($content)->toMatch('/data-input-label="Operation proof"[\s\S]*?>\s*Unavailable\s*and($visibleLabelCount('Assigned baseline'))->toBe(0) ->and($visibleLabelCount('Compare trust'))->toBe(0) ->and($visibleLabelCount('Drift impact'))->toBe(0) ->and($visibleLabelCount('Evidence path'))->toBe(1) ->and($content)->not->toContain('data-testid="baseline-compare-diagnostics" open') ->and($content)->not->toContain('evidence_gap_details') ->and($content)->not->toContain('raw diff'); }); it('renders Baseline Compare drift and evidence summary before support diagnostics', function (): void { [$user, $environment] = createUserWithTenant(role: 'owner', workspaceRole: 'manager'); [$profile, $snapshot] = seedActiveBaselineForTenant($environment); $run = seedBaselineCompareRun($environment, $profile, $snapshot, [ 'reason_code' => \App\Support\Baselines\BaselineCompareReasonCode::CoverageUnproven->value, 'coverage' => [ 'effective_types' => ['deviceConfiguration', 'deviceCompliancePolicy'], 'covered_types' => ['deviceConfiguration'], 'uncovered_types' => ['deviceCompliancePolicy'], 'proof' => true, ], 'evidence_gaps' => [ 'count' => 1, 'by_reason' => [ 'inventory_record_missing' => 1, ], ], 'diagnostics' => [ 'support_only' => 'raw payload should stay hidden', 'provider_response' => 'provider response should stay hidden', ], ], OperationRunStatus::Completed->value, OperationRunOutcome::PartiallySucceeded->value); Finding::factory()->create([ 'managed_environment_id' => (int) $environment->getKey(), 'workspace_id' => (int) $environment->workspace_id, 'finding_type' => Finding::FINDING_TYPE_DRIFT, 'scope_key' => 'baseline_profile:'.$profile->getKey(), 'severity' => Finding::SEVERITY_HIGH, 'status' => Finding::STATUS_NEW, 'source' => OperationRunType::BaselineCompare->value, 'baseline_operation_run_id' => (int) $run->getKey(), ]); $this->actingAs($user); setAdminPanelContext($environment); baselineCompareLandingLivewire($environment) ->assertSee('Which baseline drift requires action?') ->assertSee('Drift requires review') ->assertSee('Evidence path') ->assertSee('Evidence gaps need review') ->assertSee('Open operation proof') ->assertSee('Diagnostics - Collapsed') ->assertDontSee('raw payload should stay hidden') ->assertDontSee('provider response should stay hidden') ->assertDontSee('stack trace') ->assertDontSee('debug metadata'); }); it('keeps both surfaces environment-owned and rejects legacy compare entry points', function (): void { [$user, $environment] = createUserWithTenant(role: 'owner', workspaceRole: 'manager'); $this->actingAs($user) ->withSession([ WorkspaceContext::SESSION_KEY => (int) $environment->workspace_id, WorkspaceContext::LAST_ENVIRONMENT_IDS_SESSION_KEY => [ (string) $environment->workspace_id => (int) $environment->getKey(), ], ]) ->get(ManagedEnvironmentLinks::viewUrl($environment)) ->assertOk() ->assertSeeText('Is this environment ready, blocked, stale, or requiring review?') ->assertDontSeeText('MANAGED_ENVIRONMENT'); $this->actingAs($user) ->withSession([WorkspaceContext::SESSION_KEY => (int) $environment->workspace_id]) ->get(ManagedEnvironmentLinks::baselineCompareUrl($environment)) ->assertOk() ->assertSeeText('Which baseline drift requires action?') ->assertDontSeeText('MANAGED_ENVIRONMENT'); $this->actingAs($user) ->withSession([ WorkspaceContext::SESSION_KEY => (int) $environment->workspace_id, WorkspaceContext::LAST_ENVIRONMENT_IDS_SESSION_KEY => [ (string) $environment->workspace_id => (int) $environment->getKey(), ], ]) ->get('/admin/baseline-compare-landing?environment_id='.(int) $environment->getKey().'&tenant_scope=selected') ->assertNotFound(); expect(BaselineCompareLanding::getUrl([ 'tenant' => (string) $environment->external_id, 'tenant_id' => (int) $environment->getKey(), 'managed_environment_id' => (int) $environment->getKey(), 'environment' => 'legacy-alias', 'tenant_scope' => 'selected', 'tableFilters' => ['managed_environment_id' => ['value' => (int) $environment->getKey()]], ], panel: 'admin', tenant: $environment)) ->not->toContain('tenant_id=') ->not->toContain('managed_environment_id=') ->not->toContain('tenant_scope=') ->not->toContain('tableFilters'); });