What Changed Removed per-file uses(TestCase::class ...) bindings in Unit tests to avoid Pest v4 “folder already uses the test case” discovery failure (kept RefreshDatabase where needed). Updated the backup scheduling job test to pass the newly required BulkOperationService when manually calling RunBackupScheduleJob::handle(). Where Unit (bulk cleanup across 56 files) RunBackupScheduleJobTest.php Verification ./vendor/bin/sail test → 443 passed, 5 skipped Co-authored-by: Ahmed Darrazi <ahmeddarrazi@adsmac.local> Reviewed-on: #45
253 lines
7.9 KiB
PHP
253 lines
7.9 KiB
PHP
<?php
|
|
|
|
use App\Models\AuditLog;
|
|
use App\Models\Tenant;
|
|
use App\Services\AssignmentRestoreService;
|
|
use App\Services\Graph\AssignmentFilterResolver;
|
|
use App\Services\Graph\GraphClientInterface;
|
|
use App\Services\Graph\GraphContractRegistry;
|
|
use App\Services\Graph\GraphLogger;
|
|
use App\Services\Graph\GraphResponse;
|
|
use App\Services\Intune\AuditLogger;
|
|
|
|
beforeEach(function () {
|
|
config()->set('graph_contracts.types.deviceManagementScript', [
|
|
'assignments_create_path' => '/deviceManagement/deviceManagementScripts/{id}/assign',
|
|
'assignments_create_method' => 'POST',
|
|
'assignments_payload_key' => 'deviceManagementScriptAssignments',
|
|
]);
|
|
config()->set('graph_contracts.types.settingsCatalogPolicy', [
|
|
'assignments_create_path' => '/deviceManagement/configurationPolicies/{id}/assign',
|
|
'assignments_create_method' => 'POST',
|
|
]);
|
|
config()->set('graph_contracts.types.appProtectionPolicy', [
|
|
'assignments_create_path' => '/deviceAppManagement/managedAppPolicies/{id}/assign',
|
|
'assignments_create_method' => 'POST',
|
|
'assignments_payload_key' => 'assignments',
|
|
]);
|
|
|
|
$this->graphClient = Mockery::mock(GraphClientInterface::class);
|
|
$this->auditLogger = Mockery::mock(AuditLogger::class);
|
|
$this->filterResolver = Mockery::mock(AssignmentFilterResolver::class);
|
|
$this->filterResolver->shouldReceive('resolve')->andReturn([])->byDefault();
|
|
|
|
$this->service = new AssignmentRestoreService(
|
|
$this->graphClient,
|
|
app(GraphContractRegistry::class),
|
|
app(GraphLogger::class),
|
|
$this->auditLogger,
|
|
$this->filterResolver,
|
|
);
|
|
});
|
|
|
|
it('uses the contract assignment payload key for assign actions', function () {
|
|
$tenant = Tenant::factory()->make([
|
|
'tenant_id' => 'tenant-123',
|
|
'app_client_id' => null,
|
|
'app_client_secret' => null,
|
|
]);
|
|
$policyId = 'policy-123';
|
|
$assignments = [
|
|
[
|
|
'id' => 'assignment-1',
|
|
'target' => [
|
|
'@odata.type' => '#microsoft.graph.groupAssignmentTarget',
|
|
'groupId' => 'group-1',
|
|
],
|
|
],
|
|
];
|
|
$expectedAssignments = [
|
|
[
|
|
'target' => [
|
|
'@odata.type' => '#microsoft.graph.groupAssignmentTarget',
|
|
'groupId' => 'group-1',
|
|
],
|
|
],
|
|
];
|
|
|
|
$this->graphClient
|
|
->shouldReceive('request')
|
|
->once()
|
|
->with('POST', "/deviceManagement/deviceManagementScripts/{$policyId}/assign", Mockery::on(
|
|
fn (array $options) => ($options['json']['deviceManagementScriptAssignments'] ?? null) === $expectedAssignments
|
|
))
|
|
->andReturn(new GraphResponse(success: true, data: []));
|
|
|
|
$this->auditLogger
|
|
->shouldReceive('log')
|
|
->once()
|
|
->andReturn(new AuditLog);
|
|
|
|
$result = $this->service->restore(
|
|
$tenant,
|
|
'deviceManagementScript',
|
|
$policyId,
|
|
$assignments,
|
|
[]
|
|
);
|
|
|
|
expect($result['summary']['success'])->toBe(1);
|
|
expect($result['summary']['failed'])->toBe(0);
|
|
expect($result['summary']['skipped'])->toBe(0);
|
|
});
|
|
|
|
it('uses derived assign endpoints for app protection policies', function () {
|
|
$tenant = Tenant::factory()->make([
|
|
'tenant_id' => 'tenant-123',
|
|
'app_client_id' => null,
|
|
'app_client_secret' => null,
|
|
]);
|
|
$policyId = 'policy-123';
|
|
$assignments = [
|
|
[
|
|
'id' => 'assignment-1',
|
|
'target' => [
|
|
'@odata.type' => '#microsoft.graph.groupAssignmentTarget',
|
|
'groupId' => 'group-1',
|
|
],
|
|
],
|
|
];
|
|
|
|
$this->graphClient
|
|
->shouldReceive('request')
|
|
->once()
|
|
->with('POST', "/deviceAppManagement/androidManagedAppProtections/{$policyId}/assign", Mockery::on(
|
|
fn (array $options) => isset($options['json']['assignments'])
|
|
))
|
|
->andReturn(new GraphResponse(success: true, data: []));
|
|
|
|
$this->auditLogger
|
|
->shouldReceive('log')
|
|
->once()
|
|
->andReturn(new AuditLog);
|
|
|
|
$result = $this->service->restore(
|
|
$tenant,
|
|
'appProtectionPolicy',
|
|
$policyId,
|
|
$assignments,
|
|
[],
|
|
[],
|
|
null,
|
|
null,
|
|
null,
|
|
'#microsoft.graph.androidManagedAppProtection',
|
|
);
|
|
|
|
expect($result['summary']['success'])->toBe(1);
|
|
expect($result['summary']['failed'])->toBe(0);
|
|
expect($result['summary']['skipped'])->toBe(0);
|
|
});
|
|
|
|
it('maps assignment filter ids stored at the root of assignments', function () {
|
|
$tenant = Tenant::factory()->make([
|
|
'tenant_id' => 'tenant-123',
|
|
'app_client_id' => null,
|
|
'app_client_secret' => null,
|
|
]);
|
|
$policyId = 'policy-789';
|
|
$assignments = [
|
|
[
|
|
'id' => 'assignment-1',
|
|
'deviceAndAppManagementAssignmentFilterId' => 'filter-source',
|
|
'deviceAndAppManagementAssignmentFilterType' => 'include',
|
|
'target' => [
|
|
'@odata.type' => '#microsoft.graph.groupAssignmentTarget',
|
|
'groupId' => 'group-1',
|
|
],
|
|
],
|
|
];
|
|
$expectedAssignments = [
|
|
[
|
|
'deviceAndAppManagementAssignmentFilterId' => 'filter-target',
|
|
'deviceAndAppManagementAssignmentFilterType' => 'include',
|
|
'target' => [
|
|
'@odata.type' => '#microsoft.graph.groupAssignmentTarget',
|
|
'groupId' => 'group-1',
|
|
],
|
|
],
|
|
];
|
|
|
|
$this->graphClient
|
|
->shouldReceive('request')
|
|
->once()
|
|
->with('POST', "/deviceManagement/configurationPolicies/{$policyId}/assign", Mockery::on(
|
|
fn (array $options) => ($options['json']['assignments'] ?? null) === $expectedAssignments
|
|
))
|
|
->andReturn(new GraphResponse(success: true, data: []));
|
|
|
|
$this->auditLogger
|
|
->shouldReceive('log')
|
|
->once()
|
|
->andReturn(new AuditLog);
|
|
|
|
$result = $this->service->restore(
|
|
$tenant,
|
|
'settingsCatalogPolicy',
|
|
$policyId,
|
|
$assignments,
|
|
[],
|
|
[
|
|
'assignmentFilter' => [
|
|
'filter-source' => 'filter-target',
|
|
],
|
|
]
|
|
);
|
|
|
|
expect($result['summary']['success'])->toBe(1);
|
|
expect($result['summary']['failed'])->toBe(0);
|
|
expect($result['summary']['skipped'])->toBe(0);
|
|
});
|
|
|
|
it('keeps assignment filters when mapping is missing but filter exists in target', function () {
|
|
$tenant = Tenant::factory()->make([
|
|
'tenant_id' => 'tenant-123',
|
|
'app_client_id' => null,
|
|
'app_client_secret' => null,
|
|
]);
|
|
$policyId = 'policy-999';
|
|
$assignments = [
|
|
[
|
|
'id' => 'assignment-1',
|
|
'deviceAndAppManagementAssignmentFilterId' => 'filter-1',
|
|
'deviceAndAppManagementAssignmentFilterType' => 'include',
|
|
'target' => [
|
|
'@odata.type' => '#microsoft.graph.groupAssignmentTarget',
|
|
'groupId' => 'group-1',
|
|
],
|
|
],
|
|
];
|
|
|
|
$this->filterResolver
|
|
->shouldReceive('resolve')
|
|
->once()
|
|
->with(['filter-1'], $tenant)
|
|
->andReturn([['id' => 'filter-1', 'displayName' => 'Test']]);
|
|
|
|
$this->graphClient
|
|
->shouldReceive('request')
|
|
->once()
|
|
->with('POST', "/deviceManagement/configurationPolicies/{$policyId}/assign", Mockery::on(
|
|
fn (array $options) => ($options['json']['assignments'][0]['deviceAndAppManagementAssignmentFilterId'] ?? null) === 'filter-1'
|
|
))
|
|
->andReturn(new GraphResponse(success: true, data: []));
|
|
|
|
$this->auditLogger
|
|
->shouldReceive('log')
|
|
->once()
|
|
->andReturn(new AuditLog);
|
|
|
|
$result = $this->service->restore(
|
|
$tenant,
|
|
'settingsCatalogPolicy',
|
|
$policyId,
|
|
$assignments,
|
|
[],
|
|
[]
|
|
);
|
|
|
|
expect($result['summary']['success'])->toBe(1);
|
|
expect($result['summary']['failed'])->toBe(0);
|
|
expect($result['summary']['skipped'])->toBe(0);
|
|
});
|