feat/011-restore-run-wizard #37
@ -397,11 +397,7 @@ public static function getWizardSteps(): array
|
||||
|
||||
$selectedItemIds = is_array($selectedItemIds) ? $selectedItemIds : null;
|
||||
|
||||
$groupMapping = $get('group_mapping') ?? [];
|
||||
$groupMapping = is_array($groupMapping) ? $groupMapping : [];
|
||||
$groupMapping = collect($groupMapping)
|
||||
->map(fn ($value) => is_string($value) ? $value : null)
|
||||
->all();
|
||||
$groupMapping = static::normalizeGroupMapping($get('group_mapping'));
|
||||
|
||||
$checker = app(RestoreRiskChecker::class);
|
||||
$outcome = $checker->check(
|
||||
@ -1143,7 +1139,7 @@ public static function createRestoreRun(array $data): RestoreRun
|
||||
$actorEmail = auth()->user()?->email;
|
||||
$actorName = auth()->user()?->name;
|
||||
$isDryRun = (bool) ($data['is_dry_run'] ?? true);
|
||||
$groupMapping = $data['group_mapping'] ?? [];
|
||||
$groupMapping = static::normalizeGroupMapping($data['group_mapping'] ?? null);
|
||||
|
||||
$checkSummary = $data['check_summary'] ?? null;
|
||||
$checkResults = $data['check_results'] ?? null;
|
||||
@ -1386,6 +1382,76 @@ private static function unresolvedGroups(?int $backupSetId, ?array $selectedItem
|
||||
return $unresolved;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, string|null>
|
||||
*/
|
||||
private static function normalizeGroupMapping(mixed $mapping): array
|
||||
{
|
||||
if ($mapping instanceof \Illuminate\Contracts\Support\Arrayable) {
|
||||
$mapping = $mapping->toArray();
|
||||
}
|
||||
|
||||
if ($mapping instanceof \stdClass) {
|
||||
$mapping = (array) $mapping;
|
||||
}
|
||||
|
||||
if (! is_array($mapping)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$result = [];
|
||||
|
||||
if (array_key_exists('group_mapping', $mapping)) {
|
||||
$nested = $mapping['group_mapping'];
|
||||
|
||||
if ($nested instanceof \Illuminate\Contracts\Support\Arrayable) {
|
||||
$nested = $nested->toArray();
|
||||
}
|
||||
|
||||
if ($nested instanceof \stdClass) {
|
||||
$nested = (array) $nested;
|
||||
}
|
||||
|
||||
if (is_array($nested)) {
|
||||
$mapping = $nested;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($mapping as $key => $value) {
|
||||
if (! is_string($key) || $key === '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$sourceGroupId = str_starts_with($key, 'group_mapping.')
|
||||
? substr($key, strlen('group_mapping.'))
|
||||
: $key;
|
||||
|
||||
if ($sourceGroupId === '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($value instanceof BackedEnum) {
|
||||
$value = $value->value;
|
||||
}
|
||||
|
||||
if (is_array($value) || $value instanceof \stdClass) {
|
||||
$value = (array) $value;
|
||||
$value = $value['value'] ?? $value['id'] ?? null;
|
||||
}
|
||||
|
||||
if (is_string($value)) {
|
||||
$value = trim($value);
|
||||
$result[$sourceGroupId] = $value !== '' ? $value : null;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$result[$sourceGroupId] = null;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, string>
|
||||
*/
|
||||
|
||||
@ -129,3 +129,94 @@
|
||||
]);
|
||||
expect($run->metadata['check_summary']['blocking'] ?? null)->toBe(1);
|
||||
});
|
||||
|
||||
test('restore wizard treats skipped orphaned groups as a warning instead of a blocker', function () {
|
||||
$tenant = Tenant::create([
|
||||
'tenant_id' => 'tenant-1',
|
||||
'name' => 'Tenant One',
|
||||
'metadata' => [],
|
||||
]);
|
||||
|
||||
$tenant->makeCurrent();
|
||||
|
||||
$policy = Policy::create([
|
||||
'tenant_id' => $tenant->id,
|
||||
'external_id' => 'policy-1',
|
||||
'policy_type' => 'settingsCatalogPolicy',
|
||||
'display_name' => 'Settings Catalog',
|
||||
'platform' => 'windows',
|
||||
]);
|
||||
|
||||
$backupSet = BackupSet::create([
|
||||
'tenant_id' => $tenant->id,
|
||||
'name' => 'Backup',
|
||||
'status' => 'completed',
|
||||
'item_count' => 1,
|
||||
]);
|
||||
|
||||
$backupItem = BackupItem::create([
|
||||
'tenant_id' => $tenant->id,
|
||||
'backup_set_id' => $backupSet->id,
|
||||
'policy_id' => $policy->id,
|
||||
'policy_identifier' => $policy->external_id,
|
||||
'policy_type' => $policy->policy_type,
|
||||
'platform' => $policy->platform,
|
||||
'captured_at' => now(),
|
||||
'payload' => ['id' => $policy->external_id],
|
||||
'assignments' => [[
|
||||
'target' => [
|
||||
'@odata.type' => '#microsoft.graph.groupAssignmentTarget',
|
||||
'groupId' => 'source-group-1',
|
||||
'group_display_name' => 'Source Group',
|
||||
],
|
||||
'intent' => 'apply',
|
||||
]],
|
||||
]);
|
||||
|
||||
$this->mock(GroupResolver::class, function (MockInterface $mock) {
|
||||
$mock->shouldReceive('resolveGroupIds')
|
||||
->andReturnUsing(function (array $groupIds): array {
|
||||
return collect($groupIds)
|
||||
->mapWithKeys(fn (string $id) => [$id => [
|
||||
'id' => $id,
|
||||
'displayName' => null,
|
||||
'orphaned' => true,
|
||||
]])
|
||||
->all();
|
||||
});
|
||||
});
|
||||
|
||||
$user = User::factory()->create();
|
||||
$this->actingAs($user);
|
||||
|
||||
$component = Livewire::test(CreateRestoreRun::class)
|
||||
->fillForm([
|
||||
'backup_set_id' => $backupSet->id,
|
||||
])
|
||||
->goToNextWizardStep()
|
||||
->fillForm([
|
||||
'scope_mode' => 'selected',
|
||||
'backup_item_ids' => [$backupItem->id],
|
||||
])
|
||||
->goToNextWizardStep()
|
||||
->set('data.group_mapping', (object) [
|
||||
'source-group-1' => 'SKIP',
|
||||
])
|
||||
->callFormComponentAction('check_results', 'run_restore_checks');
|
||||
|
||||
$summary = $component->get('data.check_summary');
|
||||
$results = $component->get('data.check_results');
|
||||
|
||||
expect($summary)->toBeArray();
|
||||
expect($summary['blocking'] ?? null)->toBe(0);
|
||||
expect($summary['has_blockers'] ?? null)->toBeFalse();
|
||||
expect($summary['warning'] ?? null)->toBe(1);
|
||||
|
||||
$assignmentCheck = collect($results)->firstWhere('code', 'assignment_groups');
|
||||
expect($assignmentCheck)->toBeArray();
|
||||
expect($assignmentCheck['severity'] ?? null)->toBe('warning');
|
||||
|
||||
$skippedGroups = $assignmentCheck['meta']['skipped'] ?? [];
|
||||
expect($skippedGroups)->toBeArray();
|
||||
expect($skippedGroups[0]['id'] ?? null)->toBe('source-group-1');
|
||||
});
|
||||
|
||||
Loading…
Reference in New Issue
Block a user