Below is a test class I wrote that reproduces the issue. In this test class I use the Macro sobject, but the problem applies to other objects as well. I first noticed it on a custom object.
@isTest
public without sharing class UserRecordAccessTest {
private static testMethod void test(){
//Create the users that will own the records
final User[] users = createStandardUsers(2);
final User u1 = users[0];
final User u2 = users[1];
//Create the records, each owned by a different user
final SObject o1 = createRecord(u1.Id);
final SObject o2 = createRecord(u2.Id);
insert new SObject[]{o1, o2};
system.runAs(u1){
//Query for each UserRecordAccess entry individually
final UserRecordAccess access1 = queryForSingleUserRecordAccess(u1.Id, o1.Id);
final UserRecordAccess access2 = queryForSingleUserRecordAccess(u1.Id, o2.Id);
//Query for both UserRecordAccess entries at the same time
final UserRecordAccess[] accessList = queryForUserRecordAccesses(u1.Id, new Set<Id>{o1.Id, o2.Id});
//The UserRecordAccess entries should have the same values, regardless of how they were queried
compareUserRecordAccesses(access1, accessList[0]);//This fails because the HasAllAccess value is false for the entry queried individually, but is true for the entry queried in a list
compareUserRecordAccesses(access2, accessList[1]);
}
}
private static UserRecordAccess queryForSingleUserRecordAccess(final Id userId, final Id recordId){
return [
SELECT RecordId, HasReadAccess, HasEditAccess, HasDeleteAccess, HasTransferAccess, HasAllAccess
FROM UserRecordAccess
WHERE UserId = :userId AND RecordId = :recordId
];
}
private static UserRecordAccess[] queryForUserRecordAccesses(final Id userId, final Set<Id> recordIds){
return [
SELECT RecordId, HasReadAccess, HasEditAccess, HasDeleteAccess, HasTransferAccess, HasAllAccess
FROM UserRecordAccess
WHERE UserId = :userId AND RecordId IN :recordIds
ORDER BY RecordId
];
}
private static void compareUserRecordAccesses(final UserRecordAccess expected, final UserRecordAccess actual){
system.assertEquals(expected.RecordId, actual.RecordId);
system.assertEquals(expected.HasReadAccess, actual.HasReadAccess);
system.assertEquals(expected.HasEditAccess, actual.HasEditAccess);
system.assertEquals(expected.HasDeleteAccess, actual.HasDeleteAccess);
system.assertEquals(expected.HasTransferAccess, actual.HasTransferAccess);
system.assertEquals(expected.HasAllAccess, actual.HasAllAccess);
}
private static User[] createStandardUsers(final Integer count){
final Profile standardUser = [SELECT Id FROM Profile WHERE Name = 'Standard User'];
final User[] users = new User[]{};
for(Integer i = 0; i < count; i++){
users.add(new User(
UserName = 'test' + i + '@userrecordaccesstest.com',
LastName = 'Smith',
Email = 'test@user.com',
Alias = 'test',
CommunityNickname = 'test' + i,
TimeZoneSidKey = 'America/Indiana/Indianapolis',
LocaleSidKey = 'en_US',
EmailEncodingKey = 'UTF-8',
LanguageLocaleKey = 'en_US',
ProfileId = standardUser.Id
));
}
insert users;
return users;
}
private static SObject createRecord(final Id ownerId){
return new Macro(Name = 'Macro' + ownerId, OwnerId = ownerId);
}
}