actingAs($user) ->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id]) ->get(ManagedEnvironmentLinks::indexUrl($tenant)) ->assertOk(); }); it('returns 404 for non-members on the workspace-managed tenants index', function (): void { $tenant = ManagedEnvironment::factory()->create(); $user = User::factory()->create(); $this->actingAs($user) ->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id]) ->get('/admin/tenants') ->assertNotFound(); }); it('allows workspace members to open the canonical workspace-managed environment view route', function (): void { [$user, $tenant] = createMinimalUserWithTenant(role: 'owner'); $this->actingAs($user) ->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id]) ->get(ManagedEnvironmentLinks::viewUrl($tenant)) ->assertOk(); }); it('exposes a canonical provider connections link for a managed environment', function (): void { [$user, $tenant] = createMinimalUserWithTenant(role: 'owner'); $this->actingAs($user) ->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id]) ->get(ManagedEnvironmentLinks::viewUrl($tenant)) ->assertOk(); expect(ManagedEnvironmentLinks::providerConnectionsUrl($tenant)) ->toContain('/admin/provider-connections?managed_environment_id='.$tenant->external_id) ->not->toContain('/admin/tenants') ->not->toContain('/admin/t/'); }); it('returns 404 for non-members on the workspace-managed tenant view route', function (): void { $tenant = ManagedEnvironment::factory()->create(); $user = User::factory()->create(); $this->actingAs($user) ->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id]) ->get("/admin/tenants/{$tenant->external_id}") ->assertNotFound(); }); it('exposes access-scope management under workspace scope', function (): void { [$user, $tenant] = createMinimalUserWithTenant(role: 'owner'); $this->actingAs($user) ->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id]) ->get(ManagedEnvironmentLinks::accessScopesUrl($tenant)) ->assertOk(); }); it('keeps retired tenant panel operational routes unavailable even for entitled workspace members', function (): void { $workspace = Workspace::factory()->create(); $tenant = ManagedEnvironment::factory()->create([ 'workspace_id' => (int) $workspace->getKey(), 'external_id' => '11111111-1111-1111-1111-111111111111', 'managed_environment_id' => '11111111-1111-1111-1111-111111111111', ]); [$entitledUser] = createMinimalUserWithTenant(tenant: $tenant, role: 'readonly'); $nonEntitledUser = User::factory()->create(); WorkspaceMembership::factory()->create([ 'workspace_id' => (int) $workspace->getKey(), 'user_id' => (int) $nonEntitledUser->getKey(), 'role' => 'owner', ]); $this->actingAs($entitledUser) ->withSession([WorkspaceContext::SESSION_KEY => (int) $workspace->getKey()]) ->get("/admin/t/{$tenant->external_id}") ->assertNotFound(); $this->actingAs($entitledUser) ->withSession([WorkspaceContext::SESSION_KEY => (int) $workspace->getKey()]) ->get("/admin/t/{$tenant->external_id}/diagnostics") ->assertNotFound(); $this->actingAs($nonEntitledUser) ->withSession([WorkspaceContext::SESSION_KEY => (int) $workspace->getKey()]) ->get("/admin/t/{$tenant->external_id}") ->assertNotFound(); $this->actingAs($nonEntitledUser) ->withSession([WorkspaceContext::SESSION_KEY => (int) $workspace->getKey()]) ->get("/admin/t/{$tenant->external_id}/diagnostics") ->assertNotFound(); }); it('keeps retired tenant panel route shapes unavailable and rejects duplicated /t prefixes', function (): void { [$user, $tenant] = createMinimalUserWithTenant(role: 'owner'); $this->actingAs($user) ->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id]) ->get("/admin/t/{$tenant->external_id}/diagnostics") ->assertNotFound(); $this->actingAs($user) ->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id]) ->get("/admin/t/t/{$tenant->external_id}/diagnostics") ->assertNotFound(); }); it('removes tenant-scoped management routes', function (): void { [$user, $tenant] = createMinimalUserWithTenant(role: 'owner'); $this->actingAs($user) ->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id]) ->get("/admin/t/{$tenant->external_id}/provider-connections") ->assertNotFound(); $this->actingAs($user) ->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id]) ->get("/admin/t/{$tenant->external_id}/required-permissions") ->assertNotFound(); $this->actingAs($user) ->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id]) ->get("/admin/t/{$tenant->external_id}/memberships") ->assertNotFound(); }); it('serves provider connection management under canonical admin routes only', function (): void { [$user, $tenant] = createMinimalUserWithTenant(role: 'owner'); $connection = ProviderConnection::factory()->create([ 'workspace_id' => (int) $tenant->workspace_id, 'managed_environment_id' => (int) $tenant->getKey(), ]); $this->followingRedirects() ->actingAs($user) ->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id]) ->get('/admin/provider-connections?managed_environment_id='.$tenant->external_id) ->assertOk(); $this->followingRedirects() ->actingAs($user) ->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id]) ->get('/admin/provider-connections/'.$connection->getKey().'/edit?managed_environment_id='.$tenant->external_id) ->assertOk(); }); it('returns 403 for workspace members missing mutation capability on canonical provider connection routes', function (): void { [$user, $tenant] = createMinimalUserWithTenant(role: 'readonly', workspaceRole: 'readonly'); $this->followingRedirects() ->actingAs($user) ->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id]) ->get('/admin/provider-connections?managed_environment_id='.$tenant->external_id) ->assertOk(); $this->followingRedirects() ->actingAs($user) ->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id]) ->get('/admin/provider-connections/create?managed_environment_id='.$tenant->external_id) ->assertForbidden(); }); it('writes managed-environment access scope audit entries for scope mutations', function (): void { [$owner, $tenant] = createMinimalUserWithTenant(role: 'owner'); $member = User::factory()->create(); WorkspaceMembership::factory()->create([ 'workspace_id' => (int) $tenant->workspace_id, 'user_id' => (int) $member->getKey(), 'role' => 'readonly', ]); /** @var ManagedEnvironmentMembershipManager $manager */ $manager = app(ManagedEnvironmentMembershipManager::class); $membership = $manager->addMember( tenant: $tenant, actor: $owner, member: $member, role: 'readonly', source: 'manual', ); $manager->removeMember( tenant: $tenant, actor: $owner, membership: $membership, ); $actions = AuditLog::query() ->where('managed_environment_id', (int) $tenant->getKey()) ->whereIn('action', [ AuditActionId::ManagedEnvironmentAccessScopeGrant->value, AuditActionId::ManagedEnvironmentAccessScopeRemove->value, ]) ->pluck('action') ->all(); expect($actions)->toContain(AuditActionId::ManagedEnvironmentAccessScopeGrant->value) ->and($actions)->toContain(AuditActionId::ManagedEnvironmentAccessScopeRemove->value) ->and($actions)->not->toContain(AuditActionId::TenantMembershipRoleChange->value); }); it('keeps the canonical managed-environment index available after panel split', function (): void { [$user, $tenant] = createMinimalUserWithTenant(role: 'owner'); $this->actingAs($user) ->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id]) ->get(ManagedEnvironmentLinks::indexUrl($tenant)) ->assertOk() ->assertSee('Managed environments') ->assertDontSee('/admin/tenants', false) ->assertDontSee('/admin/t/', false); }); it('does not expose tenant-management resources in tenant panel registration or navigation URLs', function (): void { expect(Filament::getPanel('tenant'))->toBeNull(); [$user, $tenant] = createMinimalUserWithTenant(role: 'owner'); $this->actingAs($user) ->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id]) ->get(ManagedEnvironmentLinks::viewUrl($tenant)) ->assertOk() ->assertDontSee("/admin/t/{$tenant->external_id}/provider-connections", false) ->assertDontSee("/admin/t/{$tenant->external_id}/tenants", false); }); it('disables global search on the retired ManagedEnvironmentResource product route owner', function (): void { [$workspaceUser, $tenant] = createMinimalUserWithTenant(role: 'owner'); Filament::setCurrentPanel('admin'); Filament::setTenant(null, true); $this->actingAs($workspaceUser); expect(\App\Filament\Resources\ManagedEnvironmentResource::getGlobalSearchResults((string) $tenant->name))->toHaveCount(0); $nonMember = User::factory()->create(); Filament::setCurrentPanel('admin'); Filament::setTenant(null, true); $this->actingAs($nonMember); expect(\App\Filament\Resources\ManagedEnvironmentResource::getGlobalSearchResults((string) $tenant->name))->toHaveCount(0); });