TenantAtlas/apps/platform/tests/Feature/Monitoring/GovernanceOperationRunSummariesTest.php
ahmido 2752515da5
Some checks failed
Main Confidence / confidence (push) Failing after 54s
Spec 235: harden baseline truth and onboarding flows (#271)
## Summary
- harden baseline capture truth, compare readiness, and monitoring explanations around latest inventory eligibility, blocked prerequisites, and zero-subject outcomes
- improve onboarding verification and bootstrap recovery handling, including admin-consent callback invalidation and queued execution legitimacy/report behavior
- align workspace findings/workspace overview signals and refresh the related spec, roadmap, and spec-candidate artifacts

## Validation
- `cd apps/platform && ./vendor/bin/sail bin pint --dirty --format agent`
- `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/BaselineDriftEngine/BaselineCaptureAuditEventsTest.php tests/Feature/BaselineDriftEngine/BaselineSnapshotNoTenantIdentifiersTest.php tests/Feature/BaselineDriftEngine/CaptureBaselineContentTest.php tests/Feature/BaselineDriftEngine/CaptureBaselineFullContentOnDemandTest.php tests/Feature/BaselineDriftEngine/CaptureBaselineMetaFallbackTest.php tests/Feature/Baselines/BaselineCaptureTest.php tests/Feature/Baselines/BaselineCompareFindingsTest.php tests/Feature/Baselines/BaselineSnapshotBackfillTest.php tests/Feature/Filament/BaselineCaptureResultExplanationSurfaceTest.php tests/Feature/Filament/BaselineCompareLandingStartSurfaceTest.php tests/Feature/Filament/BaselineProfileCaptureStartSurfaceTest.php tests/Feature/Filament/OperationRunBaselineTruthSurfaceTest.php tests/Feature/Monitoring/AuditCoverageGovernanceTest.php tests/Feature/Monitoring/GovernanceOperationRunSummariesTest.php tests/Feature/Notifications/OperationRunNotificationTest.php tests/Feature/Authorization/OperatorExplanationSurfaceAuthorizationTest.php`
- `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/AdminConsentCallbackTest.php tests/Feature/Filament/WorkspaceOverviewDbOnlyTest.php tests/Feature/Guards/Spec194GovernanceActionSemanticsGuardTest.php tests/Feature/ManagedTenantOnboardingWizardTest.php tests/Feature/Onboarding/OnboardingVerificationTest.php tests/Feature/Operations/QueuedExecutionAuditTrailTest.php tests/Unit/Operations/QueuedExecutionLegitimacyGateTest.php`

## Notes
- browser validation was not re-run in this pass

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #271
2026-04-24 05:44:54 +00:00

253 lines
9.7 KiB
PHP

<?php
declare(strict_types=1);
use App\Filament\Pages\Operations\TenantlessOperationRunViewer;
use App\Models\OperationRun;
use App\Models\Tenant;
use App\Support\Baselines\BaselineReasonCodes;
use App\Support\Workspaces\WorkspaceContext;
use Filament\Facades\Filament;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Livewire\Features\SupportTesting\Testable;
use Livewire\Livewire;
use Tests\Feature\Concerns\BuildsGovernanceArtifactTruthFixtures;
use Tests\TestCase;
uses(RefreshDatabase::class, BuildsGovernanceArtifactTruthFixtures::class);
function governanceVisibleText(Testable $component): string
{
$html = $component->html();
$html = preg_replace('/<script\b[^>]*>.*?<\/script>/is', '', $html) ?? $html;
$html = preg_replace('/<style\b[^>]*>.*?<\/style>/is', '', $html) ?? $html;
$html = preg_replace('/\s+wire:snapshot="[^"]*"/', '', $html) ?? $html;
$html = preg_replace('/\s+wire:effects="[^"]*"/', '', $html) ?? $html;
return trim((string) preg_replace('/\s+/', ' ', strip_tags($html)));
}
function governanceRunViewer(TestCase $testCase, $user, Tenant $tenant, OperationRun $run): Testable
{
Filament::setTenant(null, true);
$testCase->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id]);
session([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id]);
return Livewire::actingAs($user)
->test(TenantlessOperationRunViewer::class, ['run' => $run]);
}
it('renders a summary-first hierarchy for zero-output baseline compare runs', function (): void {
$tenant = Tenant::factory()->create();
[$user, $tenant] = createUserWithTenant(tenant: $tenant, role: 'owner');
$run = OperationRun::factory()->create([
'tenant_id' => (int) $tenant->getKey(),
'workspace_id' => (int) $tenant->workspace_id,
'type' => 'baseline_compare',
'status' => 'completed',
'outcome' => 'partially_succeeded',
'context' => [
'baseline_compare' => [
'reason_code' => 'coverage_unproven',
'coverage' => [
'proof' => false,
],
],
],
'summary_counts' => [
'total' => 0,
'processed' => 0,
'errors_recorded' => 1,
],
'completed_at' => now(),
]);
$component = governanceRunViewer($this, $user, $tenant, $run)
->assertSee('Decision')
->assertSee('Artifact impact')
->assertSee('Dominant cause')
->assertSee('Primary next step')
->assertSee('The compare finished, but no decision-grade result is available yet.')
->assertSee('Artifact truth details')
->assertSee('Monitoring detail');
$pageText = governanceVisibleText($component);
expect(mb_strpos($pageText, 'Decision'))->toBeLessThan(mb_strpos($pageText, 'Artifact truth details'))
->and(mb_strpos($pageText, 'Decision'))->toBeLessThan(mb_strpos($pageText, 'Monitoring detail'))
->and($pageText)->toContain('no decision-grade result is available yet');
});
it('keeps blocked baseline capture summaries ahead of diagnostics without adding new run-detail actions', function (): void {
$tenant = Tenant::factory()->create();
[$user, $tenant] = createUserWithTenant(tenant: $tenant, role: 'owner');
$run = OperationRun::factory()->create([
'tenant_id' => (int) $tenant->getKey(),
'workspace_id' => (int) $tenant->workspace_id,
'type' => 'baseline_capture',
'status' => 'completed',
'outcome' => 'blocked',
'context' => [
'reason_code' => 'missing_capability',
'baseline_capture' => [
'subjects_total' => 0,
'gaps' => [
'count' => 0,
],
],
],
'failure_summary' => [[
'reason_code' => 'missing_capability',
'message' => 'A required capability is missing for this run.',
]],
'completed_at' => now(),
]);
$component = governanceRunViewer($this, $user, $tenant, $run)
->assertActionVisible('operate_hub_back_to_operations')
->assertActionVisible('refresh')
->assertSee('Blocked by prerequisite')
->assertSee('No baseline was captured')
->assertSee('Artifact impact')
->assertSee('Dominant cause');
$pageText = governanceVisibleText($component);
expect(mb_substr_count($pageText, 'No baseline was captured'))->toBe(1)
->and(mb_strpos($pageText, 'No baseline was captured'))->toBeLessThan(mb_strpos($pageText, 'Artifact truth details'));
});
it('shows processing outcome separately from artifact impact for stale evidence snapshot runs', function (): void {
$tenant = Tenant::factory()->create();
[$user, $tenant] = createUserWithTenant(tenant: $tenant, role: 'owner');
$run = $this->makeArtifactTruthRun($tenant, 'tenant.evidence.snapshot.generate');
$this->makeStaleArtifactTruthEvidenceSnapshot(
tenant: $tenant,
snapshotOverrides: [
'operation_run_id' => (int) $run->getKey(),
],
);
governanceRunViewer($this, $user, $tenant, $run)
->assertSee('Outcome')
->assertSee('Artifact impact')
->assertSee('Completed successfully')
->assertSee('The snapshot finished processing, but its evidence basis is already stale.')
->assertSee('Result trust');
});
it('preserves a dominant cause plus secondary causes for degraded review composition runs', function (): void {
$tenant = Tenant::factory()->create();
[$user, $tenant] = createUserWithTenant(tenant: $tenant, role: 'owner');
$run = $this->makeArtifactTruthRun($tenant, 'tenant.review.compose');
$snapshot = $this->makeStaleArtifactTruthEvidenceSnapshot($tenant);
$this->makeArtifactTruthReview(
tenant: $tenant,
user: $user,
snapshot: $snapshot,
reviewOverrides: [
'operation_run_id' => (int) $run->getKey(),
'completeness_state' => 'partial',
],
summaryOverrides: [
'section_state_counts' => [
'complete' => 4,
'partial' => 1,
'missing' => 1,
'stale' => 0,
],
],
);
$component = governanceRunViewer($this, $user, $tenant, $run)
->assertSee('Dominant cause')
->assertSee('Missing sections')
->assertSee('Secondary causes')
->assertSee('Stale evidence basis');
$pageText = governanceVisibleText($component);
expect(mb_strpos($pageText, 'Missing sections'))->toBeLessThan(mb_strpos($pageText, 'Secondary causes'))
->and($pageText)->toContain('stale evidence');
});
it('shows failed-latest-inventory baseline capture summaries before diagnostics', function (): void {
$tenant = Tenant::factory()->create();
[$user, $tenant] = createUserWithTenant(tenant: $tenant, role: 'owner');
$run = OperationRun::factory()->create([
'tenant_id' => (int) $tenant->getKey(),
'workspace_id' => (int) $tenant->workspace_id,
'type' => 'baseline_capture',
'status' => 'completed',
'outcome' => 'blocked',
'context' => [
'reason_code' => BaselineReasonCodes::CAPTURE_INVENTORY_FAILED,
'baseline_capture' => [
'reason_code' => BaselineReasonCodes::CAPTURE_INVENTORY_FAILED,
'subjects_total' => 0,
'current_baseline_changed' => false,
],
],
'failure_summary' => [[
'reason_code' => BaselineReasonCodes::CAPTURE_INVENTORY_FAILED,
'message' => 'Capture blocked because the latest inventory sync failed.',
]],
'completed_at' => now(),
]);
$component = governanceRunViewer($this, $user, $tenant, $run)
->assertSee('The baseline capture was blocked because the latest inventory sync failed.')
->assertSee('Latest inventory sync failed')
->assertSee('Artifact impact')
->assertSee('Dominant cause');
$pageText = governanceVisibleText($component);
expect(mb_strpos($pageText, 'The baseline capture was blocked because the latest inventory sync failed.'))
->toBeLessThan(mb_strpos($pageText, 'Artifact truth details'));
});
it('shows zero-subject baseline capture summaries before diagnostics', function (): void {
$tenant = Tenant::factory()->create();
[$user, $tenant] = createUserWithTenant(tenant: $tenant, role: 'owner');
$run = OperationRun::factory()->create([
'tenant_id' => (int) $tenant->getKey(),
'workspace_id' => (int) $tenant->workspace_id,
'type' => 'baseline_capture',
'status' => 'completed',
'outcome' => 'partially_succeeded',
'context' => [
'reason_code' => BaselineReasonCodes::CAPTURE_ZERO_SUBJECTS,
'baseline_capture' => [
'reason_code' => BaselineReasonCodes::CAPTURE_ZERO_SUBJECTS,
'subjects_total' => 0,
'current_baseline_changed' => false,
],
],
'summary_counts' => [
'total' => 0,
'processed' => 0,
'failed' => 0,
],
'completed_at' => now(),
]);
$component = governanceRunViewer($this, $user, $tenant, $run)
->assertSee('The baseline capture finished without a usable baseline because no governed subjects were in scope.')
->assertSee('No subjects were in scope')
->assertSee('Primary next step');
$pageText = governanceVisibleText($component);
expect(mb_strpos($pageText, 'The baseline capture finished without a usable baseline because no governed subjects were in scope.'))
->toBeLessThan(mb_strpos($pageText, 'Artifact truth details'));
});