fix: green test suite after dev merge

This commit is contained in:
Ahmed Darrazi 2026-01-20 00:26:24 +01:00
parent 1ed3b953da
commit ec99c6519c
9 changed files with 75 additions and 34 deletions

View File

@ -17,6 +17,8 @@
use App\Support\OpsUx\OperationUxPresenter; use App\Support\OpsUx\OperationUxPresenter;
use App\Support\OpsUx\OpsUxBrowserEvents; use App\Support\OpsUx\OpsUxBrowserEvents;
use BackedEnum; use BackedEnum;
use Filament\Actions\Action;
use Filament\Notifications\Notification;
use Filament\Pages\Page; use Filament\Pages\Page;
use UnitEnum; use UnitEnum;

View File

@ -37,6 +37,16 @@ class OperationRunResource extends Resource
protected static ?string $navigationLabel = 'Operations'; protected static ?string $navigationLabel = 'Operations';
public static function getEloquentQuery(): Builder
{
$tenantId = Tenant::current()?->getKey();
return parent::getEloquentQuery()
->with('user')
->latest('id')
->when($tenantId, fn (Builder $query) => $query->where('tenant_id', $tenantId));
}
public static function form(Schema $schema): Schema public static function form(Schema $schema): Schema
{ {
return $schema; return $schema;
@ -243,13 +253,6 @@ public static function table(Table $table): Table
->bulkActions([]); ->bulkActions([]);
} }
public static function getEloquentQuery(): Builder
{
return parent::getEloquentQuery()
->with('user')
->latest('id');
}
public static function getPages(): array public static function getPages(): array
{ {
return [ return [

View File

@ -391,7 +391,7 @@ public static function table(Table $table): Table
return $user->canSyncTenant($tenant); return $user->canSyncTenant($tenant);
}) })
->action(function (Policy $record) { ->action(function (Policy $record, HasTable $livewire): void {
$tenant = Tenant::current(); $tenant = Tenant::current();
$user = auth()->user(); $user = auth()->user();
@ -700,7 +700,7 @@ public static function table(Table $table): Table
return $value === 'ignored'; return $value === 'ignored';
}) })
->action(function (Collection $records) { ->action(function (Collection $records, HasTable $livewire): void {
$tenant = Tenant::current(); $tenant = Tenant::current();
$user = auth()->user(); $user = auth()->user();
$count = $records->count(); $count = $records->count();

View File

@ -6,6 +6,7 @@
use App\Models\Tenant; use App\Models\Tenant;
use App\Models\User; use App\Models\User;
use Illuminate\Auth\Access\HandlesAuthorization; use Illuminate\Auth\Access\HandlesAuthorization;
use Illuminate\Auth\Access\Response;
class OperationRunPolicy class OperationRunPolicy
{ {
@ -22,7 +23,7 @@ public function viewAny(User $user): bool
return $user->canAccessTenant($tenant); return $user->canAccessTenant($tenant);
} }
public function view(User $user, OperationRun $run): bool public function view(User $user, OperationRun $run): Response|bool
{ {
$tenant = Tenant::current(); $tenant = Tenant::current();
@ -34,6 +35,10 @@ public function view(User $user, OperationRun $run): bool
return false; return false;
} }
return (int) $run->tenant_id === (int) $tenant->getKey(); if ((int) $run->tenant_id !== (int) $tenant->getKey()) {
return Response::denyAsNotFound();
}
return true;
} }
} }

View File

