## Summary - add a workspace-scoped baseline compare matrix page under baseline profiles - derive matrix tenant summaries, subject rows, cell states, freshness, and trust from existing snapshots, compare runs, and findings - add confirmation-gated `Compare assigned tenants` actions on the baseline detail and matrix surfaces without introducing a workspace umbrella run - preserve matrix navigation context into tenant compare and finding drilldowns and add centralized matrix badge semantics - include spec, plan, data model, contracts, quickstart, tasks, and focused feature/browser coverage for Spec 190 ## Verification - `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Unit/Badges/BaselineCompareMatrixBadgesTest.php tests/Feature/Baselines/BaselineCompareMatrixBuilderTest.php tests/Feature/Baselines/BaselineCompareMatrixCompareAllActionTest.php tests/Feature/Baselines/BaselineComparePerformanceGuardTest.php tests/Feature/Filament/BaselineCompareMatrixPageTest.php tests/Feature/Filament/BaselineProfileCompareStartSurfaceTest.php tests/Feature/Rbac/BaselineCompareMatrixAuthorizationTest.php tests/Feature/Guards/ActionSurfaceContractTest.php tests/Feature/Guards/NoAdHocStatusBadgesTest.php tests/Feature/Guards/NoDiagnosticWarningBadgesTest.php` - `cd apps/platform && ./vendor/bin/sail bin pint --dirty --format agent` - completed an integrated-browser smoke flow locally for matrix render, differ filter, finding drilldown round-trip, and `Compare assigned tenants` confirmation/action Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #221
96 lines
3.8 KiB
PHP
96 lines
3.8 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use App\Filament\Pages\BaselineCompareMatrix;
|
|
use App\Jobs\CompareBaselineToTenantJob;
|
|
use App\Models\OperationRun;
|
|
use App\Models\Tenant;
|
|
use App\Services\Baselines\BaselineCompareService;
|
|
use App\Support\OperationRunOutcome;
|
|
use App\Support\OperationRunStatus;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Illuminate\Support\Facades\Queue;
|
|
use Livewire\Livewire;
|
|
use Tests\Feature\Concerns\BuildsBaselineCompareMatrixFixtures;
|
|
|
|
uses(RefreshDatabase::class, BuildsBaselineCompareMatrixFixtures::class);
|
|
|
|
it('fans out compare starts across the visible assigned set without creating a workspace umbrella run', function (): void {
|
|
Queue::fake();
|
|
|
|
$fixture = $this->makeBaselineCompareMatrixFixture();
|
|
|
|
$readonlyTenant = Tenant::factory()->create([
|
|
'workspace_id' => (int) $fixture['workspace']->getKey(),
|
|
'name' => 'Readonly Contoso',
|
|
]);
|
|
|
|
$fixture['user']->tenants()->syncWithoutDetaching([
|
|
(int) $readonlyTenant->getKey() => ['role' => 'readonly'],
|
|
]);
|
|
|
|
$this->assignTenantToBaselineProfile($fixture['profile'], $readonlyTenant);
|
|
|
|
$service = app(BaselineCompareService::class);
|
|
|
|
$existingRunResult = $service->startCompareForProfile(
|
|
$fixture['profile'],
|
|
$fixture['visibleTenantTwo'],
|
|
$fixture['user'],
|
|
);
|
|
|
|
expect($existingRunResult['ok'] ?? false)->toBeTrue();
|
|
|
|
$result = $service->startCompareForVisibleAssignments($fixture['profile'], $fixture['user']);
|
|
|
|
expect($result['visibleAssignedTenantCount'])->toBe(3)
|
|
->and($result['queuedCount'])->toBe(1)
|
|
->and($result['alreadyQueuedCount'])->toBe(1)
|
|
->and($result['blockedCount'])->toBe(1);
|
|
|
|
$launchStates = collect($result['targets'])
|
|
->mapWithKeys(static fn (array $target): array => [(int) $target['tenantId'] => (string) $target['launchState']])
|
|
->all();
|
|
|
|
expect($launchStates[(int) $fixture['visibleTenant']->getKey()] ?? null)->toBe('queued')
|
|
->and($launchStates[(int) $fixture['visibleTenantTwo']->getKey()] ?? null)->toBe('already_queued')
|
|
->and($launchStates[(int) $readonlyTenant->getKey()] ?? null)->toBe('blocked');
|
|
|
|
Queue::assertPushed(CompareBaselineToTenantJob::class);
|
|
|
|
$activeRuns = OperationRun::query()
|
|
->where('workspace_id', (int) $fixture['workspace']->getKey())
|
|
->where('type', 'baseline_compare')
|
|
->get();
|
|
|
|
expect($activeRuns)->toHaveCount(2)
|
|
->and($activeRuns->every(static fn (OperationRun $run): bool => $run->tenant_id !== null))->toBeTrue()
|
|
->and($activeRuns->every(static fn (OperationRun $run): bool => (string) $run->status === OperationRunStatus::Queued->value))->toBeTrue()
|
|
->and($activeRuns->every(static fn (OperationRun $run): bool => (string) $run->outcome === OperationRunOutcome::Pending->value))->toBeTrue()
|
|
->and(OperationRun::query()->whereNull('tenant_id')->where('type', 'baseline_compare')->count())->toBe(0);
|
|
});
|
|
|
|
it('runs compare assigned tenants from the matrix page and keeps feedback on tenant-owned runs', function (): void {
|
|
Queue::fake();
|
|
|
|
$fixture = $this->makeBaselineCompareMatrixFixture();
|
|
|
|
$this->setAdminWorkspaceContext($fixture['user'], $fixture['workspace']);
|
|
|
|
Livewire::actingAs($fixture['user'])
|
|
->test(BaselineCompareMatrix::class, ['record' => $fixture['profile']->getKey()])
|
|
->assertActionVisible('compareAssignedTenants')
|
|
->assertActionEnabled('compareAssignedTenants')
|
|
->callAction('compareAssignedTenants')
|
|
->assertStatus(200);
|
|
|
|
Queue::assertPushed(CompareBaselineToTenantJob::class, 2);
|
|
|
|
expect(OperationRun::query()
|
|
->where('workspace_id', (int) $fixture['workspace']->getKey())
|
|
->where('type', 'baseline_compare')
|
|
->whereNull('tenant_id')
|
|
->count())->toBe(0);
|
|
});
|