create([ 'tenant_id' => 'tenant-123', 'external_id' => 'tenant-123', ]); ensureDefaultProviderConnection($tenant, 'microsoft'); $backupItem = BackupItem::factory()->create([ 'tenant_id' => $tenant->id, 'metadata' => [], 'assignments' => null, ]); $policyPayload = [ 'roleScopeTagIds' => ['0'], ]; $this->mock(AssignmentFetcher::class, function (MockInterface $mock) { $mock->shouldReceive('supportsStandardAssignments') ->once() ->with('settingsCatalogPolicy', null) ->andReturnTrue(); $mock->shouldReceive('fetch') ->once() ->andReturn([ [ 'id' => 'assignment-1', 'intent' => 'apply', 'deviceAndAppManagementAssignmentFilterId' => 'filter-123', 'deviceAndAppManagementAssignmentFilterType' => 'include', 'target' => [ '@odata.type' => '#microsoft.graph.groupAssignmentTarget', 'groupId' => 'group-123', ], ], ]); }); $this->mock(GroupResolver::class, function (MockInterface $mock) { $mock->shouldReceive('resolveGroupIds') ->once() ->andReturn([ 'group-123' => [ 'id' => 'group-123', 'displayName' => 'Test Group', 'orphaned' => false, ], ]); }); $this->mock(AssignmentFilterResolver::class, function (MockInterface $mock) use ($tenant) { $mock->shouldReceive('resolve') ->once() ->with(['filter-123'], $tenant) ->andReturn([ ['id' => 'filter-123', 'displayName' => 'Targeted Devices'], ]); }); $this->mock(ScopeTagResolver::class, function (MockInterface $mock) use ($tenant) { $mock->shouldReceive('resolve') ->once() ->with(['0'], $tenant) ->andReturn([ ['id' => '0', 'displayName' => 'Default'], ]); }); $service = app(AssignmentBackupService::class); $updated = $service->enrichWithAssignments( backupItem: $backupItem, tenant: $tenant, policyType: 'settingsCatalogPolicy', policyId: 'policy-123', policyPayload: $policyPayload, includeAssignments: true ); expect($updated->assignments)->toHaveCount(1) ->and($updated->assignments[0]['target']['deviceAndAppManagementAssignmentFilterId'])->toBe('filter-123') ->and($updated->assignments[0]['target']['deviceAndAppManagementAssignmentFilterType'])->toBe('include') ->and($updated->assignments[0]['target']['assignment_filter_name'])->toBe('Targeted Devices'); }); it('marks role definitions as not using standard policy assignments', function () { $tenant = Tenant::factory()->create([ 'tenant_id' => 'tenant-123', 'external_id' => 'tenant-123', ]); ensureDefaultProviderConnection($tenant, 'microsoft'); $backupItem = BackupItem::factory()->create([ 'tenant_id' => $tenant->id, 'metadata' => [ 'assignments_fetch_failed' => true, 'assignments_fetch_error' => 'old error', ], 'assignments' => null, ]); $this->mock(AssignmentFetcher::class, function (MockInterface $mock) { $mock->shouldReceive('supportsStandardAssignments') ->once() ->with('intuneRoleDefinition', '#microsoft.graph.roleDefinition') ->andReturnFalse(); $mock->shouldReceive('fetch')->never(); }); $this->mock(ScopeTagResolver::class, function (MockInterface $mock) use ($tenant) { $mock->shouldReceive('resolve') ->once() ->with(['0'], $tenant) ->andReturn([ ['id' => '0', 'displayName' => 'Default'], ]); }); $this->mock(GroupResolver::class, function (MockInterface $mock) { $mock->shouldReceive('resolveGroupIds')->never(); }); $this->mock(AssignmentFilterResolver::class, function (MockInterface $mock) { $mock->shouldReceive('resolve')->never(); }); $service = app(AssignmentBackupService::class); $updated = $service->enrichWithAssignments( backupItem: $backupItem, tenant: $tenant, policyType: 'intuneRoleDefinition', policyId: 'role-def-1', policyPayload: [ '@odata.type' => '#microsoft.graph.roleDefinition', 'roleScopeTagIds' => ['0'], ], includeAssignments: true, ); expect($updated->assignments)->toBe([]) ->and(data_get($updated->metadata, 'assignments_not_applicable'))->toBeTrue() ->and(data_get($updated->metadata, 'assignment_capture_reason'))->toBe('separate_role_assignments') ->and(data_get($updated->metadata, 'assignments_fetch_failed'))->toBeFalse() ->and(data_get($updated->metadata, 'assignments_fetch_error'))->toBeNull(); });