## Summary - restore broad full-suite green-signal coverage across platform governance, operations, onboarding, dashboard/productization, and customer review flows - align related platform tests and supporting behavior with the current expected state for this restoration pass - update the spec-candidates queue as part of the same suite-restoration sweep ## Validation - `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Browser/Dashboard/TenantDashboardProductizationSmokeTest.php tests/Browser/Reviews/CustomerReviewWorkspaceSmokeTest.php tests/Browser/Spec194GovernanceFrictionSmokeTest.php tests/Browser/Spec265DecisionRegisterSmokeTest.php` Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #351
212 lines
8.2 KiB
PHP
212 lines
8.2 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use App\Filament\Pages\Governance\DecisionRegister;
|
|
use App\Models\Finding;
|
|
use App\Models\FindingException;
|
|
use App\Models\FindingExceptionDecision;
|
|
use App\Models\ManagedEnvironment;
|
|
use App\Models\User;
|
|
use App\Models\Workspace;
|
|
use App\Models\WorkspaceMembership;
|
|
use App\Support\Workspaces\WorkspaceContext;
|
|
use Filament\Facades\Filament;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
|
|
uses(RefreshDatabase::class);
|
|
|
|
afterEach(function (): void {
|
|
Filament::setCurrentPanel(null);
|
|
});
|
|
|
|
it('redirects decision register visits without workspace context into the existing workspace chooser flow', function (): void {
|
|
$user = User::factory()->create();
|
|
|
|
$workspaceA = Workspace::factory()->create();
|
|
$workspaceB = Workspace::factory()->create();
|
|
|
|
WorkspaceMembership::factory()->create([
|
|
'workspace_id' => (int) $workspaceA->getKey(),
|
|
'user_id' => (int) $user->getKey(),
|
|
'role' => 'owner',
|
|
]);
|
|
|
|
WorkspaceMembership::factory()->create([
|
|
'workspace_id' => (int) $workspaceB->getKey(),
|
|
'user_id' => (int) $user->getKey(),
|
|
'role' => 'owner',
|
|
]);
|
|
|
|
$this->actingAs($user)
|
|
->get(DecisionRegister::getUrl(panel: 'admin'))
|
|
->assertRedirect('/admin/choose-workspace');
|
|
});
|
|
|
|
it('returns 404 for users outside the active workspace on the decision register route', function (): void {
|
|
$user = User::factory()->create();
|
|
$workspace = Workspace::factory()->create();
|
|
|
|
WorkspaceMembership::factory()->create([
|
|
'workspace_id' => (int) Workspace::factory()->create()->getKey(),
|
|
'user_id' => (int) $user->getKey(),
|
|
'role' => 'owner',
|
|
]);
|
|
|
|
$this->actingAs($user)
|
|
->withSession([WorkspaceContext::SESSION_KEY => (int) $workspace->getKey()])
|
|
->get(DecisionRegister::getUrl(panel: 'admin'))
|
|
->assertNotFound();
|
|
});
|
|
|
|
it('returns 403 for workspace members with no visible decisions in the default unfiltered register', function (): void {
|
|
$tenant = ManagedEnvironment::factory()->create(['status' => 'active']);
|
|
[$user, $tenant] = createUserWithTenant($tenant, role: 'readonly', workspaceRole: 'readonly');
|
|
|
|
$this->actingAs($user)
|
|
->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id])
|
|
->get(DecisionRegister::getUrl(panel: 'admin'))
|
|
->assertForbidden();
|
|
});
|
|
|
|
it('hides the decision register page when the default workspace register would resolve to 403', function (): void {
|
|
$tenant = ManagedEnvironment::factory()->create(['status' => 'active']);
|
|
[$user, $tenant] = createUserWithTenant($tenant, role: 'readonly', workspaceRole: 'readonly');
|
|
|
|
$response = $this->actingAs($user)
|
|
->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id])
|
|
->get(route('admin.workspace.home', ['workspace' => $tenant->workspace]))
|
|
->assertOk();
|
|
|
|
$response->assertDontSee(DecisionRegister::getUrl(panel: 'admin'));
|
|
|
|
expect(DecisionRegister::canAccess())->toBeFalse();
|
|
});
|
|
|
|
it('returns 404 for explicit tenant filters outside the actor scope', function (): void {
|
|
$visibleTenant = ManagedEnvironment::factory()->create(['status' => 'active']);
|
|
[$user, $visibleTenant] = createUserWithTenant($visibleTenant, role: 'readonly', workspaceRole: 'readonly');
|
|
|
|
$hiddenTenant = ManagedEnvironment::factory()->create([
|
|
'status' => 'active',
|
|
'workspace_id' => (int) $visibleTenant->workspace_id,
|
|
]);
|
|
|
|
$this->actingAs($user)
|
|
->withSession([WorkspaceContext::SESSION_KEY => (int) $visibleTenant->workspace_id])
|
|
->get(DecisionRegister::getUrl(panel: 'admin').'?managed_environment_id='.(string) $hiddenTenant->getKey())
|
|
->assertNotFound();
|
|
});
|
|
|
|
it('allows readonly tenant members to open the decision register when visible decisions exist', function (): void {
|
|
$tenant = ManagedEnvironment::factory()->create(['status' => 'active']);
|
|
[$user, $tenant] = createUserWithTenant($tenant, role: 'readonly', workspaceRole: 'readonly');
|
|
|
|
decisionRegisterAuthException(
|
|
tenant: $tenant,
|
|
actor: $user,
|
|
status: FindingException::STATUS_PENDING,
|
|
validityState: FindingException::VALIDITY_MISSING_SUPPORT,
|
|
decisionType: FindingExceptionDecision::TYPE_REQUESTED,
|
|
decisionReason: 'Visible approval request',
|
|
);
|
|
|
|
$this->actingAs($user)
|
|
->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id])
|
|
->get(DecisionRegister::getUrl(panel: 'admin'))
|
|
->assertOk()
|
|
->assertSee('Decision register');
|
|
});
|
|
|
|
it('registers the decision register page once visible open decisions exist', function (): void {
|
|
$tenant = ManagedEnvironment::factory()->create(['status' => 'active']);
|
|
[$user, $tenant] = createUserWithTenant($tenant, role: 'readonly', workspaceRole: 'readonly');
|
|
|
|
decisionRegisterAuthException(
|
|
tenant: $tenant,
|
|
actor: $user,
|
|
status: FindingException::STATUS_PENDING,
|
|
validityState: FindingException::VALIDITY_MISSING_SUPPORT,
|
|
decisionType: FindingExceptionDecision::TYPE_REQUESTED,
|
|
decisionReason: 'Visible approval request',
|
|
);
|
|
|
|
$response = $this->actingAs($user)
|
|
->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id])
|
|
->get(route('admin.workspace.home', ['workspace' => $tenant->workspace]))
|
|
->assertOk();
|
|
|
|
$response->assertSee(DecisionRegister::getUrl(panel: 'admin'));
|
|
|
|
expect(DecisionRegister::canAccess())->toBeTrue();
|
|
});
|
|
|
|
it('registers the decision register page and redirects the default route when only recently closed decisions are visible', function (): void {
|
|
$tenant = ManagedEnvironment::factory()->create(['status' => 'active']);
|
|
[$user, $tenant] = createUserWithTenant($tenant, role: 'readonly', workspaceRole: 'readonly');
|
|
|
|
decisionRegisterAuthException(
|
|
tenant: $tenant,
|
|
actor: $user,
|
|
status: FindingException::STATUS_REJECTED,
|
|
validityState: FindingException::VALIDITY_REJECTED,
|
|
decisionType: FindingExceptionDecision::TYPE_REJECTED,
|
|
decisionReason: 'Recently rejected closure reason',
|
|
);
|
|
|
|
$response = $this->actingAs($user)
|
|
->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id])
|
|
->get(route('admin.workspace.home', ['workspace' => $tenant->workspace]))
|
|
->assertOk();
|
|
|
|
$response->assertSee(DecisionRegister::getUrl(panel: 'admin'));
|
|
|
|
expect(DecisionRegister::canAccess())->toBeTrue();
|
|
|
|
$this->actingAs($user)
|
|
->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id])
|
|
->get(DecisionRegister::getUrl(panel: 'admin'))
|
|
->assertRedirect(DecisionRegister::getUrl(panel: 'admin', parameters: ['register_state' => 'recently_closed']));
|
|
});
|
|
|
|
function decisionRegisterAuthException(
|
|
ManagedEnvironment $tenant,
|
|
User $actor,
|
|
string $status,
|
|
string $validityState,
|
|
string $decisionType,
|
|
string $decisionReason,
|
|
): FindingException {
|
|
$finding = Finding::factory()->for($tenant)->create([
|
|
'workspace_id' => (int) $tenant->workspace_id,
|
|
]);
|
|
|
|
$exception = FindingException::query()->create([
|
|
'workspace_id' => (int) $tenant->workspace_id,
|
|
'managed_environment_id' => (int) $tenant->getKey(),
|
|
'finding_id' => (int) $finding->getKey(),
|
|
'requested_by_user_id' => (int) $actor->getKey(),
|
|
'owner_user_id' => (int) $actor->getKey(),
|
|
'status' => $status,
|
|
'current_validity_state' => $validityState,
|
|
'request_reason' => 'Decision register authorization test',
|
|
'requested_at' => now()->subDay(),
|
|
'review_due_at' => now()->addDay(),
|
|
'evidence_summary' => ['reference_count' => 0],
|
|
]);
|
|
|
|
$decision = $exception->decisions()->create([
|
|
'workspace_id' => (int) $tenant->workspace_id,
|
|
'managed_environment_id' => (int) $tenant->getKey(),
|
|
'actor_user_id' => (int) $actor->getKey(),
|
|
'decision_type' => $decisionType,
|
|
'reason' => $decisionReason,
|
|
'metadata' => [],
|
|
'decided_at' => now()->subDay(),
|
|
]);
|
|
|
|
$exception->forceFill(['current_decision_id' => (int) $decision->getKey()])->save();
|
|
|
|
return $exception->fresh(['currentDecision']);
|
|
}
|