'succeeded']); $this->actingAs($user); setAdminPanelContext($environment); $component = baselineCompareLandingLivewire($environment) ->assertSee('Which baseline drift requires action?') ->assertSee('Baseline not assigned') ->assertSee('This environment does not have an assigned baseline.') ->assertSee('Open baseline profiles') ->assertSee('Compare readiness flow') ->assertSee('Available inputs') ->assertSee('Diagnostics - Collapsed') ->assertDontSee('raw diff') ->assertDontSee('raw payload') ->assertDontSee('provider response') ->assertDontSee('stack trace'); $content = $component->html(); spec336AssertFlowStep($content, 'Baseline assigned', 'Missing', true); spec336AssertFlowStep($content, 'Baseline snapshot', 'Unavailable', false); spec336AssertFlowStep($content, 'Environment snapshot', 'Available', false); spec336AssertFlowStep($content, 'Compare run', 'Unavailable', false); spec336AssertFlowStep($content, 'Decision output', 'Unavailable', false); expect(substr_count($content, 'data-testid="baseline-compare-readiness-step"'))->toBe(5) ->and(substr_count($content, 'data-testid="baseline-compare-available-input"'))->toBe(3) ->and($content)->not->toContain('data-testid="baseline-compare-diagnostics" open'); }); it('renders baseline assigned with snapshot missing as a blocked compare flow', function (): void { [$user, $environment] = createUserWithTenant(role: 'owner', workspaceRole: 'manager'); $profile = BaselineProfile::factory()->active()->create([ 'workspace_id' => (int) $environment->workspace_id, ]); BaselineTenantAssignment::factory()->create([ 'workspace_id' => (int) $environment->workspace_id, 'managed_environment_id' => (int) $environment->getKey(), 'baseline_profile_id' => (int) $profile->getKey(), ]); $this->actingAs($user); setAdminPanelContext($environment); $component = baselineCompareLandingLivewire($environment) ->assertSee('Baseline snapshot required') ->assertSee('A baseline is assigned, but no usable baseline snapshot is available.') ->assertSee('Compare cannot run until baseline snapshot input exists.') ->assertSee('Open baseline profile') ->assertSee('No usable baseline snapshot input is linked.') ->assertDontSee('Drift findings available') ->assertDontSee('No drift detected') ->assertDontSee('raw diff'); $content = $component->html(); spec336AssertFlowStep($content, 'Baseline assigned', 'Complete', false); spec336AssertFlowStep($content, 'Baseline snapshot', 'Missing', true); spec336AssertFlowStep($content, 'Compare run', 'Unavailable', false); spec336AssertFlowStep($content, 'Decision output', 'Unavailable', false); spec336AssertInputState($content, 'Assigned baseline', 'Available'); spec336AssertInputState($content, 'Baseline snapshot', 'Missing'); spec336AssertInputState($content, 'Drift findings', 'Unavailable'); }); it('renders compare required with the capability-aware primary action', function (): void { [$owner, $environment] = createUserWithTenant(role: 'owner', workspaceRole: 'manager'); [$readonly] = createUserWithTenant(tenant: $environment, role: 'readonly', workspaceRole: 'readonly'); seedActiveBaselineForTenant($environment); createInventorySyncOperationRunWithCoverage($environment, ['deviceConfiguration' => 'succeeded']); $this->actingAs($owner); setAdminPanelContext($environment); $component = baselineCompareLandingLivewire($environment) ->assertSee('Compare run required') ->assertSee('Required inputs exist, but no compare run has been created for the current state.') ->assertSee('Compare now') ->assertActionEnabled('compareNow') ->assertDontSee('Ready to Compare'); $content = $component->html(); spec336AssertFlowStep($content, 'Baseline assigned', 'Complete', false); spec336AssertFlowStep($content, 'Baseline snapshot', 'Available', false); spec336AssertFlowStep($content, 'Compare run', 'Required', true); spec336AssertFlowStep($content, 'Decision output', 'Required', false); spec336AssertInputState($content, 'OperationRun proof', 'Unavailable'); baselineCompareLandingLivewire($environment, user: $readonly) ->assertSee('Compare unavailable') ->assertActionDisabled('compareNow'); }); it('renders in-progress and failed compare proof without claiming a decision output', function (): void { [$user, $environment] = createUserWithTenant(role: 'owner', workspaceRole: 'manager'); [$profile, $snapshot] = seedActiveBaselineForTenant($environment); $runningRun = seedBaselineCompareRun( $environment, $profile, $snapshot, [], OperationRunStatus::Running->value, OperationRunOutcome::Pending->value, now(), ); $this->actingAs($user); setAdminPanelContext($environment); $runningComponent = baselineCompareLandingLivewire($environment) ->assertSee('Compare in progress') ->assertSee('View operation progress') ->assertSet('operationRunId', (int) $runningRun->getKey()); $runningContent = $runningComponent->html(); spec336AssertFlowStep($runningContent, 'Compare run', 'In progress', true); spec336AssertFlowStep($runningContent, 'Decision output', 'Unavailable', false); spec336AssertInputState($runningContent, 'OperationRun proof', 'Available'); $failedRun = seedBaselineCompareRun( $environment, $profile, $snapshot, [], OperationRunStatus::Completed->value, OperationRunOutcome::Failed->value, now()->addMinute(), ); $failedComponent = baselineCompareLandingLivewire($environment) ->assertSee('Compare failed') ->assertSee('Review compare failure') ->assertSet('operationRunId', (int) $failedRun->getKey()); $failedContent = $failedComponent->html(); spec336AssertFlowStep($failedContent, 'Compare run', 'Failed', true); spec336AssertFlowStep($failedContent, 'Decision output', 'Unavailable', false); spec336AssertInputState($failedContent, 'OperationRun proof', 'Available'); }); it('renders drift findings and zero-drift outcomes without broad health or evidence claims', function (): void { [$user, $environment] = createUserWithTenant(role: 'owner', workspaceRole: 'manager'); [$profile, $snapshot] = seedActiveBaselineForTenant($environment); $run = seedBaselineCompareRun($environment, $profile, $snapshot, [ 'reason_code' => BaselineCompareReasonCode::OverdueFindingsRemain->value, 'coverage' => [ 'effective_types' => ['deviceConfiguration'], 'covered_types' => ['deviceConfiguration'], 'uncovered_types' => [], 'proof' => true, ], ]); 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); $driftComponent = baselineCompareLandingLivewire($environment) ->assertSee('Drift findings available') ->assertSee('Drift requires review before a decision is recorded.') ->assertSee('Review drift findings') ->assertSee('Evidence unavailable - Operation proof available') ->assertDontSee('environment is healthy') ->assertDontSee('customer-safe') ->assertDontSee('compliant'); $driftContent = $driftComponent->html(); spec336AssertFlowStep($driftContent, 'Compare run', 'Available', false); spec336AssertFlowStep($driftContent, 'Decision output', 'Available', false); spec336AssertInputState($driftContent, 'Drift findings', 'Available'); $quietEnvironment = createUserWithTenant(tenant: null, user: $user, role: 'owner', workspaceRole: 'manager')[1]; [$quietProfile, $quietSnapshot] = seedActiveBaselineForTenant($quietEnvironment); seedBaselineCompareRun($quietEnvironment, $quietProfile, $quietSnapshot, [ 'reason_code' => BaselineCompareReasonCode::NoDriftDetected->value, 'coverage' => [ 'effective_types' => ['deviceConfiguration'], 'covered_types' => ['deviceConfiguration'], 'uncovered_types' => [], 'proof' => true, ], ]); setAdminPanelContext($quietEnvironment); $quietComponent = baselineCompareLandingLivewire($quietEnvironment) ->assertSee('No drift detected') ->assertSee('No governance action is required from this compare result within available compare coverage.') ->assertSee('Review evidence') ->assertSee('Evidence unavailable - Operation proof available') ->assertDontSee('environment is healthy') ->assertDontSee('customer-safe') ->assertDontSee('compliant'); spec336AssertFlowStep($quietComponent->html(), 'Decision output', 'Available', false); }); it('keeps compare result, OperationRun proof, and evidence unavailable states separated', function (): void { [$user, $environment] = createUserWithTenant(role: 'owner', workspaceRole: 'manager'); [$profile, $snapshot] = seedActiveBaselineForTenant($environment); $run = seedBaselineCompareRun($environment, $profile, $snapshot, [ 'reason_code' => BaselineCompareReasonCode::EvidenceCaptureIncomplete->value, 'coverage' => [ 'effective_types' => ['deviceConfiguration'], 'covered_types' => ['deviceConfiguration'], 'uncovered_types' => [], 'proof' => true, ], 'evidence_gaps' => [ 'count' => 1, 'by_reason' => [ 'inventory_record_missing' => 1, ], ], ], 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_MEDIUM, 'status' => Finding::STATUS_NEW, 'source' => OperationRunType::BaselineCompare->value, 'baseline_operation_run_id' => (int) $run->getKey(), ]); $this->actingAs($user); setAdminPanelContext($environment); $component = baselineCompareLandingLivewire($environment) ->assertSee('Drift findings available') ->assertSee('OperationRun proof') ->assertSee('Evidence unavailable - Evidence gaps need review') ->assertSee('Compare result exists, but evidence output is not available. Evidence gaps need review.') ->assertSee('Diagnostics - Collapsed') ->assertDontSee('customer-safe') ->assertDontSee('raw diff'); $content = $component->html(); spec336AssertFlowStep($content, 'Compare run', 'Available', false); spec336AssertFlowStep($content, 'Decision output', 'Needs review', true); spec336AssertInputState($content, 'OperationRun proof', 'Available'); spec336AssertInputState($content, 'Evidence path', 'Needs review'); expect($content)->not->toContain('data-testid="baseline-compare-diagnostics" open'); }); it('preserves environment-owned routing and workspace isolation', function (): void { [$user, $environment] = createUserWithTenant(role: 'owner', workspaceRole: 'manager'); $hiddenEnvironment = createUserWithTenant(user: $user, role: 'owner', workspaceRole: 'manager')[1]; [$hiddenProfile, $hiddenSnapshot] = seedActiveBaselineForTenant($hiddenEnvironment); seedBaselineCompareRun($hiddenEnvironment, $hiddenProfile, $hiddenSnapshot, [ 'reason_code' => BaselineCompareReasonCode::OverdueFindingsRemain->value, 'coverage' => [ 'effective_types' => ['deviceConfiguration'], 'covered_types' => ['deviceConfiguration'], 'uncovered_types' => [], 'proof' => true, ], ]); $this->actingAs($user) ->withSession([WorkspaceContext::SESSION_KEY => (int) $environment->workspace_id]) ->get(ManagedEnvironmentLinks::baselineCompareUrl($environment)) ->assertOk() ->assertSeeText('Which baseline drift requires action?') ->assertDontSeeText((string) $hiddenProfile->name) ->assertDontSeeText('MANAGED_ENVIRONMENT'); $this->actingAs($user) ->withSession([WorkspaceContext::SESSION_KEY => (int) $environment->workspace_id]) ->get('/admin/baseline-compare-landing?tenant_id='.(int) $environment->getKey()) ->assertNotFound(); }); it('renders invalid assigned baseline scope as needs-review instead of running compare', function (): void { [$user, $environment] = createUserWithTenant(role: 'owner', workspaceRole: 'manager'); [$profile] = seedActiveBaselineForTenant($environment); DB::table('baseline_tenant_assignments') ->where('managed_environment_id', (int) $environment->getKey()) ->where('baseline_profile_id', (int) $profile->getKey()) ->update([ 'override_scope_jsonb' => json_encode(['version' => 2, 'entries' => []], JSON_THROW_ON_ERROR), 'updated_at' => now(), ]); $this->actingAs($user); setAdminPanelContext($environment); $component = baselineCompareLandingLivewire($environment) ->assertSee('Baseline scope requires review') ->assertSee('A baseline is assigned, but its scope cannot be used safely for compare.') ->assertSee('Open baseline profile') ->assertActionDisabled('compareNow'); $content = $component->html(); spec336AssertFlowStep($content, 'Baseline assigned', 'Needs review', true); spec336AssertFlowStep($content, 'Compare run', 'Unavailable', false); spec336AssertFlowStep($content, 'Decision output', 'Unavailable', false); }); function spec336AssertFlowStep(string $content, string $label, string $state, bool $currentBlocker): void { $blocker = $currentBlocker ? 'true' : 'false'; expect($content)->toMatch( '/data-step-label="'.preg_quote($label, '/').'"[\s\S]*?data-step-state="'.preg_quote($state, '/').'"[\s\S]*?data-step-current-blocker="'.$blocker.'"[\s\S]*?>\s*'.preg_quote($state, '/').'\s*toMatch( '/data-input-label="'.preg_quote($label, '/').'"[\s\S]*?>\s*'.preg_quote($state, '/').'\s*