@@ -159,3 +159,83 @@ def test_include_inherited_returns_parent_scope_assignments(self, mock_clients):
159159 )
160160
161161 assert len (result ) == 2
162+
163+ def test_include_inherited_false_filters_parent_scope (self , mock_clients ):
164+ """include_inherited=False filters out assignments at parent scopes."""
165+ assignments_client , definitions_client = mock_clients
166+ role_def_id = '/providers/Microsoft.Authorization/roleDefinitions/acdd72a7-3385-48ef-bd42-f606fba81ae7'
167+
168+ assignments_client .list_for_scope .return_value = [
169+ self ._create_assignment ('/' , role_def_id , 'principal-1' ),
170+ self ._create_assignment ('/subscriptions/sub1' , role_def_id , 'principal-2' ),
171+ ]
172+
173+ result = _search_role_assignments (
174+ assignments_client , definitions_client ,
175+ scope = '/subscriptions/sub1' ,
176+ assignee_object_id = None , role = None ,
177+ include_inherited = False , include_groups = False
178+ )
179+
180+ assert len (result ) == 1
181+ assert result [0 ].principal_id == 'principal-2'
182+
183+ def test_assignee_filter (self , mock_clients ):
184+ """assignee_object_id filters assignments by principal."""
185+ assignments_client , definitions_client = mock_clients
186+ role_def_id = '/providers/Microsoft.Authorization/roleDefinitions/acdd72a7-3385-48ef-bd42-f606fba81ae7'
187+
188+ assignments_client .list_for_scope .return_value = [
189+ self ._create_assignment ('/subscriptions/sub1' , role_def_id , 'principal-1' ),
190+ self ._create_assignment ('/subscriptions/sub1' , role_def_id , 'principal-2' ),
191+ ]
192+
193+ result = _search_role_assignments (
194+ assignments_client , definitions_client ,
195+ scope = '/subscriptions/sub1' ,
196+ assignee_object_id = 'principal-1' , role = None ,
197+ include_inherited = False , include_groups = False
198+ )
199+
200+ assert len (result ) == 1
201+ assert result [0 ].principal_id == 'principal-1'
202+
203+ def test_no_scope_uses_subscription_api (self , mock_clients ):
204+ """When scope is None, list_for_subscription is called and all assignments returned."""
205+ assignments_client , definitions_client = mock_clients
206+ role_def_id = '/providers/Microsoft.Authorization/roleDefinitions/acdd72a7-3385-48ef-bd42-f606fba81ae7'
207+
208+ assignments_client .list_for_subscription .return_value = [
209+ self ._create_assignment ('/subscriptions/sub1' , role_def_id , 'principal-1' ),
210+ self ._create_assignment ('/subscriptions/sub1/resourceGroups/rg1' , role_def_id , 'principal-2' ),
211+ ]
212+
213+ result = _search_role_assignments (
214+ assignments_client , definitions_client ,
215+ scope = None ,
216+ assignee_object_id = None , role = None ,
217+ include_inherited = False , include_groups = False
218+ )
219+
220+ assert len (result ) == 2
221+ assignments_client .list_for_subscription .assert_called_once ()
222+ assignments_client .list_for_scope .assert_not_called ()
223+
224+ def test_none_role_definition_id_is_skipped (self , mock_clients ):
225+ """Assignments with None role_definition_id are skipped when filtering by role."""
226+ assignments_client , definitions_client = mock_clients
227+ role_guid = 'acdd72a7-3385-48ef-bd42-f606fba81ae7'
228+
229+ assignment_with_none = self ._create_assignment ('/' , None )
230+ assignment_with_role = self ._create_assignment (
231+ '/' , f'/providers/Microsoft.Authorization/roleDefinitions/{ role_guid } ' )
232+ assignments_client .list_for_scope .return_value = [assignment_with_none , assignment_with_role ]
233+
234+ result = _search_role_assignments (
235+ assignments_client , definitions_client ,
236+ scope = '/' , assignee_object_id = None , role = role_guid ,
237+ include_inherited = False , include_groups = False
238+ )
239+
240+ assert len (result ) == 1
241+ assert result [0 ].role_definition_id is not None
0 commit comments