TenantAtlas/tests/Feature/Filament/TenantOwnedResourceScopeParityTest.php
2026-03-18 09:30:13 +01:00

247 lines
9.1 KiB
PHP

<?php
declare(strict_types=1);
use App\Filament\Resources\BackupScheduleResource;
use App\Filament\Resources\BackupScheduleResource\Pages\ListBackupSchedules;
use App\Filament\Resources\BackupSetResource;
use App\Filament\Resources\BackupSetResource\Pages\ListBackupSets;
use App\Filament\Resources\EntraGroupResource;
use App\Filament\Resources\EntraGroupResource\Pages\ListEntraGroups;
use App\Filament\Resources\FindingResource;
use App\Filament\Resources\FindingResource\Pages\ListFindings;
use App\Filament\Resources\InventoryItemResource;
use App\Filament\Resources\InventoryItemResource\Pages\ListInventoryItems;
use App\Filament\Resources\PolicyResource;
use App\Filament\Resources\PolicyResource\Pages\ListPolicies;
use App\Filament\Resources\PolicyVersionResource;
use App\Filament\Resources\PolicyVersionResource\Pages\ListPolicyVersions;
use App\Filament\Resources\RestoreRunResource;
use App\Filament\Resources\RestoreRunResource\Pages\ListRestoreRuns;
use App\Models\BackupSchedule;
use App\Models\BackupSet;
use App\Models\EntraGroup;
use App\Models\Finding;
use App\Models\InventoryItem;
use App\Models\Policy;
use App\Models\PolicyVersion;
use App\Models\RestoreRun;
use App\Models\Tenant;
use App\Support\Workspaces\WorkspaceContext;
use Filament\Facades\Filament;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Livewire\Livewire;
uses(RefreshDatabase::class);
function tenantOwnedAdminSession(Tenant $tenant): array
{
return [
WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id,
WorkspaceContext::LAST_TENANT_IDS_SESSION_KEY => [
(string) $tenant->workspace_id => (int) $tenant->getKey(),
],
];
}
dataset('tenant-owned-list-pages', [
'policy list' => [
ListPolicies::class,
static fn (Tenant $tenant, string $label): Policy => Policy::factory()->for($tenant)->create([
'display_name' => $label,
]),
],
'policy-version list' => [
ListPolicyVersions::class,
static function (Tenant $tenant, string $label): PolicyVersion {
$policy = Policy::factory()->for($tenant)->create([
'display_name' => $label.' policy',
]);
return PolicyVersion::factory()->for($tenant)->for($policy)->create([
'version_number' => random_int(1, 9999),
]);
},
],
'backup-schedule list' => [
ListBackupSchedules::class,
static function (Tenant $tenant, string $label): BackupSchedule {
return BackupSchedule::create([
'tenant_id' => (int) $tenant->getKey(),
'name' => $label,
'is_enabled' => true,
'timezone' => 'UTC',
'frequency' => 'daily',
'time_of_day' => '10:00:00',
'days_of_week' => null,
'policy_types' => ['deviceConfiguration'],
'include_foundations' => true,
'retention_keep_last' => 30,
'next_run_at' => now()->addHour(),
]);
},
],
'backup-set list' => [
ListBackupSets::class,
static fn (Tenant $tenant, string $label): BackupSet => BackupSet::factory()->for($tenant)->create([
'name' => $label,
]),
],
'restore-run list' => [
ListRestoreRuns::class,
static function (Tenant $tenant, string $label): RestoreRun {
$backupSet = BackupSet::factory()->for($tenant)->create([
'name' => $label.' backup set',
]);
return RestoreRun::factory()->for($tenant)->for($backupSet)->create();
},
],
'inventory-item list' => [
ListInventoryItems::class,
static fn (Tenant $tenant, string $label): InventoryItem => InventoryItem::factory()->create([
'tenant_id' => (int) $tenant->getKey(),
'display_name' => $label,
]),
],
'finding list' => [
ListFindings::class,
static fn (Tenant $tenant, string $label): Finding => Finding::factory()->for($tenant)->create(),
],
'entra-group list' => [
ListEntraGroups::class,
static fn (Tenant $tenant, string $label): EntraGroup => EntraGroup::factory()->for($tenant)->create([
'display_name' => $label,
]),
],
]);
dataset('tenant-owned-detail-pages', [
'policy view' => [
PolicyResource::class,
'view',
static fn (Tenant $tenant, string $label): Policy => Policy::factory()->for($tenant)->create([
'display_name' => $label,
]),
],
'policy-version view' => [
PolicyVersionResource::class,
'view',
static function (Tenant $tenant, string $label): PolicyVersion {
$policy = Policy::factory()->for($tenant)->create([
'display_name' => $label.' policy',
]);
return PolicyVersion::factory()->for($tenant)->for($policy)->create([
'version_number' => random_int(1, 9999),
]);
},
],
'backup-set view' => [
BackupSetResource::class,
'view',
static fn (Tenant $tenant, string $label): BackupSet => BackupSet::factory()->for($tenant)->create([
'name' => $label,
]),
],
'restore-run view' => [
RestoreRunResource::class,
'view',
static function (Tenant $tenant, string $label): RestoreRun {
$backupSet = BackupSet::factory()->for($tenant)->create([
'name' => $label.' backup set',
]);
return RestoreRun::factory()->for($tenant)->for($backupSet)->create();
},
],
'inventory-item view' => [
InventoryItemResource::class,
'view',
static fn (Tenant $tenant, string $label): InventoryItem => InventoryItem::factory()->create([
'tenant_id' => (int) $tenant->getKey(),
'display_name' => $label,
]),
],
'finding view' => [
FindingResource::class,
'view',
static fn (Tenant $tenant, string $label): Finding => Finding::factory()->for($tenant)->create(),
],
'entra-group view' => [
EntraGroupResource::class,
'view',
static fn (Tenant $tenant, string $label): EntraGroup => EntraGroup::factory()->for($tenant)->create([
'display_name' => $label,
]),
],
'backup-schedule edit' => [
BackupScheduleResource::class,
'edit',
static function (Tenant $tenant, string $label): BackupSchedule {
return BackupSchedule::create([
'tenant_id' => (int) $tenant->getKey(),
'name' => $label,
'is_enabled' => true,
'timezone' => 'UTC',
'frequency' => 'daily',
'time_of_day' => '10:00:00',
'days_of_week' => null,
'policy_types' => ['deviceConfiguration'],
'include_foundations' => true,
'retention_keep_last' => 30,
'next_run_at' => now()->addHour(),
]);
},
],
]);
it('scopes covered tenant-owned admin lists to the remembered canonical tenant', function (string $pageClass, Closure $makeRecord): void {
$tenantA = Tenant::factory()->create();
[$user, $tenantA] = createUserWithTenant(tenant: $tenantA, role: 'owner');
$tenantB = Tenant::factory()->create(['workspace_id' => (int) $tenantA->workspace_id]);
createUserWithTenant(tenant: $tenantB, user: $user, role: 'owner');
$allowed = $makeRecord($tenantA, 'Allowed record');
$blocked = $makeRecord($tenantB, 'Blocked record');
$this->actingAs($user);
Filament::setCurrentPanel('admin');
Filament::setTenant(null, true);
Filament::bootCurrentPanel();
session()->put(WorkspaceContext::SESSION_KEY, (int) $tenantA->workspace_id);
session()->put(WorkspaceContext::LAST_TENANT_IDS_SESSION_KEY, [
(string) $tenantA->workspace_id => (int) $tenantA->getKey(),
]);
Livewire::actingAs($user)->test($pageClass)
->assertCanSeeTableRecords([$allowed])
->assertCanNotSeeTableRecords([$blocked]);
})->with('tenant-owned-list-pages');
it('returns not found for covered tenant-owned admin detail pages outside the remembered canonical tenant', function (string $resourceClass, string $page, Closure $makeRecord): void {
$tenantA = Tenant::factory()->create();
[$user, $tenantA] = createUserWithTenant(tenant: $tenantA, role: 'owner');
$tenantB = Tenant::factory()->create(['workspace_id' => (int) $tenantA->workspace_id]);
createUserWithTenant(tenant: $tenantB, user: $user, role: 'owner');
$allowed = $makeRecord($tenantA, 'Allowed record');
$blocked = $makeRecord($tenantB, 'Blocked record');
$this->actingAs($user);
Filament::setCurrentPanel('admin');
Filament::setTenant(null, true);
Filament::bootCurrentPanel();
$session = tenantOwnedAdminSession($tenantA);
$this->withSession($session)
->get($resourceClass::getUrl($page, ['record' => $allowed], panel: 'admin'))
->assertSuccessful();
$this->withSession($session)
->get($resourceClass::getUrl($page, ['record' => $blocked], panel: 'admin'))
->assertNotFound();
})->with('tenant-owned-detail-pages');