## Summary - replace the inventory dependency GET/apply flow with an embedded native Filament `TableComponent` - convert tenant required permissions and evidence overview to native page-owned Filament tables with mount-only query seeding and preserved scope authority - extend focused Pest, Livewire, RBAC, and guard coverage, and update the Spec 196 artifacts and release close-out notes ## Verification - `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/InventoryItemDependenciesTest.php tests/Feature/Filament/InventoryItemDependencyEdgesTableTest.php tests/Feature/Rbac/TenantRequiredPermissionsTrustedStateTest.php tests/Feature/Filament/TenantRequiredPermissionsPageTest.php tests/Feature/Evidence/EvidenceOverviewPageTest.php tests/Feature/Filament/EvidenceOverviewDerivedStateMemoizationTest.php tests/Feature/Guards/FilamentTableStandardsGuardTest.php tests/Unit/TenantRequiredPermissionsFilteringTest.php tests/Unit/TenantRequiredPermissionsOverallStatusTest.php tests/Unit/TenantRequiredPermissionsFeatureImpactTest.php tests/Unit/TenantRequiredPermissionsFreshnessTest.php tests/Unit/TenantRequiredPermissionsCopyPayloadTest.php` (`45` tests, `177` assertions) - `cd apps/platform && ./vendor/bin/sail bin pint --dirty --format agent` - integrated-browser smoke on localhost for inventory detail dependencies, tenant required permissions, and evidence overview Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #236
127 lines
4.2 KiB
PHP
127 lines
4.2 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use App\Filament\Pages\TenantRequiredPermissions;
|
|
use App\Models\Tenant;
|
|
use App\Models\TenantPermission;
|
|
use App\Models\User;
|
|
use App\Support\Workspaces\WorkspaceContext;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Livewire\Livewire;
|
|
|
|
uses(RefreshDatabase::class);
|
|
|
|
function seedTenantRequiredPermissionsFixture(Tenant $tenant): void
|
|
{
|
|
config()->set('intune_permissions.permissions', [
|
|
[
|
|
'key' => 'DeviceManagementApps.Read.All',
|
|
'type' => 'application',
|
|
'description' => 'Backup application permission',
|
|
'features' => ['backup'],
|
|
],
|
|
[
|
|
'key' => 'Group.Read.All',
|
|
'type' => 'delegated',
|
|
'description' => 'Backup delegated permission',
|
|
'features' => ['backup'],
|
|
],
|
|
[
|
|
'key' => 'Reports.Read.All',
|
|
'type' => 'application',
|
|
'description' => 'Reporting permission',
|
|
'features' => ['reporting'],
|
|
],
|
|
]);
|
|
config()->set('entra_permissions.permissions', []);
|
|
|
|
TenantPermission::query()->create([
|
|
'tenant_id' => (int) $tenant->getKey(),
|
|
'permission_key' => 'Group.Read.All',
|
|
'status' => 'missing',
|
|
'details' => ['source' => 'fixture'],
|
|
'last_checked_at' => now(),
|
|
]);
|
|
|
|
TenantPermission::query()->create([
|
|
'tenant_id' => (int) $tenant->getKey(),
|
|
'permission_key' => 'Reports.Read.All',
|
|
'status' => 'granted',
|
|
'details' => ['source' => 'fixture'],
|
|
'last_checked_at' => now(),
|
|
]);
|
|
}
|
|
|
|
function tenantRequiredPermissionsComponent(User $user, Tenant $tenant, array $query = [])
|
|
{
|
|
test()->actingAs($user);
|
|
setAdminPanelContext();
|
|
session()->put(WorkspaceContext::SESSION_KEY, (int) $tenant->workspace_id);
|
|
|
|
$query = array_merge([
|
|
'tenant' => (string) $tenant->external_id,
|
|
], $query);
|
|
|
|
return Livewire::withQueryParams($query)->test(TenantRequiredPermissions::class);
|
|
}
|
|
|
|
it('uses native table filters and search while keeping summary state aligned with visible rows', function (): void {
|
|
[$user, $tenant] = createUserWithTenant(role: 'readonly');
|
|
seedTenantRequiredPermissionsFixture($tenant);
|
|
|
|
$component = tenantRequiredPermissionsComponent($user, $tenant)
|
|
->assertTableFilterExists('status')
|
|
->assertTableFilterExists('type')
|
|
->assertTableFilterExists('features')
|
|
->assertCanSeeTableRecords([
|
|
'DeviceManagementApps.Read.All',
|
|
'Group.Read.All',
|
|
])
|
|
->assertCanNotSeeTableRecords(['Reports.Read.All'])
|
|
->assertSee('Missing application permissions')
|
|
->assertSee('Guidance');
|
|
|
|
$component
|
|
->filterTable('status', 'present')
|
|
->filterTable('type', 'application')
|
|
->searchTable('Reports')
|
|
->assertCountTableRecords(1)
|
|
->assertCanSeeTableRecords(['Reports.Read.All'])
|
|
->assertCanNotSeeTableRecords([
|
|
'DeviceManagementApps.Read.All',
|
|
'Group.Read.All',
|
|
]);
|
|
|
|
$viewModel = $component->instance()->viewModel();
|
|
|
|
expect($viewModel['overview']['counts'])->toBe([
|
|
'missing_application' => 0,
|
|
'missing_delegated' => 0,
|
|
'present' => 1,
|
|
'error' => 0,
|
|
])
|
|
->and(array_column($viewModel['permissions'], 'key'))->toBe(['Reports.Read.All'])
|
|
->and($viewModel['copy']['application'])->toBe('DeviceManagementApps.Read.All');
|
|
});
|
|
|
|
it('keeps copy payloads feature-scoped and shows the native no-matches state', function (): void {
|
|
[$user, $tenant] = createUserWithTenant(role: 'readonly');
|
|
seedTenantRequiredPermissionsFixture($tenant);
|
|
|
|
$component = tenantRequiredPermissionsComponent($user, $tenant)
|
|
->set('tableFilters.features.values', ['backup'])
|
|
->assertSet('tableFilters.features.values', ['backup']);
|
|
|
|
$viewModel = $component->instance()->viewModel();
|
|
|
|
expect($viewModel['copy']['application'])->toBe('DeviceManagementApps.Read.All')
|
|
->and($viewModel['copy']['delegated'])->toBe('Group.Read.All');
|
|
|
|
$component
|
|
->searchTable('no-such-permission')
|
|
->assertCountTableRecords(0)
|
|
->assertSee('No matches')
|
|
->assertTableEmptyStateActionsExistInOrder(['clear_filters']);
|
|
});
|