@ -90,6 +90,29 @@ public function listPolicies(string $policyType, array $options = []): GraphResp
$response = $this->send('GET', $endpoint, $sendOptions, $context); $response = $this->send('GET', $endpoint, $sendOptions, $context);
// Some tenants intermittently return OData select parsing errors.
// Retry once with the same $select before applying the compatibility fallback.
if ($response->failed()) {
$graphResponse = $this->toGraphResponse(
action: 'list_policies',
response: $response,
transform: fn (array $json) => $json['value'] ?? (is_array($json) ? $json : []),
meta: [
'tenant' => $context['tenant'] ?? null,
'path' => $endpoint,
'full_path' => $fullPath,
'method' => 'GET',
'query' => $query ?: null,
'client_request_id' => $clientRequestId,
],
warnings: $warnings,
);
if ($this->shouldApplySelectFallback($graphResponse, $query)) {
$response = $this->send('GET', $endpoint, $sendOptions, $context);
}
}
if ($response->failed()) { if ($response->failed()) {
$graphResponse = $this->toGraphResponse( $graphResponse = $this->toGraphResponse(
action: 'list_policies', action: 'list_policies',

View File

@ -156,7 +156,7 @@ public function createBackupSet($tenant, $policyIds, ?string $actorEmail = null,
expect($operationRun->status)->toBe('completed'); expect($operationRun->status)->toBe('completed');
expect($operationRun->outcome)->toBe('failed'); expect($operationRun->outcome)->toBe('failed');
expect($operationRun->failure_summary)->toMatchArray([ expect($operationRun->failure_summary)->toMatchArray([
['code' => 'unknown_policy_type', 'message' => $run->error_message], ['code' => 'unknown_policy_type', 'message' => $run->error_message, 'reason_code' => 'unknown_error'],
]); ]);
}); });

View File

@ -6,6 +6,7 @@
use App\Models\Tenant; use App\Models\Tenant;
use App\Models\User; use App\Models\User;
use App\Services\OperationRunService; use App\Services\OperationRunService;
use App\Services\Operations\BulkSelectionIdentity;
use Illuminate\Foundation\Testing\RefreshDatabase; use Illuminate\Foundation\Testing\RefreshDatabase;
uses(RefreshDatabase::class); uses(RefreshDatabase::class);
@ -19,22 +20,29 @@
/** @var OperationRunService $service */ /** @var OperationRunService $service */
$service = app(OperationRunService::class); $service = app(OperationRunService::class);
$opRun = $service->ensureRun( /** @var BulkSelectionIdentity $selection */
$selection = app(BulkSelectionIdentity::class);
$selectionIdentity = $selection->fromIds($policyIds);
$opRun = $service->enqueueBulkOperation(
tenant: $tenant, tenant: $tenant,
type: 'policy.delete', type: 'policy.delete',
inputs: [ targetScope: [
'scope' => 'subset', 'entra_tenant_id' => (string) ($tenant->tenant_id ?? $tenant->external_id ?? $tenant->getKey()),
'policy_ids' => $policyIds,
], ],
selectionIdentity: $selectionIdentity,
dispatcher: function ($operationRun) use ($tenant, $user, $policyIds): void {
// Simulate sync execution (workers will run immediately on sync queue)
BulkPolicyDeleteJob::dispatchSync(
tenantId: (int) $tenant->getKey(),
userId: (int) $user->getKey(),
policyIds: $policyIds,
operationRun: $operationRun,
);
},
initiator: $user, initiator: $user,
); emitQueuedNotification: false,
// Simulate Sync execution (workers will run immediately on sync queue)
BulkPolicyDeleteJob::dispatchSync(
tenantId: (int) $tenant->getKey(),
userId: (int) $user->getKey(),
policyIds: $policyIds,
operationRun: $opRun,
); );
$opRun->refresh(); $opRun->refresh();
@ -45,9 +53,10 @@
'total' => 10, 'total' => 10,
'processed' => 10, 'processed' => 10,
'succeeded' => 10, 'succeeded' => 10,
'failed' => 0,
]); ]);
expect(($opRun->summary_counts['failed'] ?? 0))->toBe(0);
$policies->each(function ($policy) { $policies->each(function ($policy) {
expect($policy->refresh()->ignored_at)->not->toBeNull(); expect($policy->refresh()->ignored_at)->not->toBeNull();
}); });

View File

@ -78,11 +78,15 @@
expect($operationRun->started_at?->format('Y-m-d H:i:s'))->toBe($startedAt->format('Y-m-d H:i:s')); expect($operationRun->started_at?->format('Y-m-d H:i:s'))->toBe($startedAt->format('Y-m-d H:i:s'));
expect($operationRun->completed_at?->format('Y-m-d H:i:s'))->toBe($finishedAt->format('Y-m-d H:i:s')); expect($operationRun->completed_at?->format('Y-m-d H:i:s'))->toBe($finishedAt->format('Y-m-d H:i:s'));
expect($operationRun->summary_counts)->toMatchArray([ expect($operationRun->context)->toMatchArray([
'backup_schedule_id' => (int) $schedule->id, 'backup_schedule_id' => (int) $schedule->id,
'backup_schedule_run_id' => (int) $scheduleRun->id, 'backup_schedule_run_id' => (int) $scheduleRun->id,
'policies_total' => 5, ]);
'policies_backed_up' => 18,
'sync_failures' => 0, expect($operationRun->summary_counts)->toMatchArray([
'total' => 5,
'processed' => 5,
'succeeded' => 18,
'items' => 5,
]); ]);
}); });

View File

@ -5,7 +5,6 @@
use App\Jobs\ExecuteRestoreRunJob; use App\Jobs\ExecuteRestoreRunJob;
use App\Models\OperationRun; use App\Models\OperationRun;
use App\Models\RestoreRun; use App\Models\RestoreRun;
use App\Services\BulkOperationService;
use App\Services\Intune\AuditLogger; use App\Services\Intune\AuditLogger;
use App\Services\Intune\RestoreService; use App\Services\Intune\RestoreService;
@ -35,9 +34,6 @@
expect($operationRun)->not->toBeNull(); expect($operationRun)->not->toBeNull();
expect($operationRun?->status)->toBe('queued'); expect($operationRun?->status)->toBe('queued');
$this->mock(BulkOperationService::class, function ($mock): void {
$mock->shouldReceive('sanitizeFailureReason')->andReturnUsing(fn (string $message): string => $message);
});
// Simulate downstream code updating RestoreRun status via query builder (no model events). // Simulate downstream code updating RestoreRun status via query builder (no model events).
$this->mock(RestoreService::class, function ($mock) use ($restoreRun): void { $this->mock(RestoreService::class, function ($mock) use ($restoreRun): void {
$mock->shouldReceive('executeForRun') $mock->shouldReceive('executeForRun')
@ -56,7 +52,6 @@
$job->handle( $job->handle(
app(RestoreService::class), app(RestoreService::class),
app(AuditLogger::class), app(AuditLogger::class),
app(BulkOperationService::class),
); );
$operationRun = $operationRun?->fresh(); $operationRun = $operationRun?->fresh();