TenantAtlas/tests/Feature/Operations/TenantlessOperationRunViewerTest.php
2026-02-11 01:04:09 +01:00

145 lines
4.6 KiB
PHP

<?php
declare(strict_types=1);
use App\Models\OperationRun;
use App\Models\Tenant;
use App\Models\User;
use App\Models\Workspace;
use App\Models\WorkspaceMembership;
use App\Support\OperationRunOutcome;
use App\Support\OperationRunStatus;
use App\Support\TenantRole;
use App\Support\Workspaces\WorkspaceContext;
use Illuminate\Support\Facades\Http;
beforeEach(function (): void {
Http::preventStrayRequests();
});
it('allows viewing an operation run without a selected workspace when the user is a member of the run workspace', 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()->forget(WorkspaceContext::SESSION_KEY);
$run = OperationRun::factory()->create([
'workspace_id' => (int) $workspace->getKey(),
'tenant_id' => null,
'type' => 'provider.connection.check',
'status' => OperationRunStatus::Queued->value,
'outcome' => OperationRunOutcome::Pending->value,
]);
$this->actingAs($user)
->get("/admin/operations/{$run->getKey()}")
->assertSuccessful();
expect(session()->get(WorkspaceContext::SESSION_KEY))->toBeNull();
});
it('returns 404 for non-members when viewing an operation run without a selected workspace', function (): void {
$workspace = Workspace::factory()->create();
$user = User::factory()->create();
session()->forget(WorkspaceContext::SESSION_KEY);
$run = OperationRun::factory()->create([
'workspace_id' => (int) $workspace->getKey(),
'tenant_id' => null,
'type' => 'provider.connection.check',
'status' => OperationRunStatus::Queued->value,
'outcome' => OperationRunOutcome::Pending->value,
]);
$this->actingAs($user)
->get("/admin/operations/{$run->getKey()}")
->assertNotFound();
});
it('returns 403 for members missing the required capability for the operation type', function (): void {
$workspace = Workspace::factory()->create();
$tenant = Tenant::factory()->create([
'workspace_id' => (int) $workspace->getKey(),
]);
$user = User::factory()->create();
WorkspaceMembership::factory()->create([
'workspace_id' => (int) $workspace->getKey(),
'user_id' => (int) $user->getKey(),
'role' => 'owner',
]);
$tenant->users()->attach((int) $user->getKey(), [
'role' => TenantRole::Readonly->value,
'source' => 'manual',
'source_ref' => null,
'created_by_user_id' => null,
]);
session()->forget(WorkspaceContext::SESSION_KEY);
$run = OperationRun::factory()->create([
'tenant_id' => (int) $tenant->getKey(),
'workspace_id' => (int) $workspace->getKey(),
'type' => 'inventory.sync',
'status' => OperationRunStatus::Queued->value,
'outcome' => OperationRunOutcome::Pending->value,
]);
$this->actingAs($user)
->get("/admin/operations/{$run->getKey()}")
->assertForbidden();
});
it('renders stored target scope and failure details for a completed run', 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()->forget(WorkspaceContext::SESSION_KEY);
$entraTenantId = '11111111-1111-1111-1111-111111111111';
$failureMessage = 'Missing required Graph permissions.';
$run = OperationRun::factory()->create([
'workspace_id' => (int) $workspace->getKey(),
'tenant_id' => null,
'type' => 'provider.connection.check',
'status' => OperationRunStatus::Completed->value,
'outcome' => OperationRunOutcome::Failed->value,
'context' => [
'target_scope' => [
'entra_tenant_id' => $entraTenantId,
'entra_tenant_name' => 'Contoso',
],
],
'failure_summary' => [
[
'code' => 'provider.connection.check.failed',
'reason_code' => 'permission_denied',
'message' => $failureMessage,
],
],
]);
$this->actingAs($user)
->get("/admin/operations/{$run->getKey()}")
->assertSuccessful()
->assertSee($entraTenantId)
->assertSee('permission_denied')
->assertSee($failureMessage);
});