TenantAtlas/apps/platform/tests/Browser/Spec202GovernanceSubjectTaxonomySmokeTest.php
ahmido 7541b1eb41 Spec 202: implement governance subject taxonomy and baseline scope V2 (#232)
## Summary
- introduce the governance subject taxonomy registry and canonical Baseline Scope V2 normalization and persistence
- update baseline profile Filament surfaces, validation, capture/compare gating, and add the optional scope backfill command with audit logging
- add focused unit, feature, Filament, and browser smoke coverage for save-forward behavior, operation truth, authorization continuity, and invalid-scope rendering
- remove the duplicate legacy spec plan under `specs/001-governance-subject-taxonomy/plan.md`

## Verification
- `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Browser/Spec202GovernanceSubjectTaxonomySmokeTest.php`
- focused Spec 202 regression pack: `56 passed (300 assertions)`
- `cd apps/platform && ./vendor/bin/sail bin pint --dirty --format agent`

## Notes
- no schema migration required
- no new Filament asset registration required
- branch includes the final browser smoke test coverage for the current feature

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #232
2026-04-13 15:33:33 +00:00

163 lines
6.1 KiB
PHP

<?php
declare(strict_types=1);
use App\Filament\Resources\BaselineProfileResource;
use App\Models\BaselineProfile;
use App\Models\BaselineSnapshot;
use App\Models\BaselineTenantAssignment;
use App\Models\Tenant;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\DB;
pest()->browser()->timeout(20_000);
uses(RefreshDatabase::class);
function spec202SmokeLoginUrl(User $user, Tenant $tenant, string $redirect = ''): string
{
return route('admin.local.smoke-login', array_filter([
'email' => $user->email,
'tenant' => $tenant->external_id,
'workspace' => $tenant->workspace->slug,
'redirect' => $redirect,
], static fn (?string $value): bool => filled($value)));
}
it('smokes governance subject scope create, edit, and view surfaces', function (): void {
[$user, $tenant] = createUserWithTenant(
role: 'owner',
workspaceRole: 'manager',
ensureDefaultMicrosoftProviderConnection: false,
);
$legacyProfile = BaselineProfile::factory()->active()->create([
'workspace_id' => (int) $tenant->workspace_id,
'name' => 'Spec202 Legacy Browser Baseline',
]);
DB::table('baseline_profiles')
->where('id', (int) $legacyProfile->getKey())
->update([
'scope_jsonb' => json_encode([
'policy_types' => ['deviceConfiguration'],
'foundation_types' => ['assignmentFilter'],
], JSON_THROW_ON_ERROR),
'updated_at' => now(),
]);
$captureProfile = BaselineProfile::factory()->active()->create([
'workspace_id' => (int) $tenant->workspace_id,
'name' => 'Spec202 Capture Browser Baseline',
'scope_jsonb' => ['policy_types' => ['deviceConfiguration'], 'foundation_types' => []],
]);
$compareProfile = BaselineProfile::factory()->active()->create([
'workspace_id' => (int) $tenant->workspace_id,
'name' => 'Spec202 Compare Browser Baseline',
'scope_jsonb' => ['policy_types' => ['deviceConfiguration'], 'foundation_types' => []],
]);
$snapshot = BaselineSnapshot::factory()->complete()->create([
'workspace_id' => (int) $tenant->workspace_id,
'baseline_profile_id' => (int) $compareProfile->getKey(),
]);
$compareProfile->update(['active_snapshot_id' => (int) $snapshot->getKey()]);
BaselineTenantAssignment::factory()->create([
'workspace_id' => (int) $tenant->workspace_id,
'tenant_id' => (int) $tenant->getKey(),
'baseline_profile_id' => (int) $compareProfile->getKey(),
]);
visit(spec202SmokeLoginUrl($user, $tenant))
->waitForText('Dashboard')
->assertNoJavaScriptErrors()
->assertNoConsoleLogs();
visit(BaselineProfileResource::getUrl('create', panel: 'admin'))
->waitForText('Governed subject summary')
->assertNoJavaScriptErrors()
->assertNoConsoleLogs()
->assertSee('Support readiness')
->assertSee('Selection feedback');
visit(BaselineProfileResource::getUrl('edit', ['record' => $legacyProfile], panel: 'admin'))
->waitForText('Governed subject summary')
->assertNoJavaScriptErrors()
->assertNoConsoleLogs()
->assertSee('Support readiness')
->assertSee('Device Configuration')
->assertSee('Assignment Filter')
->assertSee('This Intune-first selection will be saved forward as canonical governed-subject scope V2.');
visit(BaselineProfileResource::getUrl('view', ['record' => $captureProfile], panel: 'admin'))
->waitForText('Governed subject summary')
->assertNoJavaScriptErrors()
->assertNoConsoleLogs()
->assertSee('Support readiness')
->assertSee('Capture baseline')
->click('Capture baseline')
->waitForText('Source Tenant')
->click('Cancel')
->assertSee('Capture baseline');
visit(BaselineProfileResource::getUrl('view', ['record' => $compareProfile], panel: 'admin'))
->waitForText('Governed subject summary')
->assertNoJavaScriptErrors()
->assertNoConsoleLogs()
->assertSee('Support readiness')
->assertSee('Normalization lineage')
->assertSee('Canonical governed-subject scope V2 is already stored for this baseline profile.')
->assertSee('Compare now')
->click('Compare now')
->waitForText('Target Tenant')
->click('Cancel')
->assertSee('Compare now');
});
it('smokes tolerant invalid scope rendering on the baseline detail surface', function (): void {
[$user, $tenant] = createUserWithTenant(
role: 'owner',
workspaceRole: 'manager',
ensureDefaultMicrosoftProviderConnection: false,
);
$invalidProfile = BaselineProfile::factory()->active()->create([
'workspace_id' => (int) $tenant->workspace_id,
'name' => 'Spec202 Invalid Browser Baseline',
]);
DB::table('baseline_profiles')
->where('id', (int) $invalidProfile->getKey())
->update([
'scope_jsonb' => json_encode([
'version' => 2,
'entries' => [
[
'domain_key' => 'platform_foundation',
'subject_class' => 'configuration_resource',
'subject_type_keys' => ['intuneRoleAssignment'],
'filters' => [],
],
],
], JSON_THROW_ON_ERROR),
'updated_at' => now(),
]);
visit(spec202SmokeLoginUrl($user, $tenant))
->waitForText('Dashboard')
->assertNoJavaScriptErrors()
->assertNoConsoleLogs();
visit(BaselineProfileResource::getUrl('view', ['record' => $invalidProfile], panel: 'admin'))
->waitForText('Governed subject summary')
->assertNoJavaScriptErrors()
->assertNoConsoleLogs()
->assertSee('Invalid governed subject selection.')
->assertSee('Support readiness')
->assertSee('Capture: blocked. Compare: blocked.')
->assertSee('Stored scope is invalid and must be repaired before capture or compare can continue.');
});