create(); $user = User::factory()->create(); WorkspaceMembership::factory()->create([ 'workspace_id' => (int) $workspace->getKey(), 'user_id' => (int) $user->getKey(), 'role' => 'operator', ]); session()->put(WorkspaceContext::SESSION_KEY, (int) $workspace->getKey()); $tenant = Tenant::factory()->create([ 'workspace_id' => (int) $workspace->getKey(), 'tenant_id' => '11111111-1111-1111-1111-111111111111', 'status' => Tenant::STATUS_ONBOARDING, ]); $connection = ProviderConnection::factory()->create([ 'workspace_id' => (int) $workspace->getKey(), 'tenant_id' => (int) $tenant->getKey(), 'provider' => 'microsoft', 'entra_tenant_id' => (string) $tenant->tenant_id, 'display_name' => 'Acme connection', 'is_default' => true, ]); ProviderCredential::factory()->create([ 'provider_connection_id' => (int) $connection->getKey(), 'payload' => [ 'client_id' => 'old-client-id', 'client_secret' => 'top-secret-client-secret', ], ]); TenantOnboardingSession::create([ 'workspace_id' => (int) $workspace->getKey(), 'tenant_id' => (int) $tenant->getKey(), 'entra_tenant_id' => (string) $tenant->tenant_id, 'current_step' => 'connection', 'state' => [ 'provider_connection_id' => (int) $connection->getKey(), ], 'started_by_user_id' => (int) $user->getKey(), 'updated_by_user_id' => (int) $user->getKey(), ]); $this->actingAs($user); Livewire::actingAs($user) ->test(ManagedTenantOnboardingWizard::class) ->call('updateSelectedProviderConnectionInline', (int) $connection->getKey(), [ 'display_name' => 'Updated name', 'client_id' => 'new-client-id', ]) ->assertStatus(403); }); it('returns 404 when a non-member attempts inline connection update', function (): void { $workspace = Workspace::factory()->create(); $user = User::factory()->create(); session()->put(WorkspaceContext::SESSION_KEY, (int) $workspace->getKey()); $tenant = Tenant::factory()->create([ 'workspace_id' => (int) $workspace->getKey(), 'tenant_id' => '22222222-2222-2222-2222-222222222222', 'status' => Tenant::STATUS_ONBOARDING, ]); $connection = ProviderConnection::factory()->create([ 'workspace_id' => (int) $workspace->getKey(), 'tenant_id' => (int) $tenant->getKey(), 'provider' => 'microsoft', 'entra_tenant_id' => (string) $tenant->tenant_id, 'display_name' => 'Acme connection', 'is_default' => true, ]); ProviderCredential::factory()->create([ 'provider_connection_id' => (int) $connection->getKey(), 'payload' => [ 'client_id' => 'old-client-id', 'client_secret' => 'top-secret-client-secret', ], ]); TenantOnboardingSession::create([ 'workspace_id' => (int) $workspace->getKey(), 'tenant_id' => (int) $tenant->getKey(), 'entra_tenant_id' => (string) $tenant->tenant_id, 'current_step' => 'connection', 'state' => [ 'provider_connection_id' => (int) $connection->getKey(), ], 'started_by_user_id' => (int) $user->getKey(), 'updated_by_user_id' => (int) $user->getKey(), ]); $this->actingAs($user); Livewire::actingAs($user) ->test(ManagedTenantOnboardingWizard::class) ->assertStatus(404); }); it('updates connection inline, invalidates verification state, and writes audit metadata without secrets', 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()); $tenant = Tenant::factory()->create([ 'workspace_id' => (int) $workspace->getKey(), 'tenant_id' => '33333333-3333-3333-3333-333333333333', 'status' => Tenant::STATUS_ONBOARDING, ]); $connection = ProviderConnection::factory()->create([ 'workspace_id' => (int) $workspace->getKey(), 'tenant_id' => (int) $tenant->getKey(), 'provider' => 'microsoft', 'entra_tenant_id' => (string) $tenant->tenant_id, 'display_name' => 'Acme connection', 'is_default' => true, ]); $secret = 'top-secret-client-secret'; ProviderCredential::factory()->create([ 'provider_connection_id' => (int) $connection->getKey(), 'payload' => [ 'client_id' => 'old-client-id', 'client_secret' => $secret, ], ]); $run = OperationRun::factory()->create([ 'tenant_id' => (int) $tenant->getKey(), 'workspace_id' => (int) $workspace->getKey(), 'type' => 'provider.connection.check', ]); $session = TenantOnboardingSession::create([ 'workspace_id' => (int) $workspace->getKey(), 'tenant_id' => (int) $tenant->getKey(), 'entra_tenant_id' => (string) $tenant->tenant_id, 'current_step' => 'verify', 'state' => [ 'provider_connection_id' => (int) $connection->getKey(), 'verification_operation_run_id' => (int) $run->getKey(), 'bootstrap_operation_runs' => [123, 456], 'bootstrap_operation_types' => ['inventory.sync'], ], 'started_by_user_id' => (int) $user->getKey(), 'updated_by_user_id' => (int) $user->getKey(), ]); $this->actingAs($user); Livewire::actingAs($user) ->test(ManagedTenantOnboardingWizard::class) ->call('updateSelectedProviderConnectionInline', (int) $connection->getKey(), [ 'display_name' => 'Updated name', 'client_id' => 'new-client-id', ]) ->assertSuccessful(); $connection->refresh(); expect($connection->display_name)->toBe('Updated name'); $credential = $connection->credential; expect($credential)->not->toBeNull(); expect($credential?->payload['client_id'] ?? null)->toBe('new-client-id'); expect($credential?->payload['client_secret'] ?? null)->toBe($secret); $session->refresh(); expect($session->state['verification_operation_run_id'] ?? null)->toBeNull(); expect($session->state['bootstrap_operation_runs'] ?? null)->toBeNull(); expect($session->state['bootstrap_operation_types'] ?? null)->toBeNull(); expect($session->state['connection_recently_updated'] ?? null)->toBeTrue(); $audit = AuditLog::query() ->where('tenant_id', (int) $tenant->getKey()) ->where('action', 'provider_connection.updated') ->latest('id') ->first(); expect($audit)->not->toBeNull(); $encodedMetadata = json_encode($audit?->metadata, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); expect($encodedMetadata)->not->toContain($secret); }); it('returns 404 when attempting to inline-edit a connection belonging to a different 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()); $tenant = Tenant::factory()->create([ 'workspace_id' => (int) $workspace->getKey(), 'tenant_id' => '44444444-4444-4444-4444-444444444444', 'status' => Tenant::STATUS_ONBOARDING, ]); $otherTenant = Tenant::factory()->create([ 'workspace_id' => (int) $workspace->getKey(), 'tenant_id' => '55555555-5555-5555-5555-555555555555', 'status' => Tenant::STATUS_ONBOARDING, ]); $connection = ProviderConnection::factory()->create([ 'workspace_id' => (int) $workspace->getKey(), 'tenant_id' => (int) $otherTenant->getKey(), 'provider' => 'microsoft', 'entra_tenant_id' => (string) $otherTenant->tenant_id, 'display_name' => 'Other tenant connection', 'is_default' => true, ]); ProviderCredential::factory()->create([ 'provider_connection_id' => (int) $connection->getKey(), 'payload' => [ 'client_id' => 'old-client-id', 'client_secret' => 'top-secret-client-secret', ], ]); TenantOnboardingSession::create([ 'workspace_id' => (int) $workspace->getKey(), 'tenant_id' => (int) $tenant->getKey(), 'entra_tenant_id' => (string) $tenant->tenant_id, 'current_step' => 'connection', 'state' => [ 'provider_connection_id' => (int) $connection->getKey(), ], 'started_by_user_id' => (int) $user->getKey(), 'updated_by_user_id' => (int) $user->getKey(), ]); $this->actingAs($user); Livewire::actingAs($user) ->test(ManagedTenantOnboardingWizard::class) ->call('updateSelectedProviderConnectionInline', (int) $connection->getKey(), [ 'display_name' => 'Updated name', 'client_id' => 'new-client-id', ]) ->assertStatus(404); });