TenantAtlas/apps/platform/tests/Browser/Spec384BaselineSubjectResolutionSmokeTest.php
ahmido 39298f27f2 feat(ui): implement baseline subject resolution ui (#455)
Added `BaselineSubjectResolution` page and supporting logic to visualize missing identities, ambiguous matches, and skipped coverages as defined in Spec 384. Replaces legacy compare warnings with an actionable, deterministic UI surface.

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #455
2026-06-16 23:36:38 +00:00

165 lines
6.1 KiB
PHP

<?php
declare(strict_types=1);
use App\Models\ManagedEnvironment;
use App\Models\User;
use App\Support\Baselines\SubjectClass;
use App\Support\ManagedEnvironmentLinks;
use App\Support\OperationRunOutcome;
use App\Support\OperationRunStatus;
use App\Support\Resources\ResourceIdentity;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\Feature\Baselines\Support\BaselineSubjectResolutionFixtures;
pest()->browser()->timeout(45_000);
uses(RefreshDatabase::class);
it('Spec384 smokes baseline subject resolution reachability and action modal', function (): void {
[$user, $tenant] = createUserWithTenant(role: 'owner', workspaceRole: 'manager');
$tenant->forceFill(['name' => 'Spec384 Subject Resolution Environment'])->save();
$run = spec384BrowserSubjectResolutionRun($tenant);
$resolutionPath = spec384BrowserPath(ManagedEnvironmentLinks::baselineSubjectResolutionUrl($tenant, [
'operation_run_id' => (int) $run->getKey(),
]));
$page = visit(spec384BrowserLoginUrl($user, $tenant, $resolutionPath))
->resize(1440, 1100)
->waitForText('Baseline subject resolution')
->assertSee('Spec384 Subject Resolution Environment')
->assertSee('1 decision required')
->assertSee('Decision worklist')
->assertSee('Duplicate policy')
->assertSee('Unresolved Duplicate Candidates')
->assertSee('Binding required')
->assertSee('TenantPilot-only')
->assertSee('Open operation')
->assertSee('Open baseline compare')
->assertSee('Bind subject')
->assertNoJavaScriptErrors()
->assertNoConsoleLogs()
->screenshot(true, spec384BrowserScreenshotName('01-baseline-subject-resolution'));
spec384BrowserCopyScreenshot('01-baseline-subject-resolution');
$page
->click('Bind subject')
->waitForText('Record a TenantPilot-only subject binding')
->assertSee('Provider resource')
->assertSee('Operator note')
->assertSee('This does not mutate the provider tenant')
->assertNoJavaScriptErrors()
->assertNoConsoleLogs()
->screenshot(true, spec384BrowserScreenshotName('02-baseline-subject-resolution-bind-modal'));
spec384BrowserCopyScreenshot('02-baseline-subject-resolution-bind-modal');
$page
->resize(430, 900)
->assertScript('document.documentElement.scrollWidth <= window.innerWidth', true)
->assertNoJavaScriptErrors()
->screenshot(true, spec384BrowserScreenshotName('03-baseline-subject-resolution-mobile'));
spec384BrowserCopyScreenshot('03-baseline-subject-resolution-mobile');
});
function spec384BrowserSubjectResolutionRun(ManagedEnvironment $tenant)
{
[$profile, $snapshot] = seedActiveBaselineForTenant($tenant);
$leftIdentity = ResourceIdentity::providerResource('fake-provider', 'policy', 'candidate-left');
$rightIdentity = ResourceIdentity::providerResource('fake-provider', 'policy', 'candidate-right');
$leftDescriptor = BaselineSubjectResolutionFixtures::providerDescriptor($leftIdentity, 'Duplicate policy');
$rightDescriptor = BaselineSubjectResolutionFixtures::providerDescriptor($rightIdentity, 'Duplicate policy');
BaselineSubjectResolutionFixtures::inventoryCandidate(
$tenant,
$leftIdentity,
'Duplicate policy',
);
BaselineSubjectResolutionFixtures::inventoryCandidate(
$tenant,
$rightIdentity,
'Duplicate policy',
);
return seedBaselineCompareRun(
tenant: $tenant,
profile: $profile,
snapshot: $snapshot,
compareContext: [
'result_semantics' => [
'version' => 1,
'subject_outcomes' => [
BaselineSubjectResolutionFixtures::semanticOutcome([
'reason' => 'unresolved_duplicate_candidates',
'actionability' => 'binding_required',
'readiness_impact' => 'customer_blocker',
'subject' => [
'subject_domain' => 'baseline',
'subject_class' => SubjectClass::PolicyBacked->value,
'subject_type_key' => 'deviceConfiguration',
'subject_key' => 'legacy-display-key',
'display_label' => 'Duplicate policy',
'candidate_descriptors' => [
$leftDescriptor->toArray(),
$rightDescriptor->toArray(),
],
],
]),
],
],
],
status: OperationRunStatus::Completed->value,
outcome: OperationRunOutcome::PartiallySucceeded->value,
);
}
function spec384BrowserLoginUrl(User $user, ManagedEnvironment $tenant, string $redirect): string
{
return route('admin.local.smoke-login', [
'email' => $user->email,
'tenant' => $tenant->external_id,
'workspace' => $tenant->workspace->slug,
'redirect' => $redirect,
]);
}
function spec384BrowserPath(string $url): string
{
$path = parse_url($url, PHP_URL_PATH) ?: '/admin';
$query = parse_url($url, PHP_URL_QUERY);
return is_string($query) && $query !== '' ? $path.'?'.$query : $path;
}
function spec384BrowserScreenshotName(string $name): string
{
return 'spec384-'.$name;
}
function spec384BrowserCopyScreenshot(string $name): void
{
$filename = spec384BrowserScreenshotName($name).'.png';
$source = base_path('tests/Browser/Screenshots/'.$filename);
$targetDirectory = repo_path('specs/384-baseline-subject-resolution-ui/artifacts/screenshots');
if (! is_dir($targetDirectory)) {
@mkdir($targetDirectory, 0755, true);
}
if (! is_file($source)) {
$source = \Pest\Browser\Support\Screenshot::path($filename);
}
for ($attempt = 0; $attempt < 10 && ! is_file($source); $attempt++) {
usleep(100_000);
clearstatcache(true, $source);
}
if (is_file($source) && is_dir($targetDirectory) && is_writable($targetDirectory)) {
@copy($source, $targetDirectory.DIRECTORY_SEPARATOR.$filename);
}
}