class CoursesAPITests(ACJAPITestCase): def setUp(self): super(CoursesAPITests, self).setUp() self.data = BasicTestData() def _verify_course_info(self, course_expected, course_actual): self.assertEqual(course_expected.name, course_actual['name'], "Expected course name does not match actual.") self.assertEqual(course_expected.id, course_actual['id'], "Expected course id does not match actual.") self.assertTrue(course_expected.criteriaandcourses, "Course is missing a criteria") def test_get_single_course(self): course_api_url = '/api/courses/' + str(self.data.get_course().id) # Test login required rv = self.client.get(course_api_url) self.assert401(rv) # Test root get course with self.login('root'): rv = self.client.get(course_api_url) self.assert200(rv) self._verify_course_info(self.data.get_course(), rv.json) # Test enroled users get course info with self.login(self.data.get_authorized_instructor().username): rv = self.client.get(course_api_url) self.assert200(rv) self._verify_course_info(self.data.get_course(), rv.json) with self.login(self.data.get_authorized_student().username): rv = self.client.get(course_api_url) self.assert200(rv) self._verify_course_info(self.data.get_course(), rv.json) # Test unenroled user not permitted to get info with self.login(self.data.get_unauthorized_instructor().username): rv = self.client.get(course_api_url) self.assert403(rv) with self.login(self.data.get_unauthorized_student().username): rv = self.client.get(course_api_url) self.assert403(rv) # Test get invalid course with self.login("root"): rv = self.client.get('/api/courses/38940450') self.assert404(rv) def test_get_all_courses(self): course_api_url = '/api/courses' # Test login required rv = self.client.get(course_api_url) self.assert401(rv) # Test only root can get a list of all courses with self.login(self.data.get_authorized_instructor().username): rv = self.client.get(course_api_url) self.assert403(rv) with self.login(self.data.get_authorized_student().username): rv = self.client.get(course_api_url) self.assert403(rv) with self.login("root"): rv = self.client.get(course_api_url) self.assert200(rv) self._verify_course_info(self.data.get_course(), rv.json['objects'][0]) def test_create_course(self): course_expected = { 'name': 'ExpectedCourse1', 'description': 'Test Course One Description Test' } # Test login required rv = self.client.post('/api/courses', data=json.dumps(course_expected), content_type='application/json') self.assert401(rv) # Test unauthorized user with self.login(self.data.get_authorized_student().username): rv = self.client.post('/api/courses', data=json.dumps(course_expected), content_type='application/json') self.assert403(rv) # Test course creation with self.login(self.data.get_authorized_instructor().username): rv = self.client.post('/api/courses', data=json.dumps(course_expected), content_type='application/json') self.assert200(rv) # Verify return course_actual = rv.json self.assertEqual(course_expected['name'], course_actual['name']) self.assertEqual(course_expected['description'], course_actual['description']) # Verify the course is created in db course_in_db = Courses.query.get(course_actual['id']) self.assertEqual(course_in_db.name, course_actual['name']) self.assertEqual(course_in_db.description, course_actual['description']) # create course with criteria course = course_expected.copy() course['name'] = 'ExpectedCourse2' course['criteria'] = [{'id': 1}] rv = self.client.post('/api/courses', data=json.dumps(course), content_type='application/json') self.assert200(rv) course_actual = rv.json # Verify the course is created in db course_in_db = Courses.query.get(course_actual['id']) self.assertEqual(len(course_in_db.criteriaandcourses), 1) self.assertEqual(course_in_db.criteriaandcourses[0].criteria_id, course['criteria'][0]['id']) def test_create_duplicate_course(self): with self.login(self.data.get_authorized_instructor().username): course_existing = self.data.get_course() course_expected = { 'name': course_existing.name, 'description': course_existing.description } rv = self.client.post('/api/courses', data=json.dumps(course_expected), content_type='application/json') self.assert400(rv) def test_create_course_with_bad_data_format(self): with self.login(self.data.get_authorized_instructor().username): rv = self.client.post('/api/courses', data=json.dumps({'description': 'd'}), content_type='application/json') self.assert400(rv) def test_edit_course(self): expected = { 'id': self.data.get_course().id, 'name': 'ExpectedCourse', 'description': 'Test Description' } url = '/api/courses/' + str(self.data.get_course().id) # test login required rv = self.client.post(url, data=json.dumps(expected), content_type='application/json') self.assert401(rv) # test unauthorized user with self.login(self.data.get_unauthorized_instructor().username): rv = self.client.post(url, data=json.dumps(expected), content_type='application/json') self.assert403(rv) # test unmatched course id rv = self.client.post('/api/courses/' + str(self.data.get_secondary_course().id), data=json.dumps(expected), content_type='application/json') self.assert400(rv) with self.login(self.data.get_authorized_instructor().username): # test invalid course id rv = self.client.post('/api/courses/999', data=json.dumps(expected), content_type='application/json') self.assert404(rv) # test authorized user rv = self.client.post(url, data=json.dumps(expected), content_type='application/json') self.assert200(rv) db.session.expire_all() self.assertEqual(expected['id'], rv.json['id']) self.assertEqual(expected['name'], rv.json['name']) self.assertEqual(expected['description'], rv.json['description']) # test add criteria course = expected.copy() course['criteria'] = [{'id': 1}] rv = self.client.post(url, data=json.dumps(course), content_type='application/json') self.assert200(rv) db.session.expire_all() course_in_db = Courses.query.get(course['id']) self.assertEqual(len(course_in_db.criteriaandcourses), 1) self.assertEqual(course_in_db.criteriaandcourses[0].criteria_id, course['criteria'][0]['id']) # test remove criteria rv = self.client.post(url, data=json.dumps(expected), content_type='application/json') self.assert200(rv) # expire all instances in session and force session to query from db db.session.expire_all() course_in_db = Courses.query.get(course['id']) self.assertEqual(len(course_in_db.criteriaandcourses), 0)
class CoursesAPITests(ComPAIRAPITestCase): def setUp(self): super(CoursesAPITests, self).setUp() self.data = BasicTestData() def _verify_course_info(self, course_expected, course_actual): self.assertEqual( course_expected.name, course_actual['name'], "Expected course name does not match actual.") self.assertEqual( course_expected.uuid, course_actual['id'], "Expected course id does not match actual.") self.assertEqual( course_expected.description, course_actual['description'], "Expected course description does not match actual.") self.assertEqual( course_expected.year, course_actual['year'], "Expected course description does not match actual.") self.assertEqual( course_expected.term, course_actual['term'], "Expected course description does not match actual.") self.assertEqual( course_expected.available, course_actual['available'], "Expected course availability does not match actual.") def test_get_single_course(self): course_api_url = '/api/courses/' + self.data.get_course().uuid # Test login required rv = self.client.get(course_api_url) self.assert401(rv) # Test root get course with self.login('root'): rv = self.client.get(course_api_url) self.assert200(rv) self._verify_course_info(self.data.get_course(), rv.json) # Test enroled users get course info with self.login(self.data.get_authorized_instructor().username): rv = self.client.get(course_api_url) self.assert200(rv) self._verify_course_info(self.data.get_course(), rv.json) with self.login(self.data.get_authorized_student().username): rv = self.client.get(course_api_url) self.assert200(rv) self._verify_course_info(self.data.get_course(), rv.json) # Test unenroled user not permitted to get info with self.login(self.data.get_unauthorized_instructor().username): rv = self.client.get(course_api_url) self.assert403(rv) with self.login(self.data.get_unauthorized_student().username): rv = self.client.get(course_api_url) self.assert403(rv) # Test get invalid course with self.login("root"): rv = self.client.get('/api/courses/38940450') self.assert404(rv) def test_create_course(self): course_expected = { 'name': 'ExpectedCourse1', 'year': 2015, 'term': 'Winter', 'start_date': None, 'end_date': None, 'description': 'Test Course One Description Test' } # Test login required rv = self.client.post( '/api/courses', data=json.dumps(course_expected), content_type='application/json') self.assert401(rv) # Test unauthorized user with self.login(self.data.get_authorized_student().username): rv = self.client.post( '/api/courses', data=json.dumps(course_expected), content_type='application/json') self.assert403(rv) # Test course creation with self.login(self.data.get_authorized_instructor().username): rv = self.client.post( '/api/courses', data=json.dumps(course_expected), content_type='application/json') self.assert200(rv) # Verify return course_actual = rv.json self.assertEqual(course_expected['name'], course_actual['name']) self.assertEqual(course_expected['year'], course_actual['year']) self.assertEqual(course_expected['term'], course_actual['term']) self.assertEqual(course_expected['description'], course_actual['description']) self.assertTrue(course_actual['available']) # Verify the course is created in db course_in_db = Course.query.filter_by(uuid=course_actual['id']).first() self.assertEqual(course_in_db.name, course_actual['name']) self.assertEqual(course_in_db.year, course_actual['year']) self.assertEqual(course_in_db.term, course_actual['term']) self.assertEqual(course_in_db.description, course_actual['description']) self.assertTrue(course_in_db.available) # Verify instructor added to course user_course = UserCourse.query \ .filter_by( user_id=self.data.get_authorized_instructor().id, course_uuid=course_actual['id'] ) \ .one_or_none() self.assertIsNotNone(user_course) # Starts in the future now = datetime.datetime.utcnow() course_expected['start_date'] = (now + datetime.timedelta(days=7)).isoformat() + 'Z', course_expected['end_date'] = None rv = self.client.post('/api/courses', data=json.dumps(course_expected), content_type='application/json') self.assert200(rv) self.assertFalse(rv.json['available']) # Ended in the past course_expected['start_date'] = None course_expected['end_date'] = (now - datetime.timedelta(days=7)).isoformat() + 'Z', rv = self.client.post('/api/courses', data=json.dumps(course_expected), content_type='application/json') self.assert200(rv) self.assertFalse(rv.json['available']) # Is currently available course_expected['start_date'] = (now - datetime.timedelta(days=7)).isoformat() + 'Z', course_expected['end_date'] = (now + datetime.timedelta(days=7)).isoformat() + 'Z', rv = self.client.post('/api/courses', data=json.dumps(course_expected), content_type='application/json') self.assert200(rv) self.assertTrue(rv.json['available']) def test_create_course_with_bad_data_format(self): with self.login(self.data.get_authorized_instructor().username): rv = self.client.post( '/api/courses', data=json.dumps({'description': 'd'}), content_type='application/json') self.assert400(rv) def test_edit_course(self): expected = { 'id': self.data.get_course().uuid, 'name': 'ExpectedCourse', 'year': 2015, 'term': 'Winter', 'start_date': None, 'end_date': None, 'description': 'Test Description' } url = '/api/courses/' + self.data.get_course().uuid # test login required rv = self.client.post(url, data=json.dumps(expected), content_type='application/json') self.assert401(rv) # test unauthorized user with self.login(self.data.get_unauthorized_instructor().username): rv = self.client.post(url, data=json.dumps(expected), content_type='application/json') self.assert403(rv) # test unmatched course id rv = self.client.post( '/api/courses/' + self.data.get_secondary_course().uuid, data=json.dumps(expected), content_type='application/json') self.assert400(rv) with self.login(self.data.get_authorized_instructor().username): # test invalid course id rv = self.client.post('/api/courses/999', data=json.dumps(expected), content_type='application/json') self.assert404(rv) # test authorized user rv = self.client.post(url, data=json.dumps(expected), content_type='application/json') self.assert200(rv) db.session.expire_all() self.assertEqual(expected['id'], rv.json['id']) self.assertEqual(expected['name'], rv.json['name']) self.assertEqual(expected['description'], rv.json['description']) self.assertTrue(rv.json['available']) # Starts in the future now = datetime.datetime.utcnow() expected['start_date'] = (now + datetime.timedelta(days=7)).isoformat() + 'Z', expected['end_date'] = None rv = self.client.post(url, data=json.dumps(expected), content_type='application/json') self.assert200(rv) self.assertFalse(rv.json['available']) # Ended in the past expected['start_date'] = None expected['end_date'] = (now - datetime.timedelta(days=7)).isoformat() + 'Z', rv = self.client.post(url, data=json.dumps(expected), content_type='application/json') self.assert200(rv) self.assertFalse(rv.json['available']) # Is currently available expected['start_date'] = (now - datetime.timedelta(days=7)).isoformat() + 'Z', expected['end_date'] = (now + datetime.timedelta(days=7)).isoformat() + 'Z', rv = self.client.post(url, data=json.dumps(expected), content_type='application/json') self.assert200(rv) self.assertTrue(rv.json['available']) def test_delete_course(self): course_uuid = self.data.get_course().uuid url = '/api/courses/' + course_uuid # test login required rv = self.client.delete(url) self.assert401(rv) # test unauthorized users with self.login(self.data.get_unauthorized_instructor().username): rv = self.client.delete(url) self.assert403(rv) with self.login(self.data.get_authorized_student().username): rv = self.client.delete(url) self.assert403(rv) with self.login(self.data.get_authorized_ta().username): rv = self.client.delete(url) self.assert403(rv) with self.login(self.data.get_authorized_instructor().username): # test invalid course id rv = self.client.delete('/api/courses/999') self.assert404(rv) # test deletion by authorized insturctor rv = self.client.delete(url) self.assert200(rv) self.assertEqual(course_uuid, rv.json['id']) # test course is deleted rv = self.client.delete(url) self.assert404(rv) course2 = self.data.create_course() url = '/api/courses/' + course2.uuid with self.login('root'): # test deletion by system admin rv = self.client.delete(url) self.assert200(rv) self.assertEqual(course2.uuid, rv.json['id']) # test course is deleted rv = self.client.delete(url) self.assert404(rv) def test_duplicate_course_simple(self): url = '/api/courses/' + self.data.get_course().uuid + '/duplicate' expected = { 'name': 'duplicate course', 'year': 2015, 'term': 'Winter', 'start_date': None, 'end_date': None } # test login required rv = self.client.post(url, content_type='application/json') self.assert401(rv) # test unauthorized user with self.login(self.data.get_unauthorized_instructor().username): rv = self.client.post(url, data=json.dumps(expected), content_type='application/json') self.assert403(rv) with self.login(self.data.get_authorized_instructor().username): # test invalid course id rv = self.client.post('/api/courses/999/duplicate', data=json.dumps(expected), content_type='application/json') self.assert404(rv) # test year missing invalid_expected = { 'term': 'Winter' } rv = self.client.post('/api/courses/999/duplicate', data=json.dumps(invalid_expected), content_type='application/json') self.assert404(rv) # test term missing invalid_expected = { 'year': 2015 } rv = self.client.post('/api/courses/999/duplicate', data=json.dumps(invalid_expected), content_type='application/json') self.assert404(rv) # test authorized user original_course = self.data.get_course() rv = self.client.post(url, data=json.dumps(expected), content_type='application/json') self.assert200(rv) # verify course duplicated correctly self.assertNotEqual(original_course.uuid, rv.json['id']) self.assertEqual(expected['name'], rv.json['name']) self.assertEqual(expected['year'], rv.json['year']) self.assertEqual(expected['term'], rv.json['term']) self.assertEqual(expected['start_date'], rv.json['start_date']) self.assertEqual(expected['end_date'], rv.json['end_date']) self.assertEqual(original_course.description, rv.json['description']) # verify instructor added to duplicate course user_course = UserCourse.query \ .filter_by( user_id=self.data.get_authorized_instructor().id, course_uuid=rv.json['id'] ) \ .one_or_none() self.assertIsNotNone(user_course)
class CoursesAPITests(ACJAPITestCase): def setUp(self): super(CoursesAPITests, self).setUp() self.data = BasicTestData() def _verify_course_info(self, course_expected, course_actual): self.assertEqual(course_expected.name, course_actual["name"], "Expected course name does not match actual.") self.assertEqual(course_expected.id, course_actual["id"], "Expected course id does not match actual.") self.assertTrue(course_expected.criteriaandcourses, "Course is missing a criteria") def test_get_single_course(self): course_api_url = "/api/courses/" + str(self.data.get_course().id) # Test login required rv = self.client.get(course_api_url) self.assert401(rv) # Test root get course with self.login("root"): rv = self.client.get(course_api_url) self.assert200(rv) self._verify_course_info(self.data.get_course(), rv.json) # Test enroled users get course info with self.login(self.data.get_authorized_instructor().username): rv = self.client.get(course_api_url) self.assert200(rv) self._verify_course_info(self.data.get_course(), rv.json) with self.login(self.data.get_authorized_student().username): rv = self.client.get(course_api_url) self.assert200(rv) self._verify_course_info(self.data.get_course(), rv.json) # Test unenroled user not permitted to get info with self.login(self.data.get_unauthorized_instructor().username): rv = self.client.get(course_api_url) self.assert403(rv) with self.login(self.data.get_unauthorized_student().username): rv = self.client.get(course_api_url) self.assert403(rv) # Test get invalid course with self.login("root"): rv = self.client.get("/api/courses/38940450") self.assert404(rv) def test_get_all_courses(self): course_api_url = "/api/courses" # Test login required rv = self.client.get(course_api_url) self.assert401(rv) # Test only root can get a list of all courses with self.login(self.data.get_authorized_instructor().username): rv = self.client.get(course_api_url) self.assert403(rv) with self.login(self.data.get_authorized_student().username): rv = self.client.get(course_api_url) self.assert403(rv) with self.login("root"): rv = self.client.get(course_api_url) self.assert200(rv) self._verify_course_info(self.data.get_course(), rv.json["objects"][0]) def test_create_course(self): course_expected = {"name": "ExpectedCourse1", "description": "Test Course One Description Test"} # Test login required rv = self.client.post("/api/courses", data=json.dumps(course_expected), content_type="application/json") self.assert401(rv) # Test unauthorized user with self.login(self.data.get_authorized_student().username): rv = self.client.post("/api/courses", data=json.dumps(course_expected), content_type="application/json") self.assert403(rv) # Test course creation with self.login(self.data.get_authorized_instructor().username): rv = self.client.post("/api/courses", data=json.dumps(course_expected), content_type="application/json") self.assert200(rv) # Verify return course_actual = rv.json self.assertEqual(course_expected["name"], course_actual["name"]) self.assertEqual(course_expected["description"], course_actual["description"]) # Verify the course is created in db course_in_db = Courses.query.get(course_actual["id"]) self.assertEqual(course_in_db.name, course_actual["name"]) self.assertEqual(course_in_db.description, course_actual["description"]) # create course with criteria course = course_expected.copy() course["name"] = "ExpectedCourse2" course["criteria"] = [{"id": 1}] rv = self.client.post("/api/courses", data=json.dumps(course), content_type="application/json") self.assert200(rv) course_actual = rv.json # Verify the course is created in db course_in_db = Courses.query.get(course_actual["id"]) self.assertEqual(len(course_in_db.criteriaandcourses), 1) self.assertEqual(course_in_db.criteriaandcourses[0].criteria_id, course["criteria"][0]["id"]) def test_create_duplicate_course(self): with self.login(self.data.get_authorized_instructor().username): course_existing = self.data.get_course() course_expected = {"name": course_existing.name, "description": course_existing.description} rv = self.client.post("/api/courses", data=json.dumps(course_expected), content_type="application/json") self.assert400(rv) def test_create_course_with_bad_data_format(self): with self.login(self.data.get_authorized_instructor().username): rv = self.client.post( "/api/courses", data=json.dumps({"description": "d"}), content_type="application/json" ) self.assert400(rv) def test_edit_course(self): expected = {"id": self.data.get_course().id, "name": "ExpectedCourse", "description": "Test Description"} url = "/api/courses/" + str(self.data.get_course().id) # test login required rv = self.client.post(url, data=json.dumps(expected), content_type="application/json") self.assert401(rv) # test unauthorized user with self.login(self.data.get_unauthorized_instructor().username): rv = self.client.post(url, data=json.dumps(expected), content_type="application/json") self.assert403(rv) # test unmatched course id rv = self.client.post( "/api/courses/" + str(self.data.get_secondary_course().id), data=json.dumps(expected), content_type="application/json", ) self.assert400(rv) with self.login(self.data.get_authorized_instructor().username): # test invalid course id rv = self.client.post("/api/courses/999", data=json.dumps(expected), content_type="application/json") self.assert404(rv) # test authorized user rv = self.client.post(url, data=json.dumps(expected), content_type="application/json") self.assert200(rv) db.session.expire_all() self.assertEqual(expected["id"], rv.json["id"]) self.assertEqual(expected["name"], rv.json["name"]) self.assertEqual(expected["description"], rv.json["description"]) # test add criteria course = expected.copy() course["criteria"] = [{"id": 1}] rv = self.client.post(url, data=json.dumps(course), content_type="application/json") self.assert200(rv) db.session.expire_all() course_in_db = Courses.query.get(course["id"]) self.assertEqual(len(course_in_db.criteriaandcourses), 1) self.assertEqual(course_in_db.criteriaandcourses[0].criteria_id, course["criteria"][0]["id"]) # test remove criteria rv = self.client.post(url, data=json.dumps(expected), content_type="application/json") self.assert200(rv) # expire all instances in session and force session to query from db db.session.expire_all() course_in_db = Courses.query.get(course["id"]) self.assertEqual(len(course_in_db.criteriaandcourses), 0)
class UsersAPITests(ACJAPITestCase): def setUp(self): super(UsersAPITests, self).setUp() self.data = BasicTestData() def test_unauthorized(self): rv = self.client.get('/api/users') self.assert401(rv) def test_login(self): with self.login('root', 'password') as rv: userid = rv.json['userid'] self.assertEqual(userid, 1, "Logged in user's id does not match!") self._verify_permissions(userid, rv.json['permissions']) def test_users_root(self): with self.login('root', 'password'): rv = self.client.get('/api/users/' + str(DefaultFixture.ROOT_USER.id)) self.assert200(rv) root = rv.json self.assertEqual(root['username'], 'root') self.assertEqual(root['displayname'], 'root') self.assertNotIn('_password', root) def test_users_invalid_id(self): with self.login('root', 'password'): rv = self.client.get('/api/users/99999') self.assert404(rv) def test_users_info_unrestricted(self): with self.login('root', 'password'): rv = self.client.get('/api/users/' + str(DefaultFixture.ROOT_USER.id)) self.assert200(rv) root = rv.json self.assertEqual(root['displayname'], 'root') # personal information should be transmitted self.assertIn('firstname', root) self.assertIn('lastname', root) self.assertIn('fullname', root) self.assertIn('email', root) def test_users_info_restricted(self): user = UsersFactory(usertypeforsystem=DefaultFixture.SYS_ROLE_NORMAL) with self.login(user.username, user.password): rv = self.client.get('/api/users/' + str(DefaultFixture.ROOT_USER.id)) self.assert200(rv) root = rv.json self.assertEqual(root['displayname'], 'root') # personal information shouldn't be transmitted self.assertNotIn('firstname', root) self.assertNotIn('lastname', root) self.assertNotIn('fullname', root) self.assertNotIn('email', root) def test_users_list(self): with self.login('root', 'password'): rv = self.client.get('/api/users') self.assert200(rv) users = rv.json self.assertEqual(users['total'], 7) self.assertEqual(users['objects'][0]['username'], 'root') rv = self.client.get('/api/users?search={}'.format( self.data.get_unauthorized_instructor().firstname)) self.assert200(rv) users = rv.json self.assertEqual(users['total'], 1) self.assertEqual(users['objects'][0]['username'], self.data.get_unauthorized_instructor().username) def test_usertypes(self): # test login required rv = self.client.get('/api/usertypes') self.assert401(rv) # test results with self.login('root'): rv = self.client.get('/api/usertypes') self.assert200(rv) types = rv.json self.assertEqual(len(types), 3) self.assertEqual(types[0]['name'], UserTypesForSystem.TYPE_NORMAL) self.assertEqual(types[1]['name'], UserTypesForSystem.TYPE_INSTRUCTOR) self.assertEqual(types[2]['name'], UserTypesForSystem.TYPE_SYSADMIN) with self.login(self.data.get_authorized_instructor().username): rv = self.client.get('/api/usertypes') self.assert200(rv) types = rv.json self.assertEqual(len(types), 2) self.assertEqual(types[0]['name'], UserTypesForSystem.TYPE_NORMAL) self.assertEqual(types[1]['name'], UserTypesForSystem.TYPE_INSTRUCTOR) def test_create_user(self): url = '/api/users' self.client.file_name = 'user_create.json' # test login required expected = UsersFactory.stub( usertypesforsystem_id=DefaultFixture.SYS_ROLE_NORMAL.id) rv = self.client.post(url, data=json.dumps(expected.__dict__), content_type='application/json') self.assert401(rv) # test unauthorized user with self.login(self.data.get_authorized_student().username): expected = UsersFactory.stub( usertypesforsystem_id=DefaultFixture.SYS_ROLE_NORMAL.id) rv = self.client.post(url, data=json.dumps(expected.__dict__), content_type='application/json', record='unauthorized') self.assert403(rv) with self.login(self.data.get_authorized_instructor().username): # test duplicate username expected = UsersFactory.stub( usertypesforsystem_id=DefaultFixture.SYS_ROLE_NORMAL.id, username=self.data.get_authorized_student().username) rv = self.client.post(url, data=json.dumps(expected.__dict__), content_type='application/json', record='duplicate_username') self.assertStatus(rv, 409) self.assertEqual( "This username already exists. Please pick another.", rv.json['error']) # test duplicate student number expected = UsersFactory.stub( usertypesforsystem_id=DefaultFixture.SYS_ROLE_NORMAL.id, student_no=self.data.get_authorized_student().student_no) rv = self.client.post(url, data=json.dumps(expected.__dict__), content_type='application/json') self.assertStatus(rv, 409) self.assertEqual( "This student number already exists. Please pick another.", rv.json['error']) # test creating student expected = UsersFactory.stub( usertypesforsystem_id=DefaultFixture.SYS_ROLE_NORMAL.id) rv = self.client.post(url, data=json.dumps(expected.__dict__), content_type="application/json", record='create_student') self.assert200(rv) self.assertEqual(expected.displayname, rv.json['displayname']) # test creating instructor expected = UsersFactory.stub( usertypesforsystem_id=DefaultFixture.SYS_ROLE_INSTRUCTOR.id) rv = self.client.post(url, data=json.dumps(expected.__dict__), content_type="application/json") self.assert200(rv) self.assertEqual(expected.displayname, rv.json['displayname']) # test creating admin expected = UsersFactory.stub( usertypesforsystem_id=DefaultFixture.SYS_ROLE_ADMIN.id) rv = self.client.post(url, data=json.dumps(expected.__dict__), content_type="application/json") self.assert403(rv) def test_edit_user(self): user = self.data.get_authorized_instructor() url = 'api/users/' + str(user.id) expected = { 'id': user.id, 'username': user.username, 'student_no': user.student_no, 'usertypesforsystem_id': user.usertypesforsystem_id, 'firstname': user.firstname, 'lastname': user.lastname, 'displayname': user.displayname, 'email': user.email } # test login required rv = self.client.post(url, data=json.dumps(expected), content_type='application/json') self.assert401(rv) # test unauthorized user # currently, instructors cannot edit users - except their own profile with self.login(self.data.get_unauthorized_instructor().username): rv = self.client.post(url, data=json.dumps(expected), content_type='application/json') self.assert403(rv) # test invalid user id with self.login('root'): rv = self.client.post('/api/users/999', data=json.dumps(expected), content_type='application/json') self.assert404(rv) # test unmatched user's id invalid_url = '/api/users/' + str( self.data.get_unauthorized_instructor().id) rv = self.client.post(invalid_url, data=json.dumps(expected), content_type='application/json') self.assert400(rv) # test duplicate username duplicate = expected.copy() duplicate['username'] = self.data.get_unauthorized_student( ).username rv = self.client.post(url, data=json.dumps(duplicate), content_type='application/json') self.assertStatus(rv, 409) self.assertEqual( "This username already exists. Please pick another.", rv.json['error']) # test duplicate student number duplicate = expected.copy() duplicate['student_no'] = self.data.get_unauthorized_student( ).student_no rv = self.client.post(url, data=json.dumps(duplicate), content_type='application/json') self.assertStatus(rv, 409) self.assertEqual( "This student number already exists. Please pick another.", rv.json['error']) # test successful update by admin valid = expected.copy() valid['displayname'] = "displayzzz" rv = self.client.post(url, data=json.dumps(valid), content_type='application/json') self.assert200(rv) self.assertEqual("displayzzz", rv.json['displayname']) # test successful update by user with self.login(self.data.get_authorized_instructor().username): valid = expected.copy() valid['displayname'] = "thebest" rv = self.client.post(url, data=json.dumps(valid), content_type='application/json') self.assert200(rv) self.assertEqual("thebest", rv.json['displayname']) # test unable to update username, student_no, usertypesforsystem_id - instructor student = UserTypesForSystem.query.filter_by( name=UserTypesForSystem.TYPE_NORMAL).first() with self.login(user.username): valid = expected.copy() valid['username'] = "******" rv = self.client.post(url, data=json.dumps(valid), content_type='application/json') self.assert200(rv) self.assertEqual(user.username, rv.json['username']) valid = expected.copy() valid['student_no'] = "999999999999" rv = self.client.post(url, data=json.dumps(valid), content_type='application/json') self.assert200(rv) self.assertEqual(user.student_no, rv.json['student_no']) valid = expected.copy() valid['usertypesforsystem_id'] = student.id rv = self.client.post(url, data=json.dumps(valid), content_type='application/json') self.assert200(rv) self.assertEqual(user.usertypesforsystem_id, rv.json['usertypesforsystem_id']) # test updating username, student number, usertype for system - admin with self.login('root'): valid = expected.copy() valid['username'] = '******' rv = self.client.post(url, data=json.dumps(valid), content_type='application/json') self.assert200(rv) self.assertEqual('newUsername', rv.json['username']) valid = expected.copy() valid['student_no'] = '99999999' rv = self.client.post(url, data=json.dumps(valid), content_type='application/json') self.assert200(rv) self.assertEqual('99999999', rv.json['student_no']) valid = expected.copy() valid['usertypesforsystem_id'] = student.id rv = self.client.post(url, data=json.dumps(valid), content_type='application/json') self.assert200(rv) self.assertEqual(user.usertypesforsystem_id, rv.json['usertypesforsystem_id']) def test_get_course_list(self): # test login required url = '/api/users/' + str( self.data.get_authorized_instructor().id) + '/courses' rv = self.client.get(url) self.assert401(rv) # test invalid user id with self.login('root'): url = '/api/users/999/courses' rv = self.client.get(url) self.assert404(rv) # test admin admin_id = Users.query.filter_by(username='******').first().id url = '/api/users/' + str(admin_id) + '/courses' rv = self.client.get(url) self.assert200(rv) self.assertEqual(2, len(rv.json['objects'])) # test authorized instructor with self.login(self.data.get_authorized_instructor().username): url = '/api/users/' + str( self.data.get_authorized_instructor().id) + '/courses' rv = self.client.get(url) self.assert200(rv) self.assertEqual(1, len(rv.json['objects'])) self.assertEqual(self.data.get_course().name, rv.json['objects'][0]['name']) # test authorized student with self.login(self.data.get_authorized_student().username): url = '/api/users/' + str( self.data.get_authorized_student().id) + '/courses' rv = self.client.get(url) self.assert200(rv) self.assertEqual(1, len(rv.json['objects'])) self.assertEqual(self.data.get_course().name, rv.json['objects'][0]['name']) # test authorized teaching assistant with self.login(self.data.get_authorized_ta().username): url = '/api/users/' + str( self.data.get_authorized_ta().id) + '/courses' rv = self.client.get(url) self.assert200(rv) self.assertEqual(1, len(rv.json['objects'])) self.assertEqual(self.data.get_course().name, rv.json['objects'][0]['name']) # test dropped instructor with self.login(self.data.get_dropped_instructor().username): url = '/api/users/' + str( self.data.get_dropped_instructor().id) + '/courses' rv = self.client.get(url) self.assert200(rv) self.assertEqual(0, len(rv.json['objects'])) def test_get_teaching_course(self): url = '/api/users/courses/teaching' # test login required rv = self.client.get(url) self.assert401(rv) # test student with self.login(self.data.get_authorized_student().username): rv = self.client.get(url) self.assert200(rv) self.assertEqual(0, len(rv.json['courses'])) # test TA with self.login(self.data.get_authorized_ta().username): rv = self.client.get(url) self.assert200(rv) self.assertEqual(1, len(rv.json['courses'])) # test instructor with self.login(self.data.get_authorized_instructor().username): rv = self.client.get(url) self.assert200(rv) self.assertEqual(1, len(rv.json['courses'])) # test admin with self.login('root'): rv = self.client.get(url) self.assert200(rv) self.assertEqual(2, len(rv.json['courses'])) def test_update_password(self): url = '/api/users/{}/password' data = {'oldpassword': '******', 'newpassword': '******'} # test login required rv = self.client.post(url.format( str(self.data.authorized_instructor.id)), data=json.dumps(data), content_type='application/json') self.assert401(rv) # test student update password with self.login(self.data.authorized_student.username): # test without old password rv = self.client.post(url.format( str(self.data.authorized_student.id)), data=json.dumps({'newpassword': '******'}), content_type='application/json') self.assert403(rv) self.assertEqual( 'The old password is incorrect or you do not have permission to change password.', rv.json['error']) # test incorrect old password invalid_input = data.copy() invalid_input['oldpassword'] = '******' rv = self.client.post(url.format( str(self.data.authorized_student.id)), data=json.dumps(invalid_input), content_type='application/json') self.assert403(rv) self.assertEqual( 'The old password is incorrect or you do not have permission to change password.', rv.json['error']) # test with old password rv = self.client.post(url.format( str(self.data.authorized_student.id)), data=json.dumps(data), content_type='application/json') self.assert200(rv) self.assertEqual(self.data.get_authorized_student().id, rv.json['id']) # test instructor update password with self.login(self.data.get_authorized_instructor().username): rv = self.client.post(url.format(str(999)), data=json.dumps(data), content_type='application/json') self.assert404(rv) # test instructor changes the password of a student in the course rv = self.client.post(url.format( str(self.data.get_authorized_student().id)), data=json.dumps(data), content_type='application/json') self.assert200(rv) self.assertEqual(self.data.get_authorized_student().id, rv.json['id']) # test changing own password rv = self.client.post(url.format( str(self.data.get_authorized_instructor().id)), data=json.dumps(data), content_type='application/json') self.assert200(rv) self.assertEqual(self.data.get_authorized_instructor().id, rv.json['id']) # test instructor changes the password of a student not in the course with self.login(self.data.get_unauthorized_instructor().username): rv = self.client.post(url.format( str(self.data.get_authorized_student().id)), data=json.dumps(data), content_type='application/json') self.assert403(rv) self.assertEqual( '<p>{} does not have edit access to {}</p>'.format( self.data.get_unauthorized_instructor().username, self.data.get_authorized_student().username), rv.json['message']) # test admin update password with self.login('root'): # test admin changes student password without old password rv = self.client.post(url.format( str(self.data.get_authorized_student().id)), data=json.dumps({'newpassword': '******'}), content_type='application/json') self.assert200(rv) self.assertEqual(self.data.get_authorized_student().id, rv.json['id']) def test_get_course_roles(self): url = '/api/courseroles' # test login required rv = self.client.get(url) self.assert401(rv) # test successful query with self.login(self.data.get_authorized_instructor().username): rv = self.client.get(url) self.assert200(rv) types = rv.json self.assertEqual(len(types), 3) self.assertEqual(types[0]['name'], UserTypesForCourse.TYPE_INSTRUCTOR) self.assertEqual(types[1]['name'], UserTypesForCourse.TYPE_TA) self.assertEqual(types[2]['name'], UserTypesForCourse.TYPE_STUDENT) def test_get_edit_button(self): url = '/api/users/' + str( self.data.get_authorized_student().id) + '/edit' # test login required rv = self.client.get(url) self.assert401(rv) # test invalid user id with self.login(self.data.get_unauthorized_student().username): invalid_url = '/api/users/999/edit' rv = self.client.get(invalid_url) self.assert404(rv) # test unavailable button rv = self.client.get(url) self.assert200(rv) self.assertFalse(rv.json['available']) # test available button with self.login(self.data.get_authorized_instructor().username): rv = self.client.get(url) self.assert200(rv) self.assertTrue(rv.json['available']) def _verify_permissions(self, userid, permissions): user = Users.query.get(userid) with self.app.app_context(): # can't figure out how to get into logged in app context, so just force a login here login_user(user, force=True) admin = user.usertypeforsystem.name == 'System Administrator' for model_name, operations in permissions.items(): for operation, permission in operations.items(): expected = True try: ensure(operation, model_name) except Unauthorized: expected = False expected = expected or admin self.assertEqual( permission['global'], expected, "Expected permission " + operation + " on " + model_name + " to be " + str(expected)) # undo the forced login earlier logout_user() def _generate_search_users(self, user): return { 'id': user.id, 'display': user.fullname + ' (' + user.displayname + ') - ' + user.usertypeforsystem.name, 'name': user.fullname }
class CoursesAPITests(ComPAIRAPITestCase): def setUp(self): super(CoursesAPITests, self).setUp() self.data = BasicTestData() def _verify_course_info(self, course_expected, course_actual): self.assertEqual(course_expected.name, course_actual['name'], "Expected course name does not match actual.") self.assertEqual(course_expected.uuid, course_actual['id'], "Expected course id does not match actual.") self.assertEqual(course_expected.year, course_actual['year'], "Expected course year does not match actual.") self.assertEqual(course_expected.term, course_actual['term'], "Expected course term does not match actual.") self.assertEqual( course_expected.sandbox, course_actual['sandbox'], "Expected course sandbox flag does not match actual.") self.assertEqual( course_expected.available, course_actual['available'], "Expected course availability does not match actual.") def test_get_single_course(self): course_api_url = '/api/courses/' + self.data.get_course().uuid # Test login required rv = self.client.get(course_api_url) self.assert401(rv) # Test root get course with self.login('root'): rv = self.client.get(course_api_url) self.assert200(rv) self._verify_course_info(self.data.get_course(), rv.json) # Test enroled users get course info with self.login(self.data.get_authorized_instructor().username): rv = self.client.get(course_api_url) self.assert200(rv) self._verify_course_info(self.data.get_course(), rv.json) with self.login(self.data.get_authorized_student().username): rv = self.client.get(course_api_url) self.assert200(rv) self._verify_course_info(self.data.get_course(), rv.json) # Test unenroled user not permitted to get info with self.login(self.data.get_unauthorized_instructor().username): rv = self.client.get(course_api_url) self.assert403(rv) with self.login(self.data.get_unauthorized_student().username): rv = self.client.get(course_api_url) self.assert403(rv) # Test get invalid course with self.login("root"): rv = self.client.get('/api/courses/38940450') self.assert404(rv) def test_create_course(self): course_expected = { 'name': 'ExpectedCourse1', 'year': 2015, 'term': 'Winter', 'sandbox': False, 'start_date': None, 'end_date': None } # Test login required rv = self.client.post('/api/courses', data=json.dumps(course_expected), content_type='application/json') self.assert401(rv) # Test unauthorized user with self.login(self.data.get_authorized_student().username): rv = self.client.post('/api/courses', data=json.dumps(course_expected), content_type='application/json') self.assert403(rv) # Test course creation with self.login(self.data.get_authorized_instructor().username): rv = self.client.post('/api/courses', data=json.dumps(course_expected), content_type='application/json') self.assert200(rv) # Verify return course_actual = rv.json self.assertEqual(course_expected['name'], course_actual['name']) self.assertEqual(course_expected['year'], course_actual['year']) self.assertEqual(course_expected['term'], course_actual['term']) self.assertEqual(course_expected['sandbox'], course_actual['sandbox']) self.assertTrue(course_actual['available']) # Verify the course is created in db course_in_db = Course.query.filter_by( uuid=course_actual['id']).first() self.assertEqual(course_in_db.name, course_actual['name']) self.assertEqual(course_in_db.year, course_actual['year']) self.assertEqual(course_in_db.term, course_actual['term']) self.assertEqual(course_in_db.sandbox, course_actual['sandbox']) self.assertTrue(course_in_db.available) # Verify instructor added to course user_course = UserCourse.query \ .filter_by( user_id=self.data.get_authorized_instructor().id, course_uuid=course_actual['id'] ) \ .one_or_none() self.assertIsNotNone(user_course) # Starts in the future now = datetime.datetime.utcnow() course_expected['start_date'] = ( now + datetime.timedelta(days=7)).isoformat() + 'Z', course_expected['end_date'] = None rv = self.client.post('/api/courses', data=json.dumps(course_expected), content_type='application/json') self.assert200(rv) self.assertFalse(rv.json['available']) # Ended in the past course_expected['start_date'] = None course_expected['end_date'] = ( now - datetime.timedelta(days=7)).isoformat() + 'Z', rv = self.client.post('/api/courses', data=json.dumps(course_expected), content_type='application/json') self.assert200(rv) self.assertFalse(rv.json['available']) # Is currently available course_expected['start_date'] = ( now - datetime.timedelta(days=7)).isoformat() + 'Z', course_expected['end_date'] = ( now + datetime.timedelta(days=7)).isoformat() + 'Z', rv = self.client.post('/api/courses', data=json.dumps(course_expected), content_type='application/json') self.assert200(rv) self.assertTrue(rv.json['available']) def test_create_course_with_bad_data_format(self): with self.login(self.data.get_authorized_instructor().username): rv = self.client.post('/api/courses', data=json.dumps({'year': 'd'}), content_type='application/json') self.assert400(rv) def test_edit_course(self): expected = { 'id': self.data.get_course().uuid, 'name': 'ExpectedCourse', 'year': 2015, 'term': 'Winter', 'sandbox': False, 'start_date': None, 'end_date': None } url = '/api/courses/' + self.data.get_course().uuid # test login required rv = self.client.post(url, data=json.dumps(expected), content_type='application/json') self.assert401(rv) # test unauthorized user with self.login(self.data.get_unauthorized_instructor().username): rv = self.client.post(url, data=json.dumps(expected), content_type='application/json') self.assert403(rv) # test unmatched course id rv = self.client.post('/api/courses/' + self.data.get_secondary_course().uuid, data=json.dumps(expected), content_type='application/json') self.assert400(rv) with self.login(self.data.get_authorized_instructor().username): # test invalid course id rv = self.client.post('/api/courses/999', data=json.dumps(expected), content_type='application/json') self.assert404(rv) # test authorized user rv = self.client.post(url, data=json.dumps(expected), content_type='application/json') self.assert200(rv) db.session.expire_all() self.assertEqual(expected['id'], rv.json['id']) self.assertEqual(expected['name'], rv.json['name']) self.assertTrue(rv.json['available']) # Starts in the future now = datetime.datetime.utcnow() expected['start_date'] = ( now + datetime.timedelta(days=7)).isoformat() + 'Z', expected['end_date'] = None rv = self.client.post(url, data=json.dumps(expected), content_type='application/json') self.assert200(rv) self.assertFalse(rv.json['available']) # Ended in the past expected['start_date'] = None expected['end_date'] = ( now - datetime.timedelta(days=7)).isoformat() + 'Z', rv = self.client.post(url, data=json.dumps(expected), content_type='application/json') self.assert200(rv) self.assertFalse(rv.json['available']) # Is currently available expected['start_date'] = ( now - datetime.timedelta(days=7)).isoformat() + 'Z', expected['end_date'] = ( now + datetime.timedelta(days=7)).isoformat() + 'Z', rv = self.client.post(url, data=json.dumps(expected), content_type='application/json') self.assert200(rv) self.assertTrue(rv.json['available']) def test_delete_course(self): course_uuid = self.data.get_course().uuid url = '/api/courses/' + course_uuid # test login required rv = self.client.delete(url) self.assert401(rv) # test unauthorized users with self.login(self.data.get_unauthorized_instructor().username): rv = self.client.delete(url) self.assert403(rv) with self.login(self.data.get_authorized_student().username): rv = self.client.delete(url) self.assert403(rv) with self.login(self.data.get_authorized_ta().username): rv = self.client.delete(url) self.assert403(rv) with self.login(self.data.get_authorized_instructor().username): # test invalid course id rv = self.client.delete('/api/courses/999') self.assert404(rv) # test deletion by authorized insturctor rv = self.client.delete(url) self.assert200(rv) self.assertEqual(course_uuid, rv.json['id']) # test course is deleted rv = self.client.delete(url) self.assert404(rv) course2 = self.data.create_course() url = '/api/courses/' + course2.uuid with self.login('root'): # test deletion by system admin rv = self.client.delete(url) self.assert200(rv) self.assertEqual(course2.uuid, rv.json['id']) # test course is deleted rv = self.client.delete(url) self.assert404(rv) def test_duplicate_course_simple(self): url = '/api/courses/' + self.data.get_course().uuid + '/duplicate' expected = { 'name': 'duplicate course', 'year': 2015, 'term': 'Winter', 'sandbox': False, 'start_date': None, 'end_date': None } # test login required rv = self.client.post(url, content_type='application/json') self.assert401(rv) # test unauthorized user with self.login(self.data.get_unauthorized_instructor().username): rv = self.client.post(url, data=json.dumps(expected), content_type='application/json') self.assert403(rv) with self.login(self.data.get_authorized_instructor().username): # test invalid course id rv = self.client.post('/api/courses/999/duplicate', data=json.dumps(expected), content_type='application/json') self.assert404(rv) # test year missing invalid_expected = {'term': 'Winter'} rv = self.client.post('/api/courses/999/duplicate', data=json.dumps(invalid_expected), content_type='application/json') self.assert404(rv) # test term missing invalid_expected = {'year': 2015} rv = self.client.post('/api/courses/999/duplicate', data=json.dumps(invalid_expected), content_type='application/json') self.assert404(rv) # test authorized user original_course = self.data.get_course() rv = self.client.post(url, data=json.dumps(expected), content_type='application/json') self.assert200(rv) # verify course duplicated correctly self.assertNotEqual(original_course.uuid, rv.json['id']) self.assertEqual(expected['name'], rv.json['name']) self.assertEqual(expected['year'], rv.json['year']) self.assertEqual(expected['term'], rv.json['term']) self.assertEqual(expected['sandbox'], rv.json['sandbox']) self.assertEqual(expected['start_date'], rv.json['start_date']) self.assertEqual(expected['end_date'], rv.json['end_date']) # verify instructor added to duplicate course user_course = UserCourse.query \ .filter_by( user_id=self.data.get_authorized_instructor().id, course_uuid=rv.json['id'] ) \ .one_or_none() self.assertIsNotNone(user_course)
class UsersAPITests(ComPAIRAPITestCase): def setUp(self): super(UsersAPITests, self).setUp() self.data = BasicTestData() def test_unauthorized(self): rv = self.client.get('/api/users') self.assert401(rv) def test_login(self): with self.login('root', 'password') as rv: root = User.query.get(1) user_id = rv.json['user_id'] self.assertEqual(root.uuid, user_id, "Logged in user's id does not match!") self._verify_permissions(root.id, rv.json['permissions']) def test_users_root(self): with self.login('root', 'password'): rv = self.client.get('/api/users/' + DefaultFixture.ROOT_USER.uuid) self.assert200(rv) root = rv.json self.assertEqual(root['username'], 'root') self.assertEqual(root['displayname'], 'root') self.assertNotIn('_password', root) def test_users_invalid_id(self): with self.login('root', 'password'): rv = self.client.get('/api/users/99999') self.assert404(rv) def test_users_info_unrestricted(self): with self.login('root', 'password'): rv = self.client.get('/api/users/' + DefaultFixture.ROOT_USER.uuid) self.assert200(rv) root = rv.json self.assertEqual(root['displayname'], 'root') # personal information should be transmitted self.assertIn('firstname', root) self.assertIn('lastname', root) self.assertIn('fullname', root) self.assertIn('email', root) def test_users_info_restricted(self): user = UserFactory(system_role=SystemRole.student) with self.login(user.username, user.password): rv = self.client.get('/api/users/' + DefaultFixture.ROOT_USER.uuid) self.assert200(rv) root = rv.json self.assertEqual(root['displayname'], 'root') # personal information shouldn't be transmitted self.assertNotIn('firstname', root) self.assertNotIn('lastname', root) self.assertNotIn('fullname', root) self.assertNotIn('email', root) def test_users_list(self): with self.login('root', 'password'): rv = self.client.get('/api/users') self.assert200(rv) users = rv.json self.assertEqual(users['total'], 7) self.assertEqual(users['objects'][0]['username'], 'root') rv = self.client.get('/api/users?search={}'.format(self.data.get_unauthorized_instructor().firstname)) self.assert200(rv) users = rv.json self.assertEqual(users['total'], 1) self.assertEqual(users['objects'][0]['username'], self.data.get_unauthorized_instructor().username) def test_create_user(self): url = '/api/users' # test login required expected = UserFactory.stub(system_role=SystemRole.student.value) rv = self.client.post(url, data=json.dumps(expected.__dict__), content_type='application/json') self.assert401(rv) # test unauthorized user with self.login(self.data.get_authorized_student().username): expected = UserFactory.stub(system_role=SystemRole.student.value) rv = self.client.post( url, data=json.dumps(expected.__dict__), content_type='application/json') self.assert403(rv) with self.login(self.data.get_authorized_instructor().username): expected = UserFactory.stub(system_role=SystemRole.student.value) rv = self.client.post( url, data=json.dumps(expected.__dict__), content_type='application/json') self.assert403(rv) # only system admins can create users with self.login('root'): # test duplicate username expected = UserFactory.stub( system_role=SystemRole.student.value, username=self.data.get_authorized_student().username) rv = self.client.post( url, data=json.dumps(expected.__dict__), content_type='application/json') self.assertStatus(rv, 409) self.assertEqual("This username already exists. Please pick another.", rv.json['error']) # test duplicate student number expected = UserFactory.stub( system_role=SystemRole.student.value, student_number=self.data.get_authorized_student().student_number) rv = self.client.post(url, data=json.dumps(expected.__dict__), content_type='application/json') self.assertStatus(rv, 409) self.assertEqual("This student number already exists. Please pick another.", rv.json['error']) # test creating student expected = UserFactory.stub(system_role=SystemRole.student.value) rv = self.client.post( url, data=json.dumps(expected.__dict__), content_type="application/json") self.assert200(rv) self.assertEqual(expected.displayname, rv.json['displayname']) # test creating instructor expected = UserFactory.stub(system_role=SystemRole.instructor.value) rv = self.client.post(url, data=json.dumps(expected.__dict__), content_type="application/json") self.assert200(rv) self.assertEqual(expected.displayname, rv.json['displayname']) # test creating admin expected = UserFactory.stub(system_role=SystemRole.sys_admin.value) rv = self.client.post(url, data=json.dumps(expected.__dict__), content_type="application/json") self.assert200(rv) self.assertEqual(expected.displayname, rv.json['displayname']) def test_create_user_lti(self): url = '/api/users' lti_data = LTITestData() # test login required when LTI and oauth_create_user_link are not present expected = UserFactory.stub(system_role=SystemRole.student.value) rv = self.client.post(url, data=json.dumps(expected.__dict__), content_type='application/json') self.assert401(rv) # Instructor - no context with self.lti_launch(lti_data.get_consumer(), lti_data.generate_resource_link_id(), user_id=lti_data.generate_user_id(), context_id=None, roles="Instructor") as lti_response: self.assert200(lti_response) # test create instructor via lti session expected = UserFactory.stub(system_role=None) rv = self.client.post(url, data=json.dumps(expected.__dict__), content_type="application/json") self.assert200(rv) self.assertEqual(expected.displayname, rv.json['displayname']) user = User.query.filter_by(uuid=rv.json['id']).one() self.assertEqual(SystemRole.instructor, user.system_role) self.assertIsNotNone(user.password) self.assertEqual(expected.username, user.username) # verify not enrolled in any course self.assertEqual(len(user.user_courses), 0) # Instructor - with context not linked with self.lti_launch(lti_data.get_consumer(), lti_data.generate_resource_link_id(), user_id=lti_data.generate_user_id(), context_id=lti_data.generate_context_id(), roles="Instructor") as lti_response: self.assert200(lti_response) # test create instructor via lti session expected = UserFactory.stub(system_role=None) rv = self.client.post(url, data=json.dumps(expected.__dict__), content_type="application/json") self.assert200(rv) self.assertEqual(expected.displayname, rv.json['displayname']) user = User.query.filter_by(uuid=rv.json['id']).one() self.assertEqual(SystemRole.instructor, user.system_role) self.assertIsNotNone(user.password) self.assertEqual(expected.username, user.username) # verify not enrolled in any course self.assertEqual(len(user.user_courses), 0) # Instructor - with context linked with self.lti_launch(lti_data.get_consumer(), lti_data.generate_resource_link_id(), user_id=lti_data.generate_user_id(), context_id=lti_data.generate_context_id(), roles="Instructor") as lti_response: self.assert200(lti_response) lti_context = LTIContext.query.all()[-1] course = self.data.create_course() lti_context.compair_course_id = course.id db.session.commit() # test create instructor via lti session expected = UserFactory.stub(system_role=None) rv = self.client.post(url, data=json.dumps(expected.__dict__), content_type="application/json") self.assert200(rv) self.assertEqual(expected.displayname, rv.json['displayname']) user = User.query.filter_by(uuid=rv.json['id']).one() self.assertEqual(SystemRole.instructor, user.system_role) self.assertIsNotNone(user.password) self.assertEqual(expected.username, user.username) # verify enrolled in course self.assertEqual(len(user.user_courses), 1) self.assertEqual(user.user_courses[0].course_id, course.id) # test create student via lti session with self.lti_launch(lti_data.get_consumer(), lti_data.generate_resource_link_id(), user_id=lti_data.generate_user_id(), context_id=lti_data.generate_context_id(), roles="Student") as lti_response: self.assert200(lti_response) expected = UserFactory.stub(system_role=None) rv = self.client.post(url, data=json.dumps(expected.__dict__), content_type="application/json") self.assert200(rv) self.assertEqual(expected.displayname, rv.json['displayname']) user = User.query.filter_by(uuid=rv.json['id']).one() self.assertEqual(SystemRole.student, user.system_role) self.assertIsNotNone(user.password) self.assertEqual(expected.username, user.username) # test create teaching assistant (student role) via lti session with self.lti_launch(lti_data.get_consumer(), lti_data.generate_resource_link_id(), user_id=lti_data.generate_user_id(), context_id=lti_data.generate_context_id(), roles="TeachingAssistant") as lti_response: self.assert200(lti_response) expected = UserFactory.stub(system_role=None) rv = self.client.post(url, data=json.dumps(expected.__dict__), content_type="application/json") self.assert200(rv) self.assertEqual(expected.displayname, rv.json['displayname']) user = User.query.filter_by(uuid=rv.json['id']).one() self.assertEqual(SystemRole.student, user.system_role) self.assertIsNotNone(user.password) self.assertEqual(expected.username, user.username) def test_create_user_lti_and_CAS(self): url = '/api/users' lti_data = LTITestData() auth_data = ThirdPartyAuthTestData() with self.client.session_transaction() as sess: sess['CAS_CREATE'] = True sess['CAS_UNIQUE_IDENTIFIER'] = "some_unique_identifier" self.assertIsNone(sess.get('LTI')) # test login required when LTI and oauth_create_user_link are not present (even when CAS params are present) expected = UserFactory.stub(system_role=SystemRole.student.value) rv = self.client.post(url, data=json.dumps(expected.__dict__), content_type='application/json') self.assert401(rv) # test create student via lti session with self.lti_launch(lti_data.get_consumer(), lti_data.generate_resource_link_id(), user_id=lti_data.generate_user_id(), context_id=lti_data.generate_context_id(), roles="Student") as lti_response: self.assert200(lti_response) with self.client.session_transaction() as sess: sess['CAS_CREATE'] = True sess['CAS_UNIQUE_IDENTIFIER'] = "some_unique_identifier" self.assertTrue(sess.get('LTI')) expected = UserFactory.stub(system_role=None) rv = self.client.post(url, data=json.dumps(expected.__dict__), content_type="application/json") self.assert200(rv) self.assertEqual(expected.displayname, rv.json['displayname']) user = User.query.filter_by(uuid=rv.json['id']).one() self.assertEqual(SystemRole.student, user.system_role) self.assertIsNone(user.password) self.assertIsNone(user.username) third_party_user = ThirdPartyUser.query \ .filter_by( third_party_type=ThirdPartyType.cas, unique_identifier="some_unique_identifier", user_id=user.id ) \ .one_or_none() self.assertIsNotNone(third_party_user) with self.client.session_transaction() as sess: self.assertTrue(sess.get('CAS_LOGIN')) self.assertIsNone(sess.get('CAS_CREATE')) self.assertIsNone(sess.get('CAS_UNIQUE_IDENTIFIER')) self.assertIsNone(sess.get('oauth_create_user_link')) def test_edit_user(self): user = self.data.get_authorized_student() url = 'api/users/' + user.uuid expected = { 'id': user.uuid, 'username': user.username, 'student_number': user.student_number, 'system_role': user.system_role.value, 'firstname': user.firstname, 'lastname': user.lastname, 'displayname': user.displayname, 'email': user.email } instructor = self.data.get_authorized_instructor() instructor_url = 'api/users/' + instructor.uuid expected_instructor = { 'id': instructor.uuid, 'username': instructor.username, 'student_number': instructor.student_number, 'system_role': instructor.system_role.value, 'firstname': instructor.firstname, 'lastname': instructor.lastname, 'displayname': instructor.displayname, 'email': instructor.email } # test login required rv = self.client.post(url, data=json.dumps(expected), content_type='application/json') self.assert401(rv) # test unauthorized user with self.login(self.data.get_unauthorized_instructor().username): rv = self.client.post(url, data=json.dumps(expected), content_type='application/json') self.assert403(rv) # test invalid user id with self.login('root'): rv = self.client.post('/api/users/999', data=json.dumps(expected), content_type='application/json') self.assert404(rv) # test unmatched user's id invalid_url = '/api/users/' + self.data.get_unauthorized_instructor().uuid rv = self.client.post(invalid_url, data=json.dumps(expected), content_type='application/json') self.assert400(rv) # test duplicate username duplicate = expected.copy() duplicate['username'] = self.data.get_unauthorized_student().username rv = self.client.post(url, data=json.dumps(duplicate), content_type='application/json') self.assertStatus(rv, 409) self.assertEqual("This username already exists. Please pick another.", rv.json['error']) # test duplicate student number duplicate = expected.copy() duplicate['student_number'] = self.data.get_unauthorized_student().student_number rv = self.client.post(url, data=json.dumps(duplicate), content_type='application/json') self.assertStatus(rv, 409) self.assertEqual("This student number already exists. Please pick another.", rv.json['error']) # test successful update by admin valid = expected.copy() valid['displayname'] = "displayzzz" rv = self.client.post(url, data=json.dumps(valid), content_type='application/json') self.assert200(rv) self.assertEqual("displayzzz", rv.json['displayname']) # test successful update by user (as instructor) with self.login(self.data.get_authorized_instructor().username): valid = expected_instructor.copy() valid['displayname'] = "thebest" rv = self.client.post(instructor_url, data=json.dumps(valid), content_type='application/json') self.assert200(rv) self.assertEqual("thebest", rv.json['displayname']) # test successful update by user (as student) with self.login(self.data.get_authorized_student().username): valid = expected.copy() valid['displayname'] = "thebest" rv = self.client.post(url, data=json.dumps(valid), content_type='application/json') self.assert200(rv) self.assertEqual("thebest", rv.json['displayname']) # test updating username, student number, usertype for system - instructor with self.login(instructor.username): # for student valid = expected.copy() valid['username'] = "******" rv = self.client.post(url, data=json.dumps(valid), content_type='application/json') self.assert200(rv) self.assertEqual(user.username, rv.json['username']) valid = expected.copy() valid['student_number'] = "999999999999" rv = self.client.post(url, data=json.dumps(valid), content_type='application/json') self.assert200(rv) self.assertEqual(user.student_number, rv.json['student_number']) valid = expected.copy() valid['system_role'] = SystemRole.student.value rv = self.client.post(url, data=json.dumps(valid), content_type='application/json') self.assert200(rv) self.assertEqual(user.system_role.value, rv.json['system_role']) # for instructor valid = expected_instructor.copy() valid['username'] = "******" rv = self.client.post(instructor_url, data=json.dumps(valid), content_type='application/json') self.assert200(rv) self.assertEqual(instructor.username, rv.json['username']) ignored = expected_instructor.copy() ignored['student_number'] = "999999999999" rv = self.client.post(instructor_url, data=json.dumps(ignored), content_type='application/json') self.assert200(rv) self.assertIsNone(rv.json['student_number']) self.assertEqual(instructor.student_number, rv.json['student_number']) valid = expected_instructor.copy() valid['system_role'] = SystemRole.student.value rv = self.client.post(instructor_url, data=json.dumps(valid), content_type='application/json') self.assert200(rv) self.assertEqual(instructor.system_role.value, rv.json['system_role']) # test updating username, student number, usertype for system - admin with self.login('root'): # for student valid = expected.copy() valid['username'] = '******' rv = self.client.post(url, data=json.dumps(valid), content_type='application/json') self.assert200(rv) self.assertEqual('newUsername', rv.json['username']) valid = expected.copy() valid['student_number'] = '99999999' rv = self.client.post(url, data=json.dumps(valid), content_type='application/json') self.assert200(rv) self.assertEqual('99999999', rv.json['student_number']) valid = expected.copy() valid['system_role'] = SystemRole.student.value rv = self.client.post(url, data=json.dumps(valid), content_type='application/json') self.assert200(rv) self.assertEqual(user.system_role.value, rv.json['system_role']) # for instructor valid = expected_instructor.copy() valid['username'] = "******" rv = self.client.post(instructor_url, data=json.dumps(valid), content_type='application/json') self.assert200(rv) self.assertEqual(instructor.username, rv.json['username']) ignored = expected_instructor.copy() ignored['student_number'] = "999999999999" rv = self.client.post(instructor_url, data=json.dumps(ignored), content_type='application/json') self.assert200(rv) self.assertIsNone(rv.json['student_number']) self.assertEqual(instructor.student_number, rv.json['student_number']) valid = expected_instructor.copy() valid['system_role'] = SystemRole.student.value rv = self.client.post(instructor_url, data=json.dumps(valid), content_type='application/json') self.assert200(rv) self.assertEqual(instructor.system_role.value, rv.json['system_role']) # test edit user with no compair login auth_data = ThirdPartyAuthTestData() cas_user_auth = auth_data.create_cas_user_auth(SystemRole.student) user = cas_user_auth.user self.data.enrol_user(user, self.data.get_course(), CourseRole.student) url = 'api/users/' + user.uuid expected = { 'id': user.uuid, 'username': user.username, 'student_number': user.student_number, 'system_role': user.system_role.value, 'firstname': user.firstname, 'lastname': user.lastname, 'displayname': user.displayname, 'email': user.email } # edit own profile as cas user with self.cas_login(cas_user_auth.unique_identifier): # cannot change username (must be None) valid = expected.copy() valid['username'] = "******" rv = self.client.post(url, data=json.dumps(valid), content_type='application/json') self.assert200(rv) user = User.query.filter_by(uuid=rv.json['id']).one() self.assertIsNone(user.username) # test updating username as instructor with self.login(instructor.username): # cannot change username (must be None) valid = expected.copy() valid['username'] = "******" rv = self.client.post(url, data=json.dumps(valid), content_type='application/json') self.assert200(rv) user = User.query.filter_by(uuid=rv.json['id']).one() self.assertIsNone(user.username) # test updating username as system admin with self.login('root'): # admin can optionally change username valid = expected.copy() valid['username'] = '' rv = self.client.post(url, data=json.dumps(valid), content_type='application/json') self.assert200(rv) user = User.query.filter_by(uuid=rv.json['id']).one() self.assertIsNone(user.username) valid = expected.copy() valid['username'] = "******" rv = self.client.post(url, data=json.dumps(valid), content_type='application/json') self.assert200(rv) user = User.query.filter_by(uuid=rv.json['id']).one() self.assertEqual(user.username, "valid_username") def test_get_course_list(self): # test login required url = '/api/users/courses' rv = self.client.get(url) self.assert401(rv) with self.login('root'): # test admin url = '/api/users/courses' rv = self.client.get(url) self.assert200(rv) self.assertEqual(2, len(rv.json['objects'])) # test authorized instructor with self.login(self.data.get_authorized_instructor().username): url = '/api/users/courses' rv = self.client.get(url) self.assert200(rv) self.assertEqual(1, len(rv.json['objects'])) self.assertEqual(self.data.get_course().name, rv.json['objects'][0]['name']) # test search filter url = '/api/users/courses?search='+self.data.get_course().name rv = self.client.get(url) self.assert200(rv) self.assertEqual(1, len(rv.json['objects'])) self.assertEqual(self.data.get_course().name, rv.json['objects'][0]['name']) # test search filter url = '/api/users/courses?search=notfounds'+self.data.get_course().name rv = self.client.get(url) self.assert200(rv) self.assertEqual(0, len(rv.json['objects'])) # test sort order (when some courses have start_dates and other do not) url = '/api/users/courses' self.data.get_course().start_date = None course_2 = self.data.create_course() course_2.start_date = datetime.datetime.now() self.data.enrol_instructor(self.data.get_authorized_instructor(), course_2) course_3 = self.data.create_course() course_3.start_date = datetime.datetime.now() + datetime.timedelta(days=10) self.data.enrol_instructor(self.data.get_authorized_instructor(), course_3) courses = [course_3, course_2, self.data.get_course()] rv = self.client.get(url) self.assert200(rv) self.assertEqual(3, len(rv.json['objects'])) for index, result in enumerate(rv.json['objects']): self.assertEqual(courses[index].uuid, result['id']) # test sort order (when course with no start date has assignment) assignment = AssignmentFactory( user=self.data.get_authorized_instructor(), course=self.data.get_course(), answer_start=(datetime.datetime.now() + datetime.timedelta(days=5)) ) db.session.commit() courses = [course_3, self.data.get_course(), course_2] rv = self.client.get(url) self.assert200(rv) self.assertEqual(3, len(rv.json['objects'])) for index, result in enumerate(rv.json['objects']): self.assertEqual(courses[index].uuid, result['id']) # test authorized student with self.login(self.data.get_authorized_student().username): url = '/api/users/courses' rv = self.client.get(url) self.assert200(rv) self.assertEqual(1, len(rv.json['objects'])) self.assertEqual(self.data.get_course().name, rv.json['objects'][0]['name']) # test authorized teaching assistant with self.login(self.data.get_authorized_ta().username): url = '/api/users/courses' rv = self.client.get(url) self.assert200(rv) self.assertEqual(1, len(rv.json['objects'])) self.assertEqual(self.data.get_course().name, rv.json['objects'][0]['name']) # test dropped instructor with self.login(self.data.get_dropped_instructor().username): url = '/api/users/courses' rv = self.client.get(url) self.assert200(rv) self.assertEqual(0, len(rv.json['objects'])) def test_get_teaching_course(self): url = '/api/users/courses/teaching' # test login required rv = self.client.get(url) self.assert401(rv) # test student with self.login(self.data.get_authorized_student().username): rv = self.client.get(url) self.assert200(rv) self.assertEqual(0, len(rv.json['courses'])) # test TA with self.login(self.data.get_authorized_ta().username): rv = self.client.get(url) self.assert200(rv) self.assertEqual(1, len(rv.json['courses'])) # test instructor with self.login(self.data.get_authorized_instructor().username): rv = self.client.get(url) self.assert200(rv) self.assertEqual(1, len(rv.json['courses'])) # test admin with self.login('root'): rv = self.client.get(url) self.assert200(rv) self.assertEqual(2, len(rv.json['courses'])) def test_update_password(self): url = '/api/users/{}/password' data = { 'oldpassword': '******', 'newpassword': '******' } # test login required rv = self.client.post( url.format(self.data.authorized_instructor.uuid), data=json.dumps(data), content_type='application/json') self.assert401(rv) # test student update password with self.login(self.data.authorized_student.username): # test without old password rv = self.client.post( url.format(self.data.authorized_student.uuid), data=json.dumps({'newpassword': '******'}), content_type='application/json') self.assert403(rv) self.assertEqual( 'The old password is incorrect or you do not have permission to change password.', rv.json['error']) # test incorrect old password invalid_input = data.copy() invalid_input['oldpassword'] = '******' rv = self.client.post( url.format(self.data.authorized_student.uuid), data=json.dumps(invalid_input), content_type='application/json') self.assert403(rv) self.assertEqual( 'The old password is incorrect or you do not have permission to change password.', rv.json['error']) # test with old password rv = self.client.post( url.format(self.data.authorized_student.uuid), data=json.dumps(data), content_type='application/json') self.assert200(rv) self.assertEqual(self.data.get_authorized_student().uuid, rv.json['id']) # test instructor update password with self.login(self.data.get_authorized_instructor().username): rv = self.client.post(url.format(str(999)), data=json.dumps(data), content_type='application/json') self.assert404(rv) # test instructor changes the password of a student in the course rv = self.client.post( url.format(self.data.get_authorized_student().uuid), data=json.dumps(data), content_type='application/json') self.assert200(rv) self.assertEqual(self.data.get_authorized_student().uuid, rv.json['id']) # test changing own password rv = self.client.post( url.format(self.data.get_authorized_instructor().uuid), data=json.dumps(data), content_type='application/json') self.assert200(rv) self.assertEqual(self.data.get_authorized_instructor().uuid, rv.json['id']) # test instructor changes the password of a student not in the course with self.login(self.data.get_unauthorized_instructor().username): rv = self.client.post( url.format(self.data.get_authorized_student().uuid), data=json.dumps(data), content_type='application/json') self.assert403(rv) self.assertEqual( '<p>{} does not have edit access to {}</p>'.format(self.data.get_unauthorized_instructor().username, self.data.get_authorized_student().username), rv.json['message']) # test admin update password with self.login('root'): # test admin changes student password without old password rv = self.client.post( url.format(self.data.get_authorized_student().uuid), data=json.dumps({'newpassword': '******'}), content_type='application/json') self.assert200(rv) self.assertEqual(self.data.get_authorized_student().uuid, rv.json['id']) # test update password of user with no compair login auth_data = ThirdPartyAuthTestData() cas_user_auth = auth_data.create_cas_user_auth(SystemRole.student) user = cas_user_auth.user self.data.enrol_user(user, self.data.get_course(), CourseRole.student) url = 'api/users/' + user.uuid + '/password' # update own password as cas user with self.cas_login(cas_user_auth.unique_identifier): # cannot change password rv = self.client.post(url, data=json.dumps(data), content_type='application/json') self.assert400(rv) self.assertEqual("Cannot update password. User does not use ComPAIR account login authentication method.", rv.json['error']) def test_get_edit_button(self): url = '/api/users/' + self.data.get_authorized_student().uuid + '/edit' # test login required rv = self.client.get(url) self.assert401(rv) # test invalid user id with self.login(self.data.get_unauthorized_student().username): invalid_url = '/api/users/999/edit' rv = self.client.get(invalid_url) self.assert404(rv) # test unavailable button rv = self.client.get(url) self.assert200(rv) self.assertFalse(rv.json['available']) # test available button with self.login(self.data.get_authorized_instructor().username): rv = self.client.get(url) self.assert200(rv) self.assertTrue(rv.json['available']) def _verify_permissions(self, user_id, permissions): user = User.query.get(user_id) with self.app.app_context(): # can't figure out how to get into logged in app context, so just force a login here login_user(user, force=True) admin = user.system_role == SystemRole.sys_admin for model_name, operations in permissions.items(): for operation, permission in operations.items(): expected = True try: ensure(operation, model_name) except Unauthorized: expected = False expected = expected or admin self.assertEqual( permission['global'], expected, "Expected permission " + operation + " on " + model_name + " to be " + str(expected)) # undo the forced login earlier logout_user() def _generate_search_users(self, user): return { 'id': user.uuid, 'display': user.fullname + ' (' + user.displayname + ') - ' + user.system_role, 'name': user.fullname}