create(); ensureDefaultProviderConnection($tenant, 'microsoft'); $roleDefinitions = [ [ 'id' => 'role-def-1', 'templateId' => '62e90394-69f5-4237-9190-012177145e10', 'displayName' => 'Global Administrator', 'isBuiltIn' => true, ], ]; $roleAssignments = [ [ 'id' => 'role-assign-1', 'roleDefinitionId' => 'role-def-1', 'principalId' => 'principal-1', 'directoryScopeId' => '/', 'principal' => [ '@odata.type' => '#microsoft.graph.user', 'displayName' => 'Ada Lovelace', ], ], ]; $calls = []; $this->mock(GraphClientInterface::class, function ($mock) use (&$calls, $roleDefinitions, $roleAssignments) { $mock->shouldReceive('listPolicies') ->twice() ->andReturnUsing(function (string $policyType, array $options) use (&$calls, $roleDefinitions, $roleAssignments): GraphResponse { $calls[] = [$policyType, $options]; if ($policyType === 'entraRoleDefinitions') { return new GraphResponse(true, $roleDefinitions); } return new GraphResponse(true, $roleAssignments); }); }); $result = app(EntraAdminRolesReportService::class)->generate($tenant); expect($calls)->toHaveCount(2); expect($calls[0][0])->toBe('entraRoleDefinitions'); expect($calls[0][1])->not->toHaveKey('expand'); expect($calls[1][0])->toBe('entraRoleAssignments'); expect($calls[1][1]['expand'] ?? null)->toBe('principal'); expect($calls[1][1])->toMatchArray(array_merge($calls[0][1], [ 'expand' => 'principal', ])); expect($result->payload['high_privilege'])->toHaveCount(1); expect($result->payload['high_privilege'][0]['principal_display_name'])->toBe('Ada Lovelace'); }); it('falls back to Unknown when principal details are missing upstream', function () { $tenant = Tenant::factory()->create(); ensureDefaultProviderConnection($tenant, 'microsoft'); $roleDefinitions = [ [ 'id' => 'role-def-1', 'templateId' => '62e90394-69f5-4237-9190-012177145e10', 'displayName' => 'Global Administrator', 'isBuiltIn' => true, ], ]; $roleAssignments = [ [ 'id' => 'role-assign-1', 'roleDefinitionId' => 'role-def-1', 'principalId' => 'principal-1', 'directoryScopeId' => '/', ], ]; $calls = []; $this->mock(GraphClientInterface::class, function ($mock) use (&$calls, $roleDefinitions, $roleAssignments) { $mock->shouldReceive('listPolicies') ->twice() ->andReturnUsing(function (string $policyType, array $options) use (&$calls, $roleDefinitions, $roleAssignments): GraphResponse { $calls[] = [$policyType, $options]; if ($policyType === 'entraRoleDefinitions') { return new GraphResponse(true, $roleDefinitions); } return new GraphResponse(true, $roleAssignments); }); }); $result = app(EntraAdminRolesReportService::class)->generate($tenant); expect($calls)->toHaveCount(2); expect($calls[0][0])->toBe('entraRoleDefinitions'); expect($calls[0][1])->not->toHaveKey('expand'); expect($calls[1][0])->toBe('entraRoleAssignments'); expect($calls[1][1]['expand'] ?? null)->toBe('principal'); expect($result->payload['high_privilege'])->toHaveCount(1); expect($result->payload['high_privilege'][0]['principal_display_name'])->toBe('Unknown'); });