I have an old test created by my predecessor which is checking that a user with a read only profile should fail to update records. After I made changes to my project, this test fails. The user with a read only profile succeeds in updating records. And I cannot deploy my changes to production because of this failure. I checked the code, profile, and debug logs and I do not understand how it is possible. Does anyone have any idea ?
EDIT 1
This test is using the following command in the code:
System.runAs(u)
Does anyone know if System.runAs(u) changed and now ignore all user permissions and profile during the last winter release?
Here are the profile image, code, and debug logs:
Read only profile:
Test code:
static testMethod void dmlErroTest()
{
String rTypeId;
//get the faculty record type for contact
List<RecordType> rTypes = [Select SobjectType, Name, Id From RecordType Where SobjectType = 'Contact' AND Name = 'Faculty'];
if(rTypes.size() < 1)
{
System.assertEquals(false, true);
}
else
{
rTypeId = rTypes[0].Id;
}
//create the schedule
Academic_Schedule__c schedule = new Academic_Schedule__c(Name = '2008-2009');
insert schedule;
//create a faculty contact
Contact faculty = new Contact(
LastName='Doe',
RecordTypeId = rTypeId
);
insert faculty;
//create courses
List<Course__c> crs = new List<Course__c>();
for(integer i = 0; i < 5; i++)
{
Course__c c;
if( math.mod(i , 2) == 0)
{
c = new Course__c(Academic_Schedule__c = schedule.Id, Section_Number__c = 'D '+i, Faculty__c = null, Unmatched_Faculty_Name__c = 'Doe, John', Faculty_USC_Id__c = '9999999999');
}
else
{
c = new Course__c(Academic_Schedule__c = schedule.Id, Section_Number__c = 'D '+i, Faculty__c = null, Unmatched_Faculty_Name__c = 'Smith, Jane', Faculty_USC_Id__c = '8888888888');
}
crs.add(c);
}
insert crs;
for(Course__c c : [SELECT Id, Unmatched_Faculty_Name__c, Faculty_USC_Id__c, Faculty__c
FROM Course__c
WHERE Unmatched_Faculty_Name__c = 'Doe, John'])
{
System.assertEquals(c.Faculty_USC_Id__c, '9999999999');
System.assertEquals(c.Faculty__c, null);
}
PageReference pageRef = Page.MassUpdateUnmatchedFaculty;
Test.setCurrentPage(pageRef);
FacultyMatch matcher = new FacultyMatch();
System.assert(matcher.Course.Id == null);
System.assert(matcher.Courses.size() > 3);
ApexPages.currentPage().getParameters().put('id', crs[0].Id);
matcher = new FacultyMatch();
System.assert(matcher.Courses.size() == 3);
System.assert(matcher.Course.Id == crs[0].Id);
System.assert(matcher.Course.Id != null);
System.assert(matcher.searchText == 'Doe, John');
for(Course__c c : matcher.Courses)
{
System.assertEquals(c.Faculty_USC_Id__c, '9999999999');
System.assertEquals(c.Unmatched_Faculty_Name__c, 'Doe, John');
System.assertEquals(c.Faculty__c, null);
}
//set the controlling course faculty id before update call doUpdate
matcher.Course.Faculty__c = faculty.Id;
// This code runs as the readonly user
Profile p = [SELECT Id FROM Profile WHERE Name='Read Only'];
System.debug('profile = ' + p);
User u = new User( Alias = 'standt', Email='standarduser@testorg.com',
EmailEncodingKey='UTF-8', LastName='Testing', LanguageLocaleKey='en_US',
LocaleSidKey='en_US', ProfileId = p.Id,
TimeZoneSidKey='America/Los_Angeles', UserName='stduser@testorg.com');
System.Assert(ApexPages.hasMessages(ApexPages.Severity.ERROR) == false);
System.runAs(u)
{
matcher.doUpdate();
}
System.debug('User = ' + u);
System.debug('ApexPages message: ' + Apexpages.getMessages().get(0).getSummary() );
System.debug('ApexPages.hasMessages(ApexPages.Severity.ERROR) = ' + ApexPages.hasMessages(ApexPages.Severity.ERROR) );
System.Assert(ApexPages.hasMessages(ApexPages.Severity.ERROR) == true);
System.Assert(Apexpages.getMessages().get(0).getSummary().contains('Insufficient Privileges')== true);
}
Function tested in code
public PageReference doUpdate()
{
try
{
if(course.Unmatched_Faculty_Name__c != null && course.Faculty__c != null && course.Faculty_USC_Id__c != null)
{
Contact c = new Contact(Id = course.Faculty__c, Faculty_USC_Id__c = course.Faculty_USC_Id__c);
update c;
//debug
UserRecordAccess ac = [SELECT RecordId, HasEditAccess
FROM UserRecordAccess
WHERE UserId = :UserInfo.getUserId()
AND RecordId = :c.Id ];
System.debug('after updating Contact, UserRecordAccess = ' + ac);
for(Course__c crs : Courses )
{
crs.Faculty__c = course.Faculty__c;
crs.Faculty_USC_Id__c = null;
crs.Unmatched_Faculty_Name__c = null;
//debug
ac = [SELECT RecordId, HasEditAccess
FROM UserRecordAccess
WHERE UserId = :UserInfo.getUserId()
AND RecordId = :crs.Id ];
System.debug('Before updating courses, course = ' + crs + ' UserRecordAccess = ' + ac );
}
update Courses;
//debug
System.debug('After updating courses');
ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.Info, 'The faculty record was updated with the usc id '+ c.Faculty_USC_Id__c ));
ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.Info, Courses.size()+ ' courses were updated with the selected faculty'));
doClear();
return null;
}
else
{
throw new FacultyMatchException('Please ensure that you select and unmatched faculty name and a faculty to which the records should be matched.');
}
}
catch(DmlException ex)
{
ApexPages.addMessages(ex);
}
catch(FacultyMatchException e)
{
ApexPages.addMessages(e);
}
return null;
}
Debug logs after test run : link
Best Answer
A complete shot in the dark, but I would guess someone has created a new Profile in production called 'Read Only' that is being returned before the default system profile.
E.g. You can create a new Profile with the same name as the system 'Read Only' Profile.
To tell them apart you would need to use the Organization.CreatedDate to find the System created on. E.g.
From the log, it appears that this line in the test case is causing the failure.
For this assertion to pass the code would need to call
ApexPages.addMessages(ex)
(where ex is an exception).I see the check against
UserRecordAccess.HasEditAccess
for the Contact comes back as false. Same with the HasEditAccess values for all the Course__c records.So it does seem really odd that you can then turn round and update those records from an apex update. Assuming you have
with sharing
on, the next thing that comes to mind is the "Modify All Data" Administrative Permissions. However, I don't think you can change that on the built in Read Only role.It doesn't appear to be the case, but it is worth confirming the the active user isn't the owner of the record you are updating.