create(); $tenantB = Tenant::factory()->create(); OperationRun::factory()->create([ 'tenant_id' => $tenantA->getKey(), 'type' => 'policy.sync', 'status' => 'queued', 'outcome' => 'pending', ]); OperationRun::factory()->create([ 'tenant_id' => $tenantB->getKey(), 'type' => 'inventory.sync', 'status' => 'queued', 'outcome' => 'pending', ]); $user = User::factory()->create(); $user->tenants()->syncWithoutDetaching([ $tenantA->getKey() => ['role' => 'owner'], $tenantB->getKey() => ['role' => 'owner'], ]); $this->actingAs($user) ->get(OperationRunResource::getUrl('index', tenant: $tenantA)) ->assertOk() ->assertSee('Policy sync') ->assertDontSee('Inventory sync'); }); test('operation run view is not accessible cross-tenant', function () { $tenantA = Tenant::factory()->create(); $tenantB = Tenant::factory()->create(); $runB = OperationRun::factory()->create([ 'tenant_id' => $tenantB->getKey(), 'type' => 'inventory.sync', 'status' => 'queued', 'outcome' => 'pending', ]); $user = User::factory()->create(); $user->tenants()->syncWithoutDetaching([ $tenantA->getKey() => ['role' => 'owner'], $tenantB->getKey() => ['role' => 'owner'], ]); $this->actingAs($user) ->get(OperationRunResource::getUrl('view', ['record' => $runB], tenant: $tenantA)) ->assertNotFound(); }); test('readonly users can view operation runs for their tenant', function () { $tenant = Tenant::factory()->create(); $run = OperationRun::factory()->create([ 'tenant_id' => $tenant->getKey(), 'type' => 'drift.generate', 'status' => 'queued', 'outcome' => 'pending', ]); $user = User::factory()->create(); $user->tenants()->syncWithoutDetaching([ $tenant->getKey() => ['role' => 'readonly'], ]); $this->actingAs($user) ->get(OperationRunResource::getUrl('index', tenant: $tenant)) ->assertOk() ->assertSee('Drift generation'); $this->actingAs($user) ->get(OperationRunResource::getUrl('view', ['record' => $run], tenant: $tenant)) ->assertOk() ->assertSee('Drift generation'); });