149 lines
5.8 KiB
PHP
149 lines
5.8 KiB
PHP
<?php
|
|
|
|
use App\Models\OperationRun;
|
|
use App\Models\Tenant;
|
|
use App\Services\Providers\ProviderOperationStartResult;
|
|
use App\Support\OperationRunLinks;
|
|
use App\Support\OpsUx\ProviderOperationStartResultPresenter;
|
|
use App\Support\ReasonTranslation\NextStepOption;
|
|
use App\Support\ReasonTranslation\ReasonResolutionEnvelope;
|
|
use Filament\Actions\Action;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
|
|
uses(RefreshDatabase::class);
|
|
|
|
it('builds queued notifications for accepted provider-backed starts', function (): void {
|
|
$tenant = Tenant::factory()->create();
|
|
$run = OperationRun::factory()->create([
|
|
'tenant_id' => $tenant->getKey(),
|
|
'type' => 'provider.connection.check',
|
|
'status' => 'queued',
|
|
'context' => [
|
|
'provider_connection_id' => 123,
|
|
],
|
|
]);
|
|
|
|
$presenter = app(ProviderOperationStartResultPresenter::class);
|
|
|
|
$notification = $presenter->notification(
|
|
result: ProviderOperationStartResult::started($run, true),
|
|
blockedTitle: 'Verification blocked',
|
|
runUrl: OperationRunLinks::tenantlessView($run),
|
|
extraActions: [
|
|
Action::make('manage_connections')
|
|
->label('Manage Provider Connections')
|
|
->url('/provider-connections'),
|
|
],
|
|
);
|
|
|
|
$actions = collect($notification->getActions());
|
|
|
|
expect($notification->getTitle())->toBe('Provider connection check queued')
|
|
->and($notification->getBody())->toBe('Queued for execution. Open the operation for progress and next steps.')
|
|
->and($actions->map(fn (Action $action): string => (string) $action->getName())->all())->toBe([
|
|
'view_run',
|
|
'manage_connections',
|
|
])
|
|
->and($actions->map(fn (Action $action): string => (string) $action->getLabel())->all())->toBe([
|
|
'Open operation',
|
|
'Manage Provider Connections',
|
|
]);
|
|
});
|
|
|
|
it('builds already-running notifications for deduped provider-backed starts', function (): void {
|
|
$tenant = Tenant::factory()->create();
|
|
$run = OperationRun::factory()->create([
|
|
'tenant_id' => $tenant->getKey(),
|
|
'type' => 'provider.connection.check',
|
|
'status' => 'running',
|
|
'context' => [
|
|
'provider_connection_id' => 123,
|
|
],
|
|
]);
|
|
|
|
$presenter = app(ProviderOperationStartResultPresenter::class);
|
|
|
|
$notification = $presenter->notification(
|
|
result: ProviderOperationStartResult::deduped($run),
|
|
blockedTitle: 'Verification blocked',
|
|
runUrl: OperationRunLinks::tenantlessView($run),
|
|
);
|
|
|
|
expect($notification->getTitle())->toBe('Provider connection check already running')
|
|
->and($notification->getBody())->toBe('A matching operation is already queued or running. Open the operation for progress and next steps.');
|
|
});
|
|
|
|
it('builds scope-busy notifications for conflicting provider-backed starts', function (): void {
|
|
$tenant = Tenant::factory()->create();
|
|
$run = OperationRun::factory()->create([
|
|
'tenant_id' => $tenant->getKey(),
|
|
'type' => 'inventory_sync',
|
|
'status' => 'running',
|
|
'context' => [
|
|
'provider_connection_id' => 123,
|
|
],
|
|
]);
|
|
|
|
$presenter = app(ProviderOperationStartResultPresenter::class);
|
|
|
|
$notification = $presenter->notification(
|
|
result: ProviderOperationStartResult::scopeBusy($run),
|
|
blockedTitle: 'Inventory sync blocked',
|
|
runUrl: OperationRunLinks::tenantlessView($run),
|
|
);
|
|
|
|
expect($notification->getTitle())->toBe('Scope busy')
|
|
->and($notification->getBody())->toBe('Another provider-backed operation is already running for this scope. Open the active operation for progress and next steps.');
|
|
});
|
|
|
|
it('builds blocked notifications from translated reason detail and first next step', function (): void {
|
|
$tenant = Tenant::factory()->create();
|
|
$reasonEnvelope = new ReasonResolutionEnvelope(
|
|
internalCode: 'provider_consent_missing',
|
|
operatorLabel: 'Admin consent required',
|
|
shortExplanation: 'Grant admin consent for this provider connection before retrying.',
|
|
actionability: 'prerequisite_missing',
|
|
nextSteps: [
|
|
NextStepOption::link('Grant admin consent', '/provider-connections/1/consent'),
|
|
NextStepOption::link('Open provider settings', '/provider-connections/1'),
|
|
],
|
|
);
|
|
$run = OperationRun::factory()->create([
|
|
'tenant_id' => $tenant->getKey(),
|
|
'type' => 'provider.connection.check',
|
|
'status' => 'completed',
|
|
'outcome' => 'blocked',
|
|
'context' => [
|
|
'reason_code' => 'provider_consent_missing',
|
|
'reason_translation' => $reasonEnvelope->toArray(),
|
|
],
|
|
]);
|
|
|
|
$presenter = app(ProviderOperationStartResultPresenter::class);
|
|
|
|
$notification = $presenter->notification(
|
|
result: ProviderOperationStartResult::blocked($run),
|
|
blockedTitle: 'Verification blocked',
|
|
runUrl: OperationRunLinks::tenantlessView($run),
|
|
extraActions: [
|
|
Action::make('manage_connections')
|
|
->label('Manage Provider Connections')
|
|
->url('/provider-connections'),
|
|
],
|
|
);
|
|
|
|
$actions = collect($notification->getActions());
|
|
|
|
expect($notification->getTitle())->toBe('Verification blocked')
|
|
->and($notification->getBody())->toBe("Admin consent required\nGrant admin consent for this provider connection before retrying.\nNext step: Grant admin consent.")
|
|
->and($actions->map(fn (Action $action): string => (string) $action->getName())->all())->toBe([
|
|
'view_run',
|
|
'next_step_0',
|
|
'manage_connections',
|
|
])
|
|
->and($actions->map(fn (Action $action): string => (string) $action->getLabel())->all())->toBe([
|
|
'Open operation',
|
|
'Grant admin consent',
|
|
'Manage Provider Connections',
|
|
]);
|
|
}); |