Implemented the first version of provider readiness resolution guidance. Added the ProviderReadinessResolutionAdapter, provider readiness guidance card, and updated EnvironmentRequiredPermissions, ProviderConnectionResource, and ListProviderConnections/ViewProviderConnection. Added tests and updated the design coverage matrix. Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #424
270 lines
10 KiB
PHP
270 lines
10 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use App\Filament\Pages\EnvironmentDashboard;
|
|
use App\Models\ManagedEnvironment;
|
|
use App\Models\ManagedEnvironmentPermission;
|
|
use App\Models\OperationRun;
|
|
use App\Models\ProviderConnection;
|
|
use App\Models\User;
|
|
use App\Models\Workspace;
|
|
use App\Support\ManagedEnvironmentLinks;
|
|
use App\Support\OperationRunOutcome;
|
|
use App\Support\OperationRunStatus;
|
|
use App\Support\Providers\ProviderVerificationStatus;
|
|
use App\Support\Workspaces\WorkspaceContext;
|
|
|
|
pest()->browser()->timeout(40_000);
|
|
|
|
beforeEach(function (): void {
|
|
config()->set('graph.client_id', 'spec353-platform-client');
|
|
config()->set('graph.client_secret', 'spec353-platform-secret');
|
|
config()->set('graph.managed_environment_id', 'organizations');
|
|
});
|
|
|
|
function spec353BrowserApplicationPermissionKey(): string
|
|
{
|
|
$permission = collect(spec283ConfiguredPermissionRows())
|
|
->first(static fn (mixed $row): bool => is_array($row) && ($row['type'] ?? null) === 'application');
|
|
|
|
expect($permission)->not->toBeNull();
|
|
|
|
return (string) $permission['key'];
|
|
}
|
|
|
|
function spec353BrowserSeedPermissionRows(
|
|
ManagedEnvironment $environment,
|
|
array $missingKeys = [],
|
|
array $errorKeys = [],
|
|
?\DateTimeInterface $lastCheckedAt = null,
|
|
): void {
|
|
foreach (spec283ConfiguredPermissionRows() as $permission) {
|
|
if (! is_array($permission)) {
|
|
continue;
|
|
}
|
|
|
|
$permissionKey = (string) ($permission['key'] ?? '');
|
|
|
|
if ($permissionKey === '') {
|
|
continue;
|
|
}
|
|
|
|
ManagedEnvironmentPermission::query()->updateOrCreate(
|
|
[
|
|
'managed_environment_id' => (int) $environment->getKey(),
|
|
'permission_key' => $permissionKey,
|
|
'workspace_id' => (int) $environment->workspace_id,
|
|
],
|
|
[
|
|
'status' => in_array($permissionKey, $errorKeys, true)
|
|
? 'error'
|
|
: (in_array($permissionKey, $missingKeys, true) ? 'missing' : 'granted'),
|
|
'details' => ['source' => 'spec353-browser-test'],
|
|
'last_checked_at' => $lastCheckedAt ?? now(),
|
|
],
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @return array{
|
|
* user: User,
|
|
* workspace: Workspace,
|
|
* blockedEnvironment: ManagedEnvironment,
|
|
* blockedConnection: ProviderConnection,
|
|
* failedEnvironment: ManagedEnvironment,
|
|
* failedConnection: ProviderConnection,
|
|
* readyEnvironment: ManagedEnvironment,
|
|
* readyConnection: ProviderConnection,
|
|
* }
|
|
*/
|
|
function spec353BrowserFixture(): array
|
|
{
|
|
[$user, $blockedEnvironment] = createUserWithTenant(
|
|
role: 'owner',
|
|
workspaceRole: 'owner',
|
|
ensureDefaultMicrosoftProviderConnection: false,
|
|
);
|
|
|
|
$workspace = $blockedEnvironment->workspace()->firstOrFail();
|
|
|
|
$failedEnvironment = ManagedEnvironment::factory()->create([
|
|
'workspace_id' => (int) $workspace->getKey(),
|
|
'name' => 'Spec353 Verification Failed',
|
|
]);
|
|
$readyEnvironment = ManagedEnvironment::factory()->create([
|
|
'workspace_id' => (int) $workspace->getKey(),
|
|
'name' => 'Spec353 Provider Ready',
|
|
]);
|
|
|
|
$user->tenants()->syncWithoutDetaching([
|
|
(int) $failedEnvironment->getKey() => ['role' => 'owner'],
|
|
(int) $readyEnvironment->getKey() => ['role' => 'owner'],
|
|
]);
|
|
|
|
$blockedConnection = ProviderConnection::factory()->platform()->verifiedHealthy()->create([
|
|
'managed_environment_id' => (int) $blockedEnvironment->getKey(),
|
|
'workspace_id' => (int) $workspace->getKey(),
|
|
'display_name' => 'Spec353 Blocked Connection',
|
|
'is_default' => true,
|
|
]);
|
|
$failedConnection = ProviderConnection::factory()->platform()->consentGranted()->create([
|
|
'managed_environment_id' => (int) $failedEnvironment->getKey(),
|
|
'workspace_id' => (int) $workspace->getKey(),
|
|
'display_name' => 'Spec353 Failed Connection',
|
|
'is_default' => true,
|
|
'verification_status' => ProviderVerificationStatus::Error->value,
|
|
'last_error_message' => 'Spec353 verification failed during health check.',
|
|
]);
|
|
$readyConnection = ProviderConnection::factory()->platform()->verifiedHealthy()->create([
|
|
'managed_environment_id' => (int) $readyEnvironment->getKey(),
|
|
'workspace_id' => (int) $workspace->getKey(),
|
|
'display_name' => 'Spec353 Ready Connection',
|
|
'is_default' => true,
|
|
'last_health_check_at' => now(),
|
|
]);
|
|
|
|
$missingPermissionKey = spec353BrowserApplicationPermissionKey();
|
|
spec353BrowserSeedPermissionRows($blockedEnvironment, missingKeys: [$missingPermissionKey]);
|
|
spec353BrowserSeedPermissionRows($failedEnvironment);
|
|
spec353BrowserSeedPermissionRows($readyEnvironment);
|
|
|
|
OperationRun::factory()->create([
|
|
'workspace_id' => (int) $workspace->getKey(),
|
|
'managed_environment_id' => (int) $failedEnvironment->getKey(),
|
|
'type' => 'provider.connection.check',
|
|
'status' => OperationRunStatus::Completed->value,
|
|
'outcome' => OperationRunOutcome::Failed->value,
|
|
'context' => [
|
|
'provider_connection_id' => (int) $failedConnection->getKey(),
|
|
],
|
|
]);
|
|
|
|
return [
|
|
'user' => $user,
|
|
'workspace' => $workspace,
|
|
'blockedEnvironment' => $blockedEnvironment,
|
|
'blockedConnection' => $blockedConnection,
|
|
'failedEnvironment' => $failedEnvironment,
|
|
'failedConnection' => $failedConnection,
|
|
'readyEnvironment' => $readyEnvironment,
|
|
'readyConnection' => $readyConnection,
|
|
];
|
|
}
|
|
|
|
function spec353BrowserActAs(User $user, Workspace $workspace, ManagedEnvironment $environment): void
|
|
{
|
|
test()->actingAs($user)->withSession([
|
|
WorkspaceContext::SESSION_KEY => (int) $workspace->getKey(),
|
|
WorkspaceContext::LAST_ENVIRONMENT_IDS_SESSION_KEY => [
|
|
(string) $workspace->getKey() => (int) $environment->getKey(),
|
|
],
|
|
]);
|
|
}
|
|
|
|
function spec353BrowserScreenshot(string $name): string
|
|
{
|
|
return 'spec353-'.$name;
|
|
}
|
|
|
|
function spec353CopyBrowserScreenshot(string $name): void
|
|
{
|
|
$filename = spec353BrowserScreenshot($name).'.png';
|
|
$source = \Pest\Browser\Support\Screenshot::path($filename);
|
|
$targetDirectory = repo_path('specs/353-provider-connections-resolution-guidance-v1/artifacts/screenshots');
|
|
|
|
if (! is_dir($targetDirectory)) {
|
|
@mkdir($targetDirectory, 0755, true);
|
|
}
|
|
|
|
if (! is_dir($targetDirectory) || ! is_writable($targetDirectory)) {
|
|
return;
|
|
}
|
|
|
|
if (is_file($source)) {
|
|
@copy($source, $targetDirectory.DIRECTORY_SEPARATOR.$filename);
|
|
}
|
|
}
|
|
|
|
function spec353BrowserTextContainsScript(string $selector, string $text): string
|
|
{
|
|
$encodedSelector = json_encode($selector);
|
|
$encodedText = json_encode($text);
|
|
|
|
return <<<JS
|
|
(() => {
|
|
const element = document.querySelector($encodedSelector);
|
|
|
|
if (! element) {
|
|
return false;
|
|
}
|
|
|
|
return element.textContent.replace(/\\s+/g, ' ').trim().includes($encodedText);
|
|
})()
|
|
JS;
|
|
}
|
|
|
|
it('smokes provider readiness guidance across dashboard, required permissions, provider connections, and screenshots', function (): void {
|
|
$fixture = spec353BrowserFixture();
|
|
|
|
spec353BrowserActAs($fixture['user'], $fixture['workspace'], $fixture['blockedEnvironment']);
|
|
|
|
$blockedPermissionsPage = visit(ManagedEnvironmentLinks::requiredPermissionsUrl($fixture['blockedEnvironment']))
|
|
->resize(1440, 1100)
|
|
->waitForText(__('localization.provider_guidance.required_permissions_missing_title'))
|
|
->assertScript(
|
|
spec353BrowserTextContainsScript(
|
|
'[data-testid="provider-readiness-reason"]',
|
|
__('localization.provider_guidance.required_application_permissions_reason'),
|
|
),
|
|
true,
|
|
)
|
|
->assertSee(__('localization.provider_guidance.action_open_admin_consent'))
|
|
->assertScript('document.querySelector("[data-testid=\"provider-readiness-details\"]")?.open === false', true)
|
|
->assertNoJavaScriptErrors()
|
|
->assertNoConsoleLogs()
|
|
->screenshot(true, spec353BrowserScreenshot('01-required-permissions-blocked'));
|
|
|
|
spec353CopyBrowserScreenshot('01-required-permissions-blocked');
|
|
|
|
$blockedPermissionsPage->resize(900, 1100)
|
|
->assertNoJavaScriptErrors()
|
|
->assertNoConsoleLogs();
|
|
|
|
visit(EnvironmentDashboard::getUrl(panel: 'admin', tenant: $fixture['blockedEnvironment']))
|
|
->waitForText('Provider readiness blocks evidence refresh')
|
|
->click('[data-testid="tenant-dashboard-primary-next-action"] a')
|
|
->waitForText(__('localization.provider_guidance.required_permissions_missing_title'))
|
|
->assertScript(
|
|
spec353BrowserTextContainsScript(
|
|
'[data-testid="provider-readiness-reason"]',
|
|
__('localization.provider_guidance.required_application_permissions_reason'),
|
|
),
|
|
true,
|
|
)
|
|
->assertNoJavaScriptErrors()
|
|
->assertNoConsoleLogs();
|
|
|
|
visit(ManagedEnvironmentLinks::providerConnectionUrl($fixture['failedConnection'], 'view', $fixture['failedEnvironment']))
|
|
->resize(1440, 1100)
|
|
->waitForText(__('localization.provider_guidance.verification_failed_title'))
|
|
->assertSee(__('localization.provider_guidance.action_open_verification_operation'))
|
|
->assertNoJavaScriptErrors()
|
|
->assertNoConsoleLogs()
|
|
->screenshot(true, spec353BrowserScreenshot('02-provider-connection-verification-failed'));
|
|
|
|
spec353CopyBrowserScreenshot('02-provider-connection-verification-failed');
|
|
|
|
visit(ManagedEnvironmentLinks::providerConnectionUrl($fixture['readyConnection'], 'view', $fixture['readyEnvironment']))
|
|
->resize(1440, 1100)
|
|
->waitForText(__('localization.provider_guidance.ready_title'))
|
|
->assertSee(__('localization.provider_guidance.ready_reason'))
|
|
->assertSee(__('localization.provider_guidance.action_open_environment_dashboard'))
|
|
->assertNoJavaScriptErrors()
|
|
->assertNoConsoleLogs()
|
|
->screenshot(true, spec353BrowserScreenshot('03-provider-connection-ready'));
|
|
|
|
spec353CopyBrowserScreenshot('03-provider-connection-ready');
|
|
});
|