Kurzbeschreibung Versteckt die Rerun-Row-Action für archivierte (soft-deleted) RestoreRuns und verhindert damit fehlerhafte Neu-Starts aus dem Archiv; ergänzt einen Regressionstest. Änderungen Code: RestoreRunResource.php — Sichtbarkeit der rerun-Action geprüft auf ! $record->trashed() und defensive Abbruchprüfung im Action-Handler. Tests: RestoreRunRerunTest.php — neuer Test rerun action is hidden for archived restore runs. Warum Archivierte RestoreRuns durften nicht neu gestartet werden; UI zeigte trotzdem die Option. Das führte zu verwirrendem Verhalten und möglichen Fehlern beim Enqueueing. Verifikation / QA Unit/Feature: ./vendor/bin/sail artisan test tests/Feature/RestoreRunRerunTest.php Stil/format: ./vendor/bin/pint --dirty Manuell (UI): Als Tenant-Admin Filament → Restore Runs öffnen. Filter Archived aktivieren (oder Trashed filter auswählen). Sicherstellen, dass für archivierte Einträge die Rerun-Action nicht sichtbar ist. Auf einem aktiven (nicht-archivierten) Run prüfen, dass Rerun sichtbar bleibt und wie erwartet eine neue RestoreRun erzeugt. Wichtige Hinweise Kein DB-Migration required. Diese PR enthält nur den UI-/Filament-Fix; die zuvor gemachten operative Fixes für Queue/adapter-Reconciliation bleiben ebenfalls auf dem Branch (z. B. frühere commits während der Debugging-Session). T055 (Schema squash) wurde bewusst zurückgestellt und ist nicht Teil dieses PRs. Merge-Checklist Tests lokal laufen (RestoreRunRerunTest grünt) Pint läuft ohne ungepatchte Fehler Branch gepusht: 056-remove-legacy-bulkops (PR-URL: https://git.cloudarix.de/ahmido/TenantAtlas/compare/dev...056-remove-legacy-bulkops) Co-authored-by: Ahmed Darrazi <ahmeddarrazi@adsmac.local> Reviewed-on: #65
133 lines
3.5 KiB
PHP
133 lines
3.5 KiB
PHP
<?php
|
|
|
|
use App\Filament\Resources\PolicyResource\Pages\ViewPolicy;
|
|
use App\Jobs\CapturePolicySnapshotJob;
|
|
use App\Jobs\Operations\CapturePolicySnapshotWorkerJob;
|
|
use App\Models\Policy;
|
|
use App\Models\Tenant;
|
|
use App\Models\User;
|
|
use App\Services\Graph\AssignmentFetcher;
|
|
use App\Services\Graph\ScopeTagResolver;
|
|
use App\Services\Intune\PolicySnapshotService;
|
|
use Filament\Facades\Filament;
|
|
use Illuminate\Contracts\Cache\Lock;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Illuminate\Support\Facades\Cache;
|
|
use Illuminate\Support\Facades\Queue;
|
|
use Livewire\Livewire;
|
|
use Mockery\MockInterface;
|
|
|
|
uses(RefreshDatabase::class);
|
|
|
|
it('captures a policy snapshot with scope tags when requested', function () {
|
|
Queue::fake();
|
|
|
|
$tenant = Tenant::factory()->create();
|
|
$tenant->makeCurrent();
|
|
$policy = Policy::factory()->for($tenant)->create([
|
|
'external_id' => 'policy-123',
|
|
]);
|
|
|
|
$user = User::factory()->create();
|
|
$this->actingAs($user);
|
|
$user->tenants()->syncWithoutDetaching([
|
|
$tenant->getKey() => ['role' => 'owner'],
|
|
]);
|
|
Filament::setTenant($tenant, true);
|
|
|
|
$this->mock(PolicySnapshotService::class, function (MockInterface $mock) use ($policy) {
|
|
$mock->shouldReceive('fetch')
|
|
->once()
|
|
->andReturn([
|
|
'payload' => [
|
|
'id' => $policy->external_id,
|
|
'name' => $policy->display_name,
|
|
'roleScopeTagIds' => ['0'],
|
|
],
|
|
]);
|
|
});
|
|
|
|
$this->mock(AssignmentFetcher::class, function (MockInterface $mock) {
|
|
$mock->shouldReceive('fetch')->never();
|
|
});
|
|
|
|
$this->mock(ScopeTagResolver::class, function (MockInterface $mock) {
|
|
$mock->shouldReceive('resolve')
|
|
->once()
|
|
->andReturn([
|
|
['id' => '0', 'displayName' => 'Default'],
|
|
]);
|
|
});
|
|
|
|
Livewire::test(ViewPolicy::class, ['record' => $policy->getRouteKey()])
|
|
->callAction('capture_snapshot', data: [
|
|
'include_assignments' => false,
|
|
'include_scope_tags' => true,
|
|
]);
|
|
|
|
$job = null;
|
|
|
|
$lock = new class implements Lock
|
|
{
|
|
public function get($callback = null): bool
|
|
{
|
|
return true;
|
|
}
|
|
|
|
public function block($seconds, $callback = null): bool
|
|
{
|
|
return true;
|
|
}
|
|
|
|
public function release(): bool
|
|
{
|
|
return true;
|
|
}
|
|
|
|
public function owner(): string
|
|
{
|
|
return 'test';
|
|
}
|
|
|
|
public function forceRelease(): void
|
|
{
|
|
// no-op
|
|
}
|
|
};
|
|
|
|
Cache::partialMock()
|
|
->shouldReceive('lock')
|
|
->andReturn($lock);
|
|
|
|
Queue::assertPushed(CapturePolicySnapshotJob::class, function (CapturePolicySnapshotJob $queuedJob) use (&$job): bool {
|
|
$job = $queuedJob;
|
|
|
|
return true;
|
|
});
|
|
|
|
expect($job)->not->toBeNull();
|
|
|
|
app()->call([$job, 'handle']);
|
|
|
|
$worker = null;
|
|
|
|
Queue::assertPushed(CapturePolicySnapshotWorkerJob::class, function (CapturePolicySnapshotWorkerJob $queuedWorker) use (&$worker): bool {
|
|
$worker = $queuedWorker;
|
|
|
|
return true;
|
|
});
|
|
|
|
expect($worker)->not->toBeNull();
|
|
|
|
app()->call([$worker, 'handle']);
|
|
|
|
$version = $policy->versions()->first();
|
|
|
|
expect($version)->not->toBeNull();
|
|
expect($version->assignments)->toBeNull();
|
|
expect($version->scope_tags)->toBe([
|
|
'ids' => ['0'],
|
|
'names' => ['Default'],
|
|
]);
|
|
});
|