-
Notifications
You must be signed in to change notification settings - Fork 3.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
* Add DeleteInstructorActionTest * Remove AtomicReference * Separate logged out access control test * Refine tests * Improve test methods * Add test specific access control for unregistered user --------- Co-authored-by: Jason Qiu <[email protected]> Co-authored-by: domoberzin <[email protected]>
- Loading branch information
1 parent
054280d
commit c72ca17
Showing
1 changed file
with
358 additions
and
0 deletions.
There are no files selected for viewing
358 changes: 358 additions & 0 deletions
358
src/test/java/teammates/sqlui/webapi/DeleteInstructorActionTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,358 @@ | ||
package teammates.sqlui.webapi; | ||
|
||
import static org.mockito.ArgumentMatchers.any; | ||
import static org.mockito.Mockito.never; | ||
import static org.mockito.Mockito.times; | ||
import static org.mockito.Mockito.verify; | ||
import static org.mockito.Mockito.when; | ||
|
||
import java.util.List; | ||
|
||
import org.mockito.Mockito; | ||
import org.testng.annotations.BeforeMethod; | ||
import org.testng.annotations.Test; | ||
|
||
import teammates.common.datatransfer.InstructorPrivileges; | ||
import teammates.common.util.Const; | ||
import teammates.storage.sqlentity.Account; | ||
import teammates.storage.sqlentity.Course; | ||
import teammates.storage.sqlentity.Instructor; | ||
import teammates.storage.sqlentity.Student; | ||
import teammates.ui.output.MessageOutput; | ||
import teammates.ui.webapi.DeleteInstructorAction; | ||
import teammates.ui.webapi.InvalidOperationException; | ||
|
||
/** | ||
* SUT: {@link DeleteInstructorAction}. | ||
*/ | ||
public class DeleteInstructorActionTest extends BaseActionTest<DeleteInstructorAction> { | ||
|
||
private Course course; | ||
private Instructor instructor; | ||
private Instructor instructor2; | ||
private Student student; | ||
private String studentId = "student-googleId"; | ||
|
||
@Override | ||
protected String getActionUri() { | ||
return Const.ResourceURIs.INSTRUCTOR; | ||
} | ||
|
||
@Override | ||
protected String getRequestMethod() { | ||
return DELETE; | ||
} | ||
|
||
@BeforeMethod | ||
void setUp() { | ||
Mockito.reset(mockLogic); | ||
|
||
course = getTypicalCourse(); | ||
instructor = setupInstructor("instructor-googleId", "[email protected]"); | ||
instructor2 = setupInstructor("instructor2-googleId", "[email protected]"); | ||
student = getTypicalStudent(); | ||
|
||
setupMockLogic(); | ||
} | ||
|
||
private Instructor setupInstructor(String googleId, String email) { | ||
Account account = getTypicalAccount(); | ||
account.setGoogleId(googleId); | ||
account.setEmail(email); | ||
|
||
Instructor instructor = getTypicalInstructor(); | ||
instructor.setEmail(email); | ||
instructor.setDisplayedToStudents(true); | ||
instructor.setAccount(account); | ||
|
||
return instructor; | ||
} | ||
|
||
private void setupMockLogic() { | ||
when(mockLogic.getCourse(course.getId())).thenReturn(course); | ||
when(mockLogic.getInstructorByGoogleId(course.getId(), instructor.getGoogleId())).thenReturn(instructor); | ||
when(mockLogic.getInstructorByGoogleId(course.getId(), instructor2.getGoogleId())).thenReturn(instructor2); | ||
when(mockLogic.getStudentByGoogleId(course.getId(), studentId)).thenReturn(student); | ||
when(mockLogic.getInstructorForEmail(course.getId(), instructor.getEmail())).thenReturn(instructor); | ||
when(mockLogic.getInstructorForEmail(course.getId(), instructor2.getEmail())).thenReturn(instructor2); | ||
when(mockLogic.getInstructorsByCourse(course.getId())).thenReturn(List.of(instructor, instructor2)); | ||
when(mockLogic.getStudentsForCourse(course.getId())).thenReturn(List.of(student)); | ||
} | ||
|
||
@Test | ||
void testExecute_deleteInstructorByGoogleId_success() { | ||
String[] params = { | ||
Const.ParamsNames.COURSE_ID, course.getId(), | ||
Const.ParamsNames.INSTRUCTOR_ID, instructor2.getGoogleId(), | ||
}; | ||
|
||
DeleteInstructorAction action = getAction(params); | ||
MessageOutput actionOutput = (MessageOutput) getJsonResult(action).getOutput(); | ||
|
||
verify(mockLogic, times(1)).getInstructorByGoogleId(course.getId(), instructor2.getGoogleId()); | ||
verify(mockLogic, times(1)).deleteInstructorCascade(course.getId(), instructor2.getEmail()); | ||
verify(mockLogic, times(1)).deleteInstructorCascade(any(), any()); | ||
assertEquals("Instructor is successfully deleted.", actionOutput.getMessage()); | ||
} | ||
|
||
@Test | ||
void testExecute_deleteInstructorByEmail_success() { | ||
String[] params = { | ||
Const.ParamsNames.COURSE_ID, course.getId(), | ||
Const.ParamsNames.INSTRUCTOR_EMAIL, instructor2.getEmail(), | ||
}; | ||
|
||
DeleteInstructorAction action = getAction(params); | ||
MessageOutput actionOutput = (MessageOutput) getJsonResult(action).getOutput(); | ||
|
||
verify(mockLogic, times(1)).getInstructorForEmail(course.getId(), instructor2.getEmail()); | ||
verify(mockLogic, times(1)).deleteInstructorCascade(course.getId(), instructor2.getEmail()); | ||
verify(mockLogic, times(1)).deleteInstructorCascade(any(), any()); | ||
assertEquals("Instructor is successfully deleted.", actionOutput.getMessage()); | ||
} | ||
|
||
@Test | ||
void testExecute_onlyOneInstructorInCourse_throwsInvalidOperationException() { | ||
// Override the mock logic for the course to have only one instructor | ||
when(mockLogic.getInstructorsByCourse(course.getId())).thenReturn(List.of(instructor)); | ||
|
||
String[] params = { | ||
Const.ParamsNames.COURSE_ID, course.getId(), | ||
Const.ParamsNames.INSTRUCTOR_ID, instructor.getGoogleId(), | ||
}; | ||
|
||
assertEquals(mockLogic.getInstructorsByCourse(course.getId()).size(), 1); | ||
|
||
InvalidOperationException ioe = verifyInvalidOperation(params); | ||
assertEquals("The instructor you are trying to delete is the last instructor in the course. " | ||
+ "Deleting the last instructor from the course is not allowed.", ioe.getMessage()); | ||
|
||
verify(mockLogic, times(1)).getInstructorByGoogleId(course.getId(), instructor.getGoogleId()); | ||
verify(mockLogic, never()).deleteInstructorCascade(any(), any()); | ||
} | ||
|
||
@Test | ||
void testExecute_onlyOneRegisteredInstructor_throwsInvalidOperationException() { | ||
instructor2.setAccount(null); | ||
|
||
String[] params = { | ||
Const.ParamsNames.COURSE_ID, course.getId(), | ||
Const.ParamsNames.INSTRUCTOR_ID, instructor.getGoogleId(), | ||
}; | ||
|
||
InvalidOperationException ioe = verifyInvalidOperation(params); | ||
assertEquals("The instructor you are trying to delete is the last instructor in the course. " | ||
+ "Deleting the last instructor from the course is not allowed.", ioe.getMessage()); | ||
|
||
verify(mockLogic, times(1)).getInstructorByGoogleId(course.getId(), instructor.getGoogleId()); | ||
verify(mockLogic, never()).deleteInstructorCascade(any(), any()); | ||
} | ||
|
||
@Test | ||
void testExecute_onlyOneInstructorDisplayedToStudents_throwsInvalidOperationException() { | ||
instructor2.setDisplayedToStudents(false); | ||
|
||
String[] params = { | ||
Const.ParamsNames.COURSE_ID, course.getId(), | ||
Const.ParamsNames.INSTRUCTOR_ID, instructor.getGoogleId(), | ||
}; | ||
|
||
InvalidOperationException ioe = verifyInvalidOperation(params); | ||
assertEquals("The instructor you are trying to delete is the last instructor in the course. " | ||
+ "Deleting the last instructor from the course is not allowed.", ioe.getMessage()); | ||
|
||
verify(mockLogic, times(1)).getInstructorByGoogleId(course.getId(), instructor.getGoogleId()); | ||
verify(mockLogic, never()).deleteInstructorCascade(any(), any()); | ||
} | ||
|
||
@Test | ||
void testExecute_instructorDeleteOwnRoleByGoogleId_success() { | ||
loginAsInstructor(instructor.getGoogleId()); | ||
|
||
String[] params = { | ||
Const.ParamsNames.COURSE_ID, course.getId(), | ||
Const.ParamsNames.INSTRUCTOR_ID, instructor.getGoogleId(), | ||
}; | ||
|
||
DeleteInstructorAction action = getAction(params); | ||
MessageOutput actionOutput = (MessageOutput) getJsonResult(action).getOutput(); | ||
|
||
verify(mockLogic, times(1)).getInstructorByGoogleId(course.getId(), instructor.getGoogleId()); | ||
verify(mockLogic, times(1)).deleteInstructorCascade(course.getId(), instructor.getEmail()); | ||
verify(mockLogic, times(1)).deleteInstructorCascade(any(), any()); | ||
assertEquals("Instructor is successfully deleted.", actionOutput.getMessage()); | ||
} | ||
|
||
@Test | ||
void testExecute_deleteNonExistentInstructorByGoogleId_failSilently() { | ||
when(mockLogic.getInstructorByGoogleId(course.getId(), "fake-googleId")).thenReturn(null); | ||
|
||
String[] params = { | ||
Const.ParamsNames.COURSE_ID, course.getId(), | ||
Const.ParamsNames.INSTRUCTOR_ID, "fake-googleId", | ||
}; | ||
|
||
DeleteInstructorAction action = getAction(params); | ||
MessageOutput actionOutput = (MessageOutput) getJsonResult(action).getOutput(); | ||
|
||
verify(mockLogic, times(1)).getInstructorByGoogleId(course.getId(), "fake-googleId"); | ||
verify(mockLogic, never()).deleteInstructorCascade(any(), any()); | ||
assertEquals("Instructor is successfully deleted.", actionOutput.getMessage()); | ||
} | ||
|
||
@Test | ||
void testExecute_deleteNonExistentInstructorByEmail_failSilently() { | ||
String fakeInstructorEmail = "[email protected]"; | ||
when(mockLogic.getInstructorForEmail(course.getId(), fakeInstructorEmail)).thenReturn(null); | ||
|
||
String[] params = { | ||
Const.ParamsNames.COURSE_ID, course.getId(), | ||
Const.ParamsNames.INSTRUCTOR_EMAIL, fakeInstructorEmail, | ||
}; | ||
|
||
DeleteInstructorAction action = getAction(params); | ||
MessageOutput actionOutput = (MessageOutput) getJsonResult(action).getOutput(); | ||
|
||
verify(mockLogic, times(1)).getInstructorForEmail(course.getId(), fakeInstructorEmail); | ||
verify(mockLogic, never()).deleteInstructorCascade(any(), any()); | ||
assertEquals("Instructor is successfully deleted.", actionOutput.getMessage()); | ||
} | ||
|
||
@Test | ||
void testExecute_nonExistentCourse_failSilently() { | ||
String nonExistentCourseId = "non-existent-course-id"; | ||
when(mockLogic.getCourse(nonExistentCourseId)).thenReturn(null); | ||
|
||
String[] params = { | ||
Const.ParamsNames.COURSE_ID, nonExistentCourseId, | ||
Const.ParamsNames.INSTRUCTOR_ID, instructor.getGoogleId(), | ||
}; | ||
|
||
DeleteInstructorAction action = getAction(params); | ||
MessageOutput actionOutput = (MessageOutput) getJsonResult(action).getOutput(); | ||
|
||
verify(mockLogic, times(1)).getInstructorByGoogleId(nonExistentCourseId, instructor.getGoogleId()); | ||
verify(mockLogic, never()).deleteInstructorCascade(any(), any()); | ||
assertEquals("Instructor is successfully deleted.", actionOutput.getMessage()); | ||
} | ||
|
||
@Test | ||
void testExecute_noParameters_throwsInvalidHttpParameterException() { | ||
verifyHttpParameterFailure(); | ||
} | ||
|
||
@Test | ||
void testExecute_missingCourseIdWithInstructorId_throwsInvalidHttpParameterException() { | ||
String[] params = { | ||
Const.ParamsNames.INSTRUCTOR_ID, instructor.getGoogleId(), | ||
}; | ||
|
||
verifyHttpParameterFailure(params); | ||
} | ||
|
||
@Test | ||
void testExecute_missingCourseIdWithInstructorEmail_throwsInvalidHttpParameterException() { | ||
String[] params = { | ||
Const.ParamsNames.INSTRUCTOR_EMAIL, instructor.getEmail(), | ||
}; | ||
|
||
verifyHttpParameterFailure(params); | ||
} | ||
|
||
@Test | ||
void testExecute_onlyCourseId_throwsInvalidHttpParameterException() { | ||
String[] params = { | ||
Const.ParamsNames.COURSE_ID, course.getId(), | ||
}; | ||
|
||
verifyHttpParameterFailure(params); | ||
} | ||
|
||
@Test | ||
void testSpecificAccessControl_admin_canAccess() { | ||
loginAsAdmin(); | ||
|
||
String[] params = { | ||
Const.ParamsNames.COURSE_ID, course.getId(), | ||
Const.ParamsNames.INSTRUCTOR_ID, instructor.getGoogleId(), | ||
}; | ||
|
||
verifyCanAccess(params); | ||
} | ||
|
||
@Test | ||
void testSpecificAccessControl_instructorWithPermission_canAccess() { | ||
loginAsInstructor(instructor.getGoogleId()); | ||
|
||
String[] params = { | ||
Const.ParamsNames.COURSE_ID, course.getId(), | ||
Const.ParamsNames.INSTRUCTOR_ID, instructor2.getGoogleId(), | ||
}; | ||
|
||
verifyCanAccess(params); | ||
} | ||
|
||
@Test | ||
void testSpecificAccessControl_instructorWithInvalidPermission_cannotAccess() { | ||
InstructorPrivileges instructorPrivileges = new InstructorPrivileges(); | ||
instructorPrivileges.updatePrivilege(Const.InstructorPermissions.CAN_MODIFY_INSTRUCTOR, false); | ||
instructor.setPrivileges(instructorPrivileges); | ||
|
||
loginAsInstructor(instructor.getGoogleId()); | ||
|
||
String[] params = { | ||
Const.ParamsNames.COURSE_ID, course.getId(), | ||
Const.ParamsNames.INSTRUCTOR_ID, instructor2.getGoogleId(), | ||
}; | ||
|
||
verifyCannotAccess(params); | ||
} | ||
|
||
@Test | ||
void testSpecificAccessControl_instructorInDifferentCourse_cannotAccess() { | ||
loginAsInstructor("instructor3-googleId"); | ||
|
||
String[] params = { | ||
Const.ParamsNames.COURSE_ID, course.getId(), | ||
Const.ParamsNames.INSTRUCTOR_ID, instructor.getGoogleId(), | ||
}; | ||
|
||
verifyCannotAccess(params); | ||
} | ||
|
||
@Test | ||
void testSpecificAccessControl_student_cannotAccess() { | ||
loginAsStudent(studentId); | ||
|
||
String[] params = { | ||
Const.ParamsNames.COURSE_ID, course.getId(), | ||
Const.ParamsNames.INSTRUCTOR_ID, instructor.getGoogleId(), | ||
}; | ||
|
||
verifyCannotAccess(params); | ||
} | ||
|
||
@Test | ||
void testSpecificAccessControl_loggedOut_cannotAccess() { | ||
logoutUser(); | ||
|
||
String[] params = { | ||
Const.ParamsNames.COURSE_ID, course.getId(), | ||
Const.ParamsNames.INSTRUCTOR_ID, instructor.getGoogleId(), | ||
}; | ||
|
||
verifyCannotAccess(params); | ||
} | ||
|
||
@Test | ||
void testSpecificAccessControl_unregistered_cannotAccess() { | ||
loginAsUnregistered(instructor.getGoogleId()); | ||
|
||
String[] params = { | ||
Const.ParamsNames.COURSE_ID, course.getId(), | ||
Const.ParamsNames.INSTRUCTOR_ID, instructor.getGoogleId(), | ||
}; | ||
|
||
verifyCannotAccess(params); | ||
} | ||
} |