Some checks failed
PR Fast Feedback / fast-feedback (pull_request) Failing after 4m7s
Implemented deterministic Baseline Result Semantics (Spec 383), introducing CompareSubjectResult and CompareEvidenceResult. Replaced generic arrays with strict Data Transfer Objects for Baseline engine output.
253 lines
9.8 KiB
PHP
253 lines
9.8 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use App\Filament\Pages\Operations\TenantlessOperationRunViewer;
|
|
use App\Models\ManagedEnvironment;
|
|
use App\Models\OperationRun;
|
|
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, ManagedEnvironment $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 = ManagedEnvironment::factory()->create();
|
|
[$user, $tenant] = createUserWithTenant(tenant: $tenant, role: 'owner');
|
|
|
|
$run = OperationRun::factory()->create([
|
|
'managed_environment_id' => (int) $tenant->getKey(),
|
|
'workspace_id' => (int) $tenant->workspace_id,
|
|
'type' => 'baseline_compare',
|
|
'status' => 'completed',
|
|
'outcome' => 'partially_succeeded',
|
|
'context' => [
|
|
'baseline_compare' => [
|
|
'reason_code' => 'compare_coverage_incomplete',
|
|
'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 = ManagedEnvironment::factory()->create();
|
|
[$user, $tenant] = createUserWithTenant(tenant: $tenant, role: 'owner');
|
|
|
|
$run = OperationRun::factory()->create([
|
|
'managed_environment_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 = ManagedEnvironment::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 = ManagedEnvironment::factory()->create();
|
|
[$user, $tenant] = createUserWithTenant(tenant: $tenant, role: 'owner');
|
|
|
|
$run = $this->makeArtifactTruthRun($tenant, 'environment.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 = ManagedEnvironment::factory()->create();
|
|
[$user, $tenant] = createUserWithTenant(tenant: $tenant, role: 'owner');
|
|
|
|
$run = OperationRun::factory()->create([
|
|
'managed_environment_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 = ManagedEnvironment::factory()->create();
|
|
[$user, $tenant] = createUserWithTenant(tenant: $tenant, role: 'owner');
|
|
|
|
$run = OperationRun::factory()->create([
|
|
'managed_environment_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'));
|
|
});
|