TenantAtlas/apps/platform/tests/Browser/Support/Spec322WorkspaceEnvironmentBrowserHarness.php
Ahmed Darrazi d5086ff35a
Some checks failed
PR Fast Feedback / fast-feedback (pull_request) Failing after 52s
test: add Spec 322 browser no-drift regression guards
2026-05-17 12:57:29 +02:00

308 lines
12 KiB
PHP

<?php
declare(strict_types=1);
namespace Tests\Browser\Support;
use App\Models\AlertDelivery;
use App\Models\AlertDestination;
use App\Models\AlertRule;
use App\Models\AuditLog;
use App\Models\EnvironmentReview;
use App\Models\EvidenceSnapshot;
use App\Models\Finding;
use App\Models\FindingException;
use App\Models\FindingExceptionDecision;
use App\Models\ManagedEnvironment;
use App\Models\OperationRun;
use App\Models\ProviderConnection;
use App\Models\User;
use App\Models\Workspace;
use App\Support\EnvironmentReviewStatus;
use App\Support\Evidence\EvidenceSnapshotStatus;
use App\Support\Workspaces\WorkspaceContext;
final class Spec322WorkspaceEnvironmentBrowserHarness
{
/**
* @return array{
* user: User,
* workspace: Workspace,
* environmentA: ManagedEnvironment,
* environmentB: ManagedEnvironment,
* runA: OperationRun,
* runB: OperationRun,
* connectionA: ProviderConnection,
* connectionB: ProviderConnection,
* exceptionA: FindingException,
* exceptionB: FindingException,
* snapshotA: EvidenceSnapshot,
* snapshotB: EvidenceSnapshot,
* reviewA: EnvironmentReview,
* reviewB: EnvironmentReview,
* deliveryA: AlertDelivery,
* deliveryB: AlertDelivery,
* auditA: AuditLog,
* auditB: AuditLog,
* auditWorkspace: AuditLog
* }
*/
public static function fixture(): array
{
$environmentA = ManagedEnvironment::factory()->active()->create([
'name' => 'Spec322 Browser Environment A',
'external_id' => 'spec322-browser-environment-a',
]);
[$user, $environmentA] = \createUserWithTenant(tenant: $environmentA, role: 'owner', workspaceRole: 'owner');
$environmentB = ManagedEnvironment::factory()->active()->create([
'workspace_id' => (int) $environmentA->workspace_id,
'name' => 'Spec322 Browser Environment B',
'external_id' => 'spec322-browser-environment-b',
]);
\createUserWithTenant(tenant: $environmentB, user: $user, role: 'owner', workspaceRole: 'owner');
$workspace = $environmentA->workspace()->firstOrFail();
$runA = OperationRun::factory()->forTenant($environmentA)->create(['type' => 'policy.sync']);
$runB = OperationRun::factory()->forTenant($environmentB)->create(['type' => 'inventory_sync']);
$exceptionA = self::findingException($environmentA, $user, 'Spec322 Browser Governance A', 'Spec322 Browser Decision A');
$exceptionB = self::findingException($environmentB, $user, 'Spec322 Browser Governance B', 'Spec322 Browser Decision B');
$connectionA = ProviderConnection::factory()->create([
'workspace_id' => (int) $workspace->getKey(),
'managed_environment_id' => (int) $environmentA->getKey(),
'display_name' => 'Spec322 Browser Provider A',
]);
$connectionB = ProviderConnection::factory()->create([
'workspace_id' => (int) $workspace->getKey(),
'managed_environment_id' => (int) $environmentB->getKey(),
'display_name' => 'Spec322 Browser Provider B',
]);
$snapshotA = self::evidenceSnapshot($environmentA);
$snapshotB = self::evidenceSnapshot($environmentB);
$reviewA = self::publishedReview($environmentA, $user, $snapshotA);
$reviewB = self::publishedReview($environmentB, $user, $snapshotB);
$rule = AlertRule::factory()->create(['workspace_id' => (int) $workspace->getKey()]);
$destination = AlertDestination::factory()->create(['workspace_id' => (int) $workspace->getKey()]);
$deliveryA = AlertDelivery::factory()->create([
'workspace_id' => (int) $workspace->getKey(),
'managed_environment_id' => (int) $environmentA->getKey(),
'alert_rule_id' => (int) $rule->getKey(),
'alert_destination_id' => (int) $destination->getKey(),
'status' => AlertDelivery::STATUS_FAILED,
'created_at' => now()->subHour(),
]);
$deliveryB = AlertDelivery::factory()->create([
'workspace_id' => (int) $workspace->getKey(),
'managed_environment_id' => (int) $environmentB->getKey(),
'alert_rule_id' => (int) $rule->getKey(),
'alert_destination_id' => (int) $destination->getKey(),
'status' => AlertDelivery::STATUS_SENT,
'created_at' => now()->subHour(),
]);
$auditA = self::auditRecord($environmentA, 'Spec322 browser audit A');
$auditB = self::auditRecord($environmentB, 'Spec322 browser audit B');
$auditWorkspace = self::auditRecord(null, 'Spec322 browser workspace audit', [
'workspace_id' => (int) $workspace->getKey(),
]);
return compact(
'user',
'workspace',
'environmentA',
'environmentB',
'runA',
'runB',
'connectionA',
'connectionB',
'exceptionA',
'exceptionB',
'snapshotA',
'snapshotB',
'reviewA',
'reviewB',
'deliveryA',
'deliveryB',
'auditA',
'auditB',
'auditWorkspace',
);
}
public static function authenticate(object $testCase, User $user, Workspace $workspace, ?ManagedEnvironment $rememberedEnvironment = null): void
{
$session = [
WorkspaceContext::SESSION_KEY => (int) $workspace->getKey(),
];
if ($rememberedEnvironment instanceof ManagedEnvironment) {
$session[WorkspaceContext::LAST_ENVIRONMENT_IDS_SESSION_KEY] = [
(string) $workspace->getKey() => (int) $rememberedEnvironment->getKey(),
];
}
$testCase->actingAs($user)->withSession($session);
foreach ($session as $key => $value) {
session()->put($key, $value);
}
\setAdminPanelContext($rememberedEnvironment);
}
public static function assertWorkspaceOnly(mixed $page, ?string $wideText = null, ?string $environmentName = null): mixed
{
$page
->waitForText(__('localization.shell.no_environment_selected'))
->assertDontSee('Environment filter:')
->assertNoJavaScriptErrors()
->assertNoConsoleLogs();
if ($wideText !== null) {
$page->assertSee($wideText);
}
if ($environmentName !== null) {
$page->assertDontSee(__('localization.shell.environment_scope').': '.$environmentName);
}
return self::assertNoLegacyQuery($page);
}
public static function assertFilteredWorkspaceHub(mixed $page, ManagedEnvironment $environment, ?string $hiddenText = null): mixed
{
$page
->waitForText('Environment filter:')
->assertSee($environment->name)
->assertDontSee(__('localization.shell.environment_scope').': '.$environment->name)
->assertSee('Clear filter')
->assertScript('window.location.search.includes("environment_id=")', true)
->assertScript('! window.location.search.includes("tenant=")', true)
->assertScript('! window.location.search.includes("tenant_id=")', true)
->assertScript('! window.location.search.includes("managed_environment_id=")', true)
->assertScript('! window.location.search.includes("tenant_scope=")', true)
->assertScript('! window.location.search.includes("tableFilters")', true)
->assertNoJavaScriptErrors()
->assertNoConsoleLogs();
if ($hiddenText !== null) {
$page->assertDontSee($hiddenText);
}
return $page;
}
public static function assertNoLegacyQuery(mixed $page): mixed
{
return $page
->assertScript('! window.location.search.includes("environment_id=")', true)
->assertScript('! window.location.search.includes("tenant=")', true)
->assertScript('! window.location.search.includes("tenant_id=")', true)
->assertScript('! window.location.search.includes("managed_environment_id=")', true)
->assertScript('! window.location.search.includes("tenant_scope=")', true)
->assertScript('! window.location.search.includes("tableFilters")', true);
}
public static function clearWorkspaceHubEnvironmentFilter(mixed $page): mixed
{
$page->assertScript('document.querySelector(\'[data-testid="workspace-hub-environment-filter-clear"]\') instanceof HTMLAnchorElement', true);
$page->script('window.location.assign(document.querySelector(\'[data-testid="workspace-hub-environment-filter-clear"]\').href);');
return $page->waitForText(__('localization.shell.no_environment_selected'));
}
private static function findingException(
ManagedEnvironment $environment,
User $actor,
string $requestReason,
string $decisionReason,
): FindingException {
$finding = Finding::factory()->for($environment)->riskAccepted()->create([
'workspace_id' => (int) $environment->workspace_id,
'subject_external_id' => str()->slug($requestReason),
]);
$exception = FindingException::query()->create([
'workspace_id' => (int) $environment->workspace_id,
'managed_environment_id' => (int) $environment->getKey(),
'finding_id' => (int) $finding->getKey(),
'requested_by_user_id' => (int) $actor->getKey(),
'owner_user_id' => (int) $actor->getKey(),
'status' => FindingException::STATUS_PENDING,
'current_validity_state' => FindingException::VALIDITY_MISSING_SUPPORT,
'request_reason' => $requestReason,
'requested_at' => now()->subDay(),
'review_due_at' => now()->addDay(),
'evidence_summary' => ['reference_count' => 0],
]);
$decision = $exception->decisions()->create([
'workspace_id' => (int) $environment->workspace_id,
'managed_environment_id' => (int) $environment->getKey(),
'actor_user_id' => (int) $actor->getKey(),
'decision_type' => FindingExceptionDecision::TYPE_REQUESTED,
'reason' => $decisionReason,
'metadata' => [],
'decided_at' => now()->subDay(),
]);
$exception->forceFill(['current_decision_id' => (int) $decision->getKey()])->save();
return $exception->fresh(['currentDecision']);
}
private static function evidenceSnapshot(ManagedEnvironment $environment): EvidenceSnapshot
{
return EvidenceSnapshot::query()->create([
'managed_environment_id' => (int) $environment->getKey(),
'workspace_id' => (int) $environment->workspace_id,
'status' => EvidenceSnapshotStatus::Active->value,
'summary' => ['missing_dimensions' => 0, 'stale_dimensions' => 0],
'generated_at' => now(),
]);
}
private static function publishedReview(ManagedEnvironment $environment, User $user, EvidenceSnapshot $snapshot): EnvironmentReview
{
$review = \composeEnvironmentReviewForTest($environment, $user, $snapshot);
$review->forceFill([
'status' => EnvironmentReviewStatus::Published->value,
'published_at' => now(),
'published_by_user_id' => (int) $user->getKey(),
])->save();
return $review->fresh();
}
/**
* @param array<string, mixed> $attributes
*/
private static function auditRecord(?ManagedEnvironment $environment, string $summary, array $attributes = []): AuditLog
{
$workspaceId = array_key_exists('workspace_id', $attributes)
? (int) $attributes['workspace_id']
: (int) ($environment?->workspace_id);
return AuditLog::query()->create(array_merge([
'workspace_id' => $workspaceId,
'managed_environment_id' => $environment?->getKey(),
'actor_email' => 'spec322-browser@example.test',
'actor_name' => 'Spec322 Browser Operator',
'action' => 'operation.completed',
'status' => 'success',
'resource_type' => 'operation_run',
'resource_id' => '322',
'summary' => $summary,
'metadata' => [],
'recorded_at' => now(),
], $attributes));
}
}