## Summary - add a workspace-scoped baseline compare matrix page under baseline profiles - derive matrix tenant summaries, subject rows, cell states, freshness, and trust from existing snapshots, compare runs, and findings - add confirmation-gated `Compare assigned tenants` actions on the baseline detail and matrix surfaces without introducing a workspace umbrella run - preserve matrix navigation context into tenant compare and finding drilldowns and add centralized matrix badge semantics - include spec, plan, data model, contracts, quickstart, tasks, and focused feature/browser coverage for Spec 190 ## Verification - `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Unit/Badges/BaselineCompareMatrixBadgesTest.php tests/Feature/Baselines/BaselineCompareMatrixBuilderTest.php tests/Feature/Baselines/BaselineCompareMatrixCompareAllActionTest.php tests/Feature/Baselines/BaselineComparePerformanceGuardTest.php tests/Feature/Filament/BaselineCompareMatrixPageTest.php tests/Feature/Filament/BaselineProfileCompareStartSurfaceTest.php tests/Feature/Rbac/BaselineCompareMatrixAuthorizationTest.php tests/Feature/Guards/ActionSurfaceContractTest.php tests/Feature/Guards/NoAdHocStatusBadgesTest.php tests/Feature/Guards/NoDiagnosticWarningBadgesTest.php` - `cd apps/platform && ./vendor/bin/sail bin pint --dirty --format agent` - completed an integrated-browser smoke flow locally for matrix render, differ filter, finding drilldown round-trip, and `Compare assigned tenants` confirmation/action Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #221
164 lines
5.9 KiB
PHP
164 lines
5.9 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use App\Filament\Pages\BaselineCompareMatrix;
|
|
use App\Filament\Resources\BaselineProfileResource;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Livewire\Livewire;
|
|
use Tests\Feature\Concerns\BuildsBaselineCompareMatrixFixtures;
|
|
|
|
uses(RefreshDatabase::class, BuildsBaselineCompareMatrixFixtures::class);
|
|
|
|
it('renders the baseline compare matrix with reference truth, legends, and explicit drilldowns', function (): void {
|
|
$fixture = $this->makeBaselineCompareMatrixFixture();
|
|
|
|
$run = $this->makeBaselineCompareMatrixRun(
|
|
$fixture['visibleTenantTwo'],
|
|
$fixture['profile'],
|
|
$fixture['snapshot'],
|
|
);
|
|
|
|
$this->makeBaselineCompareMatrixRun(
|
|
$fixture['visibleTenant'],
|
|
$fixture['profile'],
|
|
$fixture['snapshot'],
|
|
);
|
|
|
|
$this->makeBaselineCompareMatrixFinding(
|
|
$fixture['visibleTenantTwo'],
|
|
$fixture['profile'],
|
|
$run,
|
|
'wifi-corp-profile',
|
|
);
|
|
|
|
$session = $this->setAdminWorkspaceContext($fixture['user'], $fixture['workspace'], $fixture['visibleTenant']);
|
|
|
|
$this->withSession($session)
|
|
->get(BaselineProfileResource::compareMatrixUrl($fixture['profile']))
|
|
->assertOk()
|
|
->assertSee('Visible-set baseline')
|
|
->assertSee('Reference overview')
|
|
->assertSee('State legend')
|
|
->assertSee('Tenant summaries')
|
|
->assertSee('Subject-by-tenant matrix')
|
|
->assertSee('WiFi Corp Profile')
|
|
->assertSee((string) $fixture['visibleTenant']->name)
|
|
->assertSee((string) $fixture['visibleTenantTwo']->name)
|
|
->assertSee('Open finding')
|
|
->assertSee('Open tenant compare');
|
|
});
|
|
|
|
it('keeps query-backed filters and subject focus on the matrix route and drilldown links', function (): void {
|
|
$fixture = $this->makeBaselineCompareMatrixFixture();
|
|
|
|
$run = $this->makeBaselineCompareMatrixRun(
|
|
$fixture['visibleTenant'],
|
|
$fixture['profile'],
|
|
$fixture['snapshot'],
|
|
);
|
|
|
|
$finding = $this->makeBaselineCompareMatrixFinding(
|
|
$fixture['visibleTenant'],
|
|
$fixture['profile'],
|
|
$run,
|
|
'wifi-corp-profile',
|
|
['severity' => 'critical'],
|
|
);
|
|
|
|
$this->makeBaselineCompareMatrixRun(
|
|
$fixture['visibleTenantTwo'],
|
|
$fixture['profile'],
|
|
$fixture['snapshot'],
|
|
);
|
|
|
|
$this->setAdminWorkspaceContext($fixture['user'], $fixture['workspace'], $fixture['visibleTenant']);
|
|
|
|
$component = Livewire::withQueryParams([
|
|
'policy_type' => ['deviceConfiguration'],
|
|
'state' => ['differ'],
|
|
'severity' => ['critical'],
|
|
'subject_key' => 'wifi-corp-profile',
|
|
])
|
|
->actingAs($fixture['user'])
|
|
->test(BaselineCompareMatrix::class, ['record' => $fixture['profile']->getKey()])
|
|
->assertSee('Clear subject focus')
|
|
->assertDontSee('Windows Compliance');
|
|
|
|
$tenantCompareUrl = $component->instance()->tenantCompareUrl((int) $fixture['visibleTenant']->getKey(), 'wifi-corp-profile');
|
|
$findingUrl = $component->instance()->findingUrl((int) $fixture['visibleTenant']->getKey(), (int) $finding->getKey(), 'wifi-corp-profile');
|
|
|
|
expect($tenantCompareUrl)->toContain('baseline_profile_id='.(int) $fixture['profile']->getKey())
|
|
->and($tenantCompareUrl)->toContain('subject_key=wifi-corp-profile')
|
|
->and($tenantCompareUrl)->toContain('nav%5Bsource_surface%5D=baseline_compare_matrix')
|
|
->and($findingUrl)->toContain('nav%5Bsource_surface%5D=baseline_compare_matrix');
|
|
});
|
|
|
|
it('renders a blocked state when the baseline profile has no usable reference snapshot', function (): void {
|
|
$fixture = $this->makeBaselineCompareMatrixFixture();
|
|
|
|
$fixture['snapshot']->markIncomplete();
|
|
|
|
$session = $this->setAdminWorkspaceContext($fixture['user'], $fixture['workspace']);
|
|
|
|
$this->withSession($session)
|
|
->get(BaselineProfileResource::compareMatrixUrl($fixture['profile']))
|
|
->assertOk()
|
|
->assertSee('No usable reference snapshot')
|
|
->assertSee('Capture a complete baseline snapshot before using the compare matrix.');
|
|
});
|
|
|
|
it('renders an empty state when the baseline profile has no assigned tenants', function (): void {
|
|
$fixture = $this->makeBaselineCompareMatrixFixture();
|
|
|
|
$fixture['profile']->tenantAssignments()->delete();
|
|
|
|
$session = $this->setAdminWorkspaceContext($fixture['user'], $fixture['workspace']);
|
|
|
|
$this->withSession($session)
|
|
->get(BaselineProfileResource::compareMatrixUrl($fixture['profile']))
|
|
->assertOk()
|
|
->assertSee('No assigned tenants');
|
|
});
|
|
|
|
it('renders an empty state when the assigned set is not visible to the current actor', function (): void {
|
|
$fixture = $this->makeBaselineCompareMatrixFixture();
|
|
$viewer = \App\Models\User::factory()->create();
|
|
|
|
\App\Models\WorkspaceMembership::factory()->create([
|
|
'workspace_id' => (int) $fixture['workspace']->getKey(),
|
|
'user_id' => (int) $viewer->getKey(),
|
|
'role' => 'owner',
|
|
]);
|
|
|
|
$session = $this->setAdminWorkspaceContext($viewer, $fixture['workspace']);
|
|
|
|
$this->withSession($session)
|
|
->get(BaselineProfileResource::compareMatrixUrl($fixture['profile']))
|
|
->assertOk()
|
|
->assertSee('No visible assigned tenants');
|
|
});
|
|
|
|
it('renders an empty state when no rows match the current filters', function (): void {
|
|
$fixture = $this->makeBaselineCompareMatrixFixture();
|
|
|
|
$this->makeBaselineCompareMatrixRun(
|
|
$fixture['visibleTenant'],
|
|
$fixture['profile'],
|
|
$fixture['snapshot'],
|
|
);
|
|
|
|
$this->makeBaselineCompareMatrixRun(
|
|
$fixture['visibleTenantTwo'],
|
|
$fixture['profile'],
|
|
$fixture['snapshot'],
|
|
);
|
|
|
|
$session = $this->setAdminWorkspaceContext($fixture['user'], $fixture['workspace']);
|
|
|
|
$this->withSession($session)
|
|
->get(BaselineProfileResource::compareMatrixUrl($fixture['profile']).'?state[]=missing')
|
|
->assertOk()
|
|
->assertSee('No rows match the current filters');
|
|
});
|