fix: treat unknown restore types as preview-only

This commit is contained in:
Ahmed Darrazi 2026-01-03 05:13:13 +01:00
parent d54a245a58
commit 1d158ca9bf
3 changed files with 133 additions and 1 deletions

View File

@ -756,7 +756,17 @@ private function resolveRestoreMode(?string $policyType): string
{ {
$meta = $this->resolveTypeMeta($policyType); $meta = $this->resolveTypeMeta($policyType);
return (string) ($meta['restore'] ?? 'enabled'); if ($meta === []) {
return 'preview-only';
}
$restore = $meta['restore'] ?? 'enabled';
if (! is_string($restore) || $restore === '') {
return 'enabled';
}
return $restore;
} }
private function resolveTypeLabel(?string $policyType): string private function resolveTypeLabel(?string $policyType): string

View File

@ -931,6 +931,11 @@ private function resolveTypeMeta(string $policyType): array
private function resolveRestoreMode(string $policyType): string private function resolveRestoreMode(string $policyType): string
{ {
$meta = $this->resolveTypeMeta($policyType); $meta = $this->resolveTypeMeta($policyType);
if ($meta === []) {
return 'preview-only';
}
$restore = $meta['restore'] ?? 'enabled'; $restore = $meta['restore'] ?? 'enabled';
if (! is_string($restore) || $restore === '') { if (! is_string($restore) || $restore === '') {

View File

@ -0,0 +1,117 @@
<?php
use App\Models\BackupItem;
use App\Models\BackupSet;
use App\Models\Tenant;
use App\Services\Graph\GraphClientInterface;
use App\Services\Graph\GraphResponse;
use App\Services\Intune\RestoreService;
use Illuminate\Foundation\Testing\RefreshDatabase;
uses(RefreshDatabase::class);
class RestoreUnknownTypeGraphClient implements GraphClientInterface
{
/** @var array<int, array{policyType:string,policyId:string,payload:array,options:array<string,mixed>}> */
public array $applyPolicyCalls = [];
public function listPolicies(string $policyType, array $options = []): GraphResponse
{
return new GraphResponse(true, []);
}
public function getPolicy(string $policyType, string $policyId, array $options = []): GraphResponse
{
return new GraphResponse(true, ['payload' => []]);
}
public function getOrganization(array $options = []): GraphResponse
{
return new GraphResponse(true, []);
}
public function applyPolicy(string $policyType, string $policyId, array $payload, array $options = []): GraphResponse
{
$this->applyPolicyCalls[] = [
'policyType' => $policyType,
'policyId' => $policyId,
'payload' => $payload,
'options' => $options,
];
return new GraphResponse(false, ['error' => ['message' => 'Bad request']], 400, [], [], [
'error_code' => 'BadRequest',
'error_message' => 'Bad request',
]);
}
public function getServicePrincipalPermissions(array $options = []): GraphResponse
{
return new GraphResponse(true, []);
}
public function request(string $method, string $path, array $options = []): GraphResponse
{
return new GraphResponse(true, []);
}
}
beforeEach(function () {
$this->originalSupportedTypes = config('tenantpilot.supported_policy_types');
$this->originalSecurityBaselineContract = config('graph_contracts.types.securityBaselinePolicy');
});
afterEach(function () {
config()->set('tenantpilot.supported_policy_types', $this->originalSupportedTypes);
if (is_array($this->originalSecurityBaselineContract)) {
config()->set('graph_contracts.types.securityBaselinePolicy', $this->originalSecurityBaselineContract);
}
});
test('restore skips security baseline policies when type metadata is missing', function () {
$client = new RestoreUnknownTypeGraphClient;
app()->instance(GraphClientInterface::class, $client);
$supported = array_values(array_filter(
config('tenantpilot.supported_policy_types', []),
static fn (array $type): bool => ($type['type'] ?? null) !== 'securityBaselinePolicy'
));
config()->set('tenantpilot.supported_policy_types', $supported);
config()->set('graph_contracts.types.securityBaselinePolicy', []);
$tenant = Tenant::factory()->create();
$backupSet = BackupSet::factory()->for($tenant)->create([
'status' => 'completed',
'item_count' => 1,
]);
$backupItem = BackupItem::factory()->for($tenant)->for($backupSet)->create([
'policy_id' => null,
'policy_identifier' => 'baseline-1',
'policy_type' => 'securityBaselinePolicy',
'platform' => 'windows',
'payload' => [
'id' => 'baseline-1',
'@odata.type' => '#microsoft.graph.deviceManagementConfigurationPolicy',
'name' => 'Security Baseline Policy',
],
'assignments' => null,
]);
$service = app(RestoreService::class);
$run = $service->execute(
tenant: $tenant,
backupSet: $backupSet,
selectedItemIds: [$backupItem->id],
dryRun: false,
);
expect($client->applyPolicyCalls)->toHaveCount(0);
$result = $run->results[0] ?? null;
expect($result)->toBeArray();
expect($result['status'] ?? null)->toBe('skipped');
expect($result['restore_mode'] ?? null)->toBe('preview-only');
});