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.
168 lines
6.1 KiB
PHP
168 lines
6.1 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use App\Filament\Pages\BaselineCompareLanding;
|
|
use App\Models\BaselineProfile;
|
|
use App\Models\BaselineSnapshot;
|
|
use App\Models\BaselineTenantAssignment;
|
|
use App\Models\OperationRun;
|
|
use App\Support\OperationRunOutcome;
|
|
use App\Support\OperationRunStatus;
|
|
use App\Support\OperationRunType;
|
|
use App\Support\Workspaces\WorkspaceContext;
|
|
use Filament\Facades\Filament;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Tests\Feature\Baselines\Support\BaselineSubjectResolutionFixtures;
|
|
|
|
uses(RefreshDatabase::class);
|
|
|
|
/**
|
|
* @return array<string, mixed>
|
|
*/
|
|
function baselineCompareLandingGapContext(): array
|
|
{
|
|
return array_replace_recursive(BaselineSubjectResolutionFixtures::compareContext([
|
|
BaselineSubjectResolutionFixtures::structuredGap([
|
|
'policy_type' => 'deviceConfiguration',
|
|
'subject_key' => 'WiFi-Corp-Profile',
|
|
'resolution_outcome' => 'unresolved_ambiguous_identity',
|
|
'reason_code' => 'unresolved_duplicate_candidates',
|
|
'operator_action_category' => 'inspect_subject_mapping',
|
|
]),
|
|
BaselineSubjectResolutionFixtures::structuredGap([
|
|
'policy_type' => 'deviceConfiguration',
|
|
'subject_key' => 'VPN-Always-On',
|
|
'resolution_outcome' => 'unresolved_ambiguous_identity',
|
|
'reason_code' => 'unresolved_duplicate_candidates',
|
|
'operator_action_category' => 'inspect_subject_mapping',
|
|
]),
|
|
BaselineSubjectResolutionFixtures::structuredGap([
|
|
'policy_type' => 'deviceConfiguration',
|
|
'subject_key' => 'Deleted-Policy-ABC',
|
|
'resolution_outcome' => 'missing_local_evidence',
|
|
'reason_code' => 'missing_local_evidence',
|
|
'operator_action_category' => 'run_policy_sync_or_backup',
|
|
]),
|
|
]), [
|
|
'baseline_compare' => [
|
|
'subjects_total' => 50,
|
|
'reason_code' => 'compare_evidence_incomplete',
|
|
'fidelity' => 'meta',
|
|
'coverage' => [
|
|
'proof' => true,
|
|
'covered_types' => ['deviceConfiguration'],
|
|
'uncovered_types' => [],
|
|
'effective_types' => ['deviceConfiguration'],
|
|
],
|
|
'evidence_capture' => [
|
|
'requested' => 50,
|
|
'succeeded' => 47,
|
|
'skipped' => 0,
|
|
'failed' => 3,
|
|
'throttled' => 0,
|
|
],
|
|
],
|
|
]);
|
|
}
|
|
|
|
function seedBaselineCompareLandingGapRun(\App\Models\ManagedEnvironment $tenant): OperationRun
|
|
{
|
|
$profile = BaselineProfile::factory()->active()->create([
|
|
'workspace_id' => (int) $tenant->workspace_id,
|
|
]);
|
|
|
|
$snapshot = BaselineSnapshot::factory()->create([
|
|
'workspace_id' => (int) $tenant->workspace_id,
|
|
'baseline_profile_id' => (int) $profile->getKey(),
|
|
]);
|
|
|
|
$profile->update(['active_snapshot_id' => (int) $snapshot->getKey()]);
|
|
|
|
BaselineTenantAssignment::factory()->create([
|
|
'workspace_id' => (int) $tenant->workspace_id,
|
|
'managed_environment_id' => (int) $tenant->getKey(),
|
|
'baseline_profile_id' => (int) $profile->getKey(),
|
|
]);
|
|
|
|
return OperationRun::factory()->create([
|
|
'managed_environment_id' => (int) $tenant->getKey(),
|
|
'workspace_id' => (int) $tenant->workspace_id,
|
|
'type' => OperationRunType::BaselineCompare->value,
|
|
'status' => OperationRunStatus::Completed->value,
|
|
'outcome' => OperationRunOutcome::PartiallySucceeded->value,
|
|
'completed_at' => now(),
|
|
'context' => baselineCompareLandingGapContext(),
|
|
]);
|
|
}
|
|
|
|
it('does not authorize baseline compare from only the remembered admin environment', function (): void {
|
|
[$user, $tenant] = createUserWithTenant(role: 'owner');
|
|
|
|
$this->actingAs($user);
|
|
Filament::setCurrentPanel('admin');
|
|
Filament::setTenant(null, true);
|
|
Filament::bootCurrentPanel();
|
|
|
|
session()->put(WorkspaceContext::SESSION_KEY, (int) $tenant->workspace_id);
|
|
session()->put(WorkspaceContext::LAST_ENVIRONMENT_IDS_SESSION_KEY, [
|
|
(string) $tenant->workspace_id => (int) $tenant->getKey(),
|
|
]);
|
|
|
|
expect(BaselineCompareLanding::canAccess())->toBeFalse();
|
|
|
|
$this->get('/admin/baseline-compare-landing')
|
|
->assertNotFound();
|
|
});
|
|
|
|
it('renders tenant landing evidence-gap details with the same search affordances as the canonical run detail', function (): void {
|
|
[$user, $tenant] = createUserWithTenant(role: 'owner');
|
|
$this->actingAs($user);
|
|
|
|
$tenant->makeCurrent();
|
|
Filament::setTenant($tenant, true);
|
|
|
|
seedBaselineCompareLandingGapRun($tenant);
|
|
|
|
baselineCompareLandingLivewire($tenant)
|
|
->assertSee('Evidence gap details')
|
|
->assertSee('Search gap details')
|
|
->assertSee('Search by reason, type, class, outcome, action, or subject key')
|
|
->assertSee('Reason')
|
|
->assertSee('Duplicate provider candidates')
|
|
->assertSee('Missing local evidence')
|
|
->assertSee('Subject class')
|
|
->assertSee('Outcome')
|
|
->assertSee('Next action')
|
|
->assertSee('WiFi-Corp-Profile')
|
|
->assertSee('Inspect subject mapping')
|
|
->assertSee('Baseline compare evidence');
|
|
});
|
|
|
|
it('renders tenant landing evidence-gap details without invoking graph during render', function (): void {
|
|
[$user, $tenant] = createUserWithTenant(role: 'owner');
|
|
$this->actingAs($user);
|
|
|
|
$tenant->makeCurrent();
|
|
Filament::setTenant($tenant, true);
|
|
bindFailHardGraphClient();
|
|
|
|
seedBaselineCompareLandingGapRun($tenant);
|
|
|
|
baselineCompareLandingLivewire($tenant)
|
|
->assertSee('Evidence gap details')
|
|
->assertSee('WiFi-Corp-Profile')
|
|
->assertSee('Baseline compare evidence');
|
|
});
|
|
|
|
it('returns 404 for non-members on the tenant landing even when evidence-gap details exist', function (): void {
|
|
[$member, $tenant] = createUserWithTenant(role: 'owner');
|
|
$nonMember = \App\Models\User::factory()->create();
|
|
|
|
seedBaselineCompareLandingGapRun($tenant);
|
|
|
|
$this->actingAs($nonMember)
|
|
->get(BaselineCompareLanding::getUrl(tenant: $tenant, panel: 'admin'))
|
|
->assertNotFound();
|
|
});
|