## Summary - add the RBAC role definition diff UX upgrade as the first concrete consumer of the shared diff presentation foundation - refine managed tenant onboarding draft routing, CTA labeling, and cancellation redirect behavior - tighten related Filament and diff rendering regression coverage ## Testing - updated focused Pest coverage for onboarding draft routing and lifecycle behavior - updated focused Pest coverage for shared diff partials and RBAC finding rendering ## Notes - Livewire v4.0+ compliance is preserved within the existing Filament v5 surfaces - provider registration remains unchanged in bootstrap/providers.php - no new Filament assets were added; existing deployment practice still relies on php artisan filament:assets when assets change Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #171
199 lines
6.6 KiB
PHP
199 lines
6.6 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use App\Filament\Pages\Workspaces\ManagedTenantOnboardingWizard;
|
|
use App\Models\Tenant;
|
|
use App\Models\TenantOnboardingSession;
|
|
use App\Models\User;
|
|
use App\Models\Workspace;
|
|
use App\Models\WorkspaceMembership;
|
|
use App\Support\Workspaces\WorkspaceContext;
|
|
use Livewire\Livewire;
|
|
|
|
it('shows the onboarding start state when no resumable drafts exist', function (): void {
|
|
$workspace = Workspace::factory()->create();
|
|
$user = User::factory()->create();
|
|
|
|
WorkspaceMembership::factory()->create([
|
|
'workspace_id' => (int) $workspace->getKey(),
|
|
'user_id' => (int) $user->getKey(),
|
|
'role' => 'owner',
|
|
]);
|
|
|
|
session()->put(WorkspaceContext::SESSION_KEY, (int) $workspace->getKey());
|
|
|
|
$this->actingAs($user)
|
|
->get(route('admin.onboarding'))
|
|
->assertSuccessful()
|
|
->assertSee('Create or resume a managed tenant in this workspace.')
|
|
->assertDontSee('Multiple onboarding drafts are available.');
|
|
});
|
|
|
|
it('redirects the landing route to the only resumable draft', function (): void {
|
|
$workspace = Workspace::factory()->create();
|
|
$user = User::factory()->create();
|
|
|
|
WorkspaceMembership::factory()->create([
|
|
'workspace_id' => (int) $workspace->getKey(),
|
|
'user_id' => (int) $user->getKey(),
|
|
'role' => 'owner',
|
|
]);
|
|
|
|
session()->put(WorkspaceContext::SESSION_KEY, (int) $workspace->getKey());
|
|
|
|
$draft = createOnboardingDraft([
|
|
'workspace' => $workspace,
|
|
'started_by' => $user,
|
|
'updated_by' => $user,
|
|
'state' => [
|
|
'entra_tenant_id' => '11111111-1111-1111-1111-111111111111',
|
|
'tenant_name' => 'Contoso',
|
|
'environment' => 'prod',
|
|
],
|
|
]);
|
|
|
|
$this->actingAs($user)
|
|
->get(route('admin.onboarding'))
|
|
->assertRedirect(route('admin.onboarding.draft', ['onboardingDraft' => $draft->getKey()]));
|
|
});
|
|
|
|
it('loads a concrete draft route with confirmed persisted state', function (): void {
|
|
$workspace = Workspace::factory()->create();
|
|
$tenant = Tenant::factory()->create([
|
|
'workspace_id' => (int) $workspace->getKey(),
|
|
'tenant_id' => '22222222-2222-2222-2222-222222222222',
|
|
'name' => 'Contoso GmbH',
|
|
'status' => Tenant::STATUS_ONBOARDING,
|
|
]);
|
|
$user = User::factory()->create();
|
|
|
|
createUserWithTenant(
|
|
tenant: $tenant,
|
|
user: $user,
|
|
role: 'owner',
|
|
workspaceRole: 'owner',
|
|
ensureDefaultMicrosoftProviderConnection: false,
|
|
);
|
|
|
|
$draft = createOnboardingDraft([
|
|
'workspace' => $workspace,
|
|
'tenant' => $tenant,
|
|
'started_by' => $user,
|
|
'updated_by' => $user,
|
|
'current_step' => 'connection',
|
|
'state' => [
|
|
'entra_tenant_id' => (string) $tenant->tenant_id,
|
|
'tenant_name' => (string) $tenant->name,
|
|
'environment' => 'prod',
|
|
'primary_domain' => 'contoso.example',
|
|
'notes' => 'Confirmed draft state',
|
|
],
|
|
]);
|
|
|
|
$this->actingAs($user)
|
|
->get(route('admin.onboarding.draft', ['onboardingDraft' => $draft->getKey()]))
|
|
->assertSuccessful()
|
|
->assertSee('Onboarding draft')
|
|
->assertSee('Contoso GmbH')
|
|
->assertSee('22222222-2222-2222-2222-222222222222')
|
|
->assertSee('Started by')
|
|
->assertSee($user->name);
|
|
});
|
|
|
|
it('hides the all drafts header action when the current draft is the only resumable draft', function (): void {
|
|
$workspace = Workspace::factory()->create();
|
|
$user = User::factory()->create();
|
|
|
|
WorkspaceMembership::factory()->create([
|
|
'workspace_id' => (int) $workspace->getKey(),
|
|
'user_id' => (int) $user->getKey(),
|
|
'role' => 'owner',
|
|
]);
|
|
|
|
session()->put(WorkspaceContext::SESSION_KEY, (int) $workspace->getKey());
|
|
|
|
$draft = createOnboardingDraft([
|
|
'workspace' => $workspace,
|
|
'started_by' => $user,
|
|
'updated_by' => $user,
|
|
'state' => [
|
|
'entra_tenant_id' => '77777777-7777-7777-7777-777777777777',
|
|
'tenant_name' => 'Single Draft Tenant',
|
|
'environment' => 'prod',
|
|
],
|
|
]);
|
|
|
|
$this->actingAs($user)
|
|
->get(route('admin.onboarding.draft', ['onboardingDraft' => $draft->getKey()]))
|
|
->assertSuccessful()
|
|
->assertDontSee('All onboarding drafts');
|
|
});
|
|
|
|
it('shows the all drafts header action when multiple resumable drafts exist', function (): void {
|
|
$workspace = Workspace::factory()->create();
|
|
$user = User::factory()->create();
|
|
|
|
WorkspaceMembership::factory()->create([
|
|
'workspace_id' => (int) $workspace->getKey(),
|
|
'user_id' => (int) $user->getKey(),
|
|
'role' => 'owner',
|
|
]);
|
|
|
|
session()->put(WorkspaceContext::SESSION_KEY, (int) $workspace->getKey());
|
|
|
|
createOnboardingDraft([
|
|
'workspace' => $workspace,
|
|
'started_by' => $user,
|
|
'updated_by' => $user,
|
|
'state' => [
|
|
'entra_tenant_id' => '88888888-8888-8888-8888-888888888888',
|
|
'tenant_name' => 'First Draft Tenant',
|
|
'environment' => 'prod',
|
|
],
|
|
]);
|
|
|
|
$draft = createOnboardingDraft([
|
|
'workspace' => $workspace,
|
|
'started_by' => $user,
|
|
'updated_by' => $user,
|
|
'state' => [
|
|
'entra_tenant_id' => '99999999-9999-9999-9999-999999999999',
|
|
'tenant_name' => 'Second Draft Tenant',
|
|
'environment' => 'staging',
|
|
],
|
|
]);
|
|
|
|
$this->actingAs($user)
|
|
->get(route('admin.onboarding.draft', ['onboardingDraft' => $draft->getKey()]))
|
|
->assertSuccessful()
|
|
->assertSee('All onboarding drafts');
|
|
});
|
|
|
|
it('redirects to the canonical draft route immediately after step one identifies a tenant', function (): void {
|
|
$workspace = Workspace::factory()->create();
|
|
$user = User::factory()->create();
|
|
|
|
WorkspaceMembership::factory()->create([
|
|
'workspace_id' => (int) $workspace->getKey(),
|
|
'user_id' => (int) $user->getKey(),
|
|
'role' => 'owner',
|
|
]);
|
|
|
|
session()->put(WorkspaceContext::SESSION_KEY, (int) $workspace->getKey());
|
|
|
|
Livewire::actingAs($user)
|
|
->test(ManagedTenantOnboardingWizard::class)
|
|
->call('identifyManagedTenant', [
|
|
'entra_tenant_id' => '33333333-3333-3333-3333-333333333333',
|
|
'environment' => 'prod',
|
|
'name' => 'Canonical Draft Tenant',
|
|
])
|
|
->assertRedirect(route('admin.onboarding.draft', [
|
|
'onboardingDraft' => TenantOnboardingSession::query()
|
|
->where('workspace_id', (int) $workspace->getKey())
|
|
->where('entra_tenant_id', '33333333-3333-3333-3333-333333333333')
|
|
->value('id'),
|
|
]));
|
|
});
|