def test_delete_submission_of_group( lti1p3_provider, describe, logged_in, admin_user, watch_signal, stub_function, test_client, session, tomorrow ): with describe('setup'), logged_in(admin_user): watch_signal(signals.WORK_CREATED, clear_all_but=[]) watch_signal(signals.GRADE_UPDATED, clear_all_but=[]) watch_signal(signals.USER_ADDED_TO_COURSE, clear_all_but=[]) stub_function( pylti1p3.service_connector.ServiceConnector, 'get_access_token', lambda: '' ) stub_passback = stub_function( pylti1p3.assignments_grades.AssignmentsGradesService, 'put_grade' ) course, course_conn = helpers.create_lti1p3_course( test_client, session, lti1p3_provider ) assig = helpers.create_lti1p3_assignment( session, course, state='done', deadline=tomorrow ) # Create some users and let them have individual submissions g_user1 = helpers.create_lti1p3_user(session, lti1p3_provider) g_user2 = helpers.create_lti1p3_user(session, lti1p3_provider) for user in [g_user1, g_user2]: course_conn.maybe_add_user_to_course(user, ['Learner']) sub = helpers.create_submission(test_client, assig, for_user=user) helpers.to_db_object(sub, m.Work).set_grade( 5.0, m.User.resolve(admin_user) ) # Place the users in a group, with a submission with a different grade # than their individual submission. gset = helpers.create_group_set(test_client, course, 1, 2, [assig]) helpers.create_group(test_client, gset, [g_user1, g_user2]) gsub = helpers.create_submission(test_client, assig, for_user=g_user2) helpers.to_db_object(gsub, m.Work).set_grade( 6.0, m.User.resolve(admin_user) ) session.commit() with describe( 'deleting group submission passes back individual submissions' ): with logged_in(admin_user): test_client.req( 'delete', f'/api/v1/submissions/{helpers.get_id(gsub)}', 204 ) assert stub_passback.called_amount == 2 # The grade passed back is that of their individual submission assert stub_passback.args[0][0].get_score_given() == 5.0 assert stub_passback.args[1][0].get_score_given() == 5.0
def test_list_groups( test_client, logged_in, bs_course, error_template, teacher_user, assignment, session ): def make_empty_user(): return create_user_with_perms(session, [], bs_course) user_others_too = create_user_with_perms( session, [CPerm.can_view_others_groups], bs_course, ) user_no_perm = make_empty_user() with logged_in(teacher_user): group_set = create_group_set(test_client, bs_course.id, 2, 4) empty_groups = [ create_group(test_client, group_set['id'], []) for _ in range(4) ] other_user_groups = [ create_group( test_client, group_set['id'], [make_empty_user().id, make_empty_user().id] ) ] user_no_perm_group = [ create_group( test_client, group_set['id'], [ make_empty_user().id, make_empty_user().id, user_no_perm.id, make_empty_user().id, make_empty_user().id, ] ) ] with logged_in(user_no_perm): test_client.req( 'get', f'/api/v1/group_sets/{group_set["id"]}/groups/', 200, result=empty_groups + user_no_perm_group ) with logged_in(user_others_too): test_client.req( 'get', f'/api/v1/group_sets/{group_set["id"]}/groups/', 200, result=empty_groups + other_user_groups + user_no_perm_group )
def test_parse_group(): g = helpers.create_group() root = RootGroup().parse(g.to_bytearray(), 1, 0) groups = root.get_groups() assert len(groups) == 1 helpers.equal_groups(g, groups[0])
def test_submit_with_small_group( test_client, session, logged_in, teacher_user, course, error_template, assignment ): user_no_group = create_user_with_perms( session, [CPerm.can_submit_own_work, CPerm.can_see_assignments], course ) user_with_group = create_user_with_perms( session, [CPerm.can_submit_own_work, CPerm.can_see_assignments], course ) with logged_in(teacher_user): g_set = create_group_set(test_client, course.id, 1, 1) test_client.req( 'patch', f'/api/v1/assignments/{assignment.id}', 200, data={'group_set_id': g_set['id']} ) g1 = create_group( test_client, g_set['id'], [user_with_group.id], ) with logged_in(user_no_group): # Can submit without group, you are simply the author (not a group) sub = create_submission(test_client, assignment.id) assert sub['user']['id'] == user_no_group.id with logged_in(user_with_group): # But when submitting as a user in a group the group should still be # the author sub = create_submission(test_client, assignment.id) assert sub['user']['group']['id'] == g1['id']
def test_parse_metaentry(): g = helpers.create_group() e = helpers.create_metaentry(g) root = RootGroup().parse(g.to_bytearray() + e.to_bytearray(), 1, 1) entries = root.get_groups()[0].get_meta_entries() assert len(entries) == 1 helpers.equal_entries(e, entries[0])
def test_add_test_student_to_group( session, test_client, logged_in, assignment, teacher_user, error_template, describe, monkeypatch_celery ): c_id = assignment.course.id with logged_in(teacher_user): g_set = create_group_set(test_client, c_id, 1, 4) res = create_submission( test_client, assignment.id, is_test_submission=True, ) test_student = res['user'] with describe('new group with a test student cannot be created'): res = test_client.req( 'post', f'/api/v1/group_sets/{g_set["id"]}/group', 400, result=error_template, data={ 'member_ids': [test_student['id']], }, ) with describe( 'new group with a test student and other students cannot be created' ): u1 = create_user_with_perms( session, [CPerm.can_edit_own_groups], assignment.course ) res = test_client.req( 'post', f'/api/v1/group_sets/{g_set["id"]}/group', 400, result=error_template, data={ 'member_ids': [test_student['id'], u1.id], }, ) with describe('test student can not be added to existing group'): g1 = create_group( test_client, g_set['id'], [], ) res = test_client.req( 'post', f'/api/v1/groups/{g1["id"]}/member', 400, result=error_template, data={ 'username': test_student['username'], }, )
def test_change_name_of_group(test_client, logged_in, error_template, prog_course, session, teacher_user): new_name = f'NEW_NAME-{uuid.uuid4()}' def check_name(name): res = g1 if name is None else {**g1, 'name': name} with logged_in(teacher_user): return test_client.req('get', f'/api/v1/groups/{g1["id"]}', 200, result=res) u1 = create_user_with_perms(session, [CPerm.can_edit_own_groups], prog_course) u2 = create_user_with_perms(session, [CPerm.can_edit_own_groups], prog_course) u3 = create_user_with_perms(session, [CPerm.can_edit_own_groups], prog_course) with logged_in(teacher_user): g_set = create_group_set(test_client, prog_course.id, 2, 4) g1 = create_group( test_client, g_set['id'], [u1.id, u2.id], ) with logged_in(u3): test_client.req('post', f'/api/v1/groups/{g1["id"]}/name', 403, data={'name': new_name}) check_name(None) with logged_in(u1): # u1 is member so it can change the name old_name = g1['name'] test_client.req('post', f'/api/v1/groups/{g1["id"]}/name', 200, data={'name': new_name}) check_name(new_name) # Reset back to old name test_client.req('post', f'/api/v1/groups/{g1["id"]}/name', 200, data={'name': old_name}) check_name(old_name) test_client.req( 'post', f'/api/v1/groups/{g1["id"]}/name', 400, data={'name': 'sh'}, # This name is too short result=error_template) check_name(None)
def test_parse_with_subgroups(): g = helpers.create_group(0) g2 = helpers.create_group(1) g3 = helpers.create_group(1) e = helpers.create_entry(g3) root = RootGroup().parse(g.to_bytearray() + g2.to_bytearray() + g3.to_bytearray() + e.to_bytearray(), 3, 1) groups = root.get_groups() assert len(groups) == 1 rg = groups[0] groups = rg.get_groups() print len(groups) assert len(groups) == 2 pg = groups[1] helpers.equal_groups(g3, pg) entries = pg.get_entries() assert len(entries) == 1 helpers.equal_entries(e, entries[0])
def test_parse_entry(): g = helpers.create_group() e = helpers.create_entry(g) root = RootGroup().parse(g.to_bytearray() + e.to_bytearray(), 1, 1) groups = root.get_groups() assert len(groups) == 1 g2 = groups[0] helpers.equal_groups(g, g2) entries = g2.get_entries() assert len(entries) == 1 helpers.equal_entries(e, entries[0])
def test_remove_user_from_group( test_client, session, logged_in, teacher_user, prog_course, error_template, assignment, monkeypatch_celery ): def make_user(): return create_user_with_perms( session, [ CPerm.can_edit_own_groups, CPerm.can_submit_own_work, CPerm.can_see_assignments ], prog_course ) u1 = make_user() u2 = make_user() u3 = make_user() u4 = make_user() with logged_in(teacher_user): g_set = create_group_set(test_client, prog_course.id, 2, 4) g1 = create_group( test_client, g_set['id'], [u1.id, u2.id, u3.id, u4.id], ) test_client.req( 'patch', f'/api/v1/assignments/{assignment.id}', 200, data={'group_set_id': g_set['id']} ) with logged_in(u3) as me: # Cannot remove other users from own group test_client.req( 'delete', f'/api/v1/groups/{g1["id"]}/members/{u1.id}', 403, result=error_template ) # Can remove self from group g1 = test_client.req( 'delete', f'/api/v1/groups/{g1["id"]}/members/{me.id}', 200, ) # Cannot remove self twice test_client.req( 'delete', f'/api/v1/groups/{g1["id"]}/members/{me.id}', 404, ) with logged_in(u2) as me: # Make sure group has a submission sub = create_submission(test_client, assignment.id) assert sub['user']['group']['id'] == g1['id'] # Cannot remove self from group as the group has submission test_client.req( 'delete', f'/api/v1/groups/{g1["id"]}/members/{me.id}', 403, result=error_template ) with logged_in(teacher_user): # Group is has three members so this is possible g1 = test_client.req( 'delete', f'/api/v1/groups/{g1["id"]}/members/{u1.id}', 200, ) # Group only has two member with and has a submission so this is not # possible test_client.req( 'delete', f'/api/v1/groups/{g1["id"]}/members/{u2.id}', 400, result=error_template ) test_client.req('delete', f'/api/v1/submissions/{sub["id"]}', 204) # The submission is deleted so this is now possible g1 = test_client.req( 'delete', f'/api/v1/groups/{g1["id"]}/members/{u4.id}', 200, ) g1 = test_client.req( 'delete', f'/api/v1/groups/{g1["id"]}/members/{u2.id}', 200, ) assert g1['members'] == []
def test_parse_entry_with_no_parent(): g = helpers.create_group(0) e = helpers.create_entry(g) e.group_id = 1 RootGroup().parse(g.to_bytearray() + e.to_bytearray(), 1, 1)
def test_submit_with_group( test_client, session, logged_in, teacher_user, course, error_template, assignment, monkeypatch_celery ): user_full_group = create_user_with_perms( session, [CPerm.can_submit_own_work, CPerm.can_see_assignments], course ) user_empty_group = create_user_with_perms( session, [CPerm.can_submit_own_work, CPerm.can_see_assignments], course ) user_no_group = create_user_with_perms( session, [CPerm.can_submit_own_work, CPerm.can_see_assignments], course ) user_no_perms = create_user_with_perms( session, [CPerm.can_see_assignments], course ) with logged_in(teacher_user): g_set = create_group_set(test_client, course.id, 2, 4) test_client.req( 'patch', f'/api/v1/assignments/{assignment.id}', 200, data={'group_set_id': g_set['id']} ) g1 = create_group( test_client, g_set['id'], [user_full_group.id, user_no_perms.id], ) create_group(test_client, g_set['id'], [user_empty_group.id]) with logged_in(user_empty_group): err = create_submission(test_client, assignment.id, err=400) assert 'enough members' in err['message'] with logged_in(user_no_perms): # Can't submit even if some users in the group can err = create_submission(test_client, assignment.id, err=403) with logged_in(user_no_group): # Can't submit as a user without group as the minimum size is 2 err = create_submission(test_client, assignment.id, err=404) assert 'group was found' in err['message'] with logged_in(user_full_group): # User can submit submission sub = create_submission(test_client, assignment.id) # Make sure submission is done as the group, not as the user assert sub['user']['group']['id'] == g1['id'] # Make sure the user can see the just submitted submission test_client.req( 'get', f'/api/v1/assignments/{assignment.id}/submissions/?extended', 200, result=[sub] ) test_client.req( 'get', f'/api/v1/submissions/{sub["id"]}?extended', 200, result=sub ) files = test_client.req( 'get', f'/api/v1/submissions/{sub["id"]}/files/', 200 ) while 'entries' in files: files = files['entries'][0] code_id = files['id'] response = test_client.get(f'/api/v1/code/{code_id}') assert response.status_code == 200 with logged_in(teacher_user): test_client.req( 'put', f'/api/v1/code/{code_id}/comments/3', 204, data={'comment': 'Lekker gewerkt pik!'} ) # Set state to done so the student can see its feedback test_client.req( 'patch', f'/api/v1/assignments/{assignment.id}', 200, data={'state': 'done'} ) with logged_in(user_full_group): # Make sure we can see comments on the files test_client.req( 'get', f'/api/v1/code/{code_id}?type=feedback', 200, result={'3': {'line': 3, 'msg': 'Lekker gewerkt pik!'}} ) with logged_in(teacher_user): # Reset state back test_client.req( 'patch', f'/api/v1/assignments/{assignment.id}', 200, data={'state': 'open'} )
def test_add_user_to_group( test_client, session, logged_in, teacher_user, prog_course, error_template ): user_only_own = create_user_with_perms( session, [CPerm.can_edit_own_groups], prog_course ) user_other_too = create_user_with_perms( session, [CPerm.can_edit_others_groups], prog_course ) nobody = create_user_with_perms(session, [], prog_course) with logged_in(teacher_user): g_set = create_group_set(test_client, prog_course.id, 2, 4) g1 = create_group( test_client, g_set['id'], [create_user_with_perms(session, [], prog_course).id], ) g2 = create_group(test_client, g_set['id'], []) with logged_in(user_only_own) as u: # Can add to group with members not including the user. g1 = test_client.req( 'post', f'/api/v1/groups/{g1["id"]}/member', 200, data={'username': u.username}, ) # Cannot add other user to group test_client.req( 'post', f'/api/v1/groups/{g2["id"]}/member', 403, data={'username': nobody.username} ) with logged_in(user_other_too) as u: # Can add to empty group g2 = test_client.req( 'post', f'/api/v1/groups/{g2["id"]}/member', 200, data={'username': u.username}, ) # Can other user to a group test_client.req( 'post', f'/api/v1/groups/{g2["id"]}/member', 200, data={'username': nobody.username} ) # Cannot add virtual user to a group test_client.req( 'post', f'/api/v1/groups/{g1["id"]}/member', 400, data={ 'username': m.Group.query.get(g2['id']).virtual_user.username } ) # user_only_own is already in g1 so we can't add this user to any group # too. for g in [g1, g2]: err = test_client.req( 'post', f'/api/v1/groups/{g["id"]}/member', 400, data={ 'username': user_only_own.username, }, result=error_template, ) assert 'already in a group' in err['message'] # Fill group g1 for _ in range(2): test_client.req( 'post', f'/api/v1/groups/{g1["id"]}/member', 200, data={ 'username': create_user_with_perms(session, [], prog_course).username } ) # Group is full so we can't add another user test_client.req( 'post', f'/api/v1/groups/{g1["id"]}/member', 400, data={ 'username': create_user_with_perms(session, [], prog_course).username }, result=error_template )
def test_delete_group( test_client, logged_in, prog_course, session, teacher_user, error_template, app, monkeypatch_celery ): user_only_own = create_user_with_perms( session, [ CPerm.can_edit_own_groups, CPerm.can_submit_own_work, CPerm.can_see_assignments ], prog_course ) user_other_too = create_user_with_perms( session, [CPerm.can_edit_others_groups, CPerm.can_see_assignments], prog_course ) with logged_in(teacher_user): assig_id = [ a for a in test_client. req('get', f'/api/v1/courses/{prog_course.id}/assignments/', 200) if a['state'] == 'submitting' ][0]['id'] with logged_in(teacher_user): group_set = create_group_set(test_client, prog_course.id, 2, 4) test_client.req( 'patch', f'/api/v1/assignments/{assig_id}', 200, data={'group_set_id': group_set['id']} ) group_own_id = create_group( test_client, group_set["id"], [ create_user_with_perms(session, [], prog_course).id, user_only_own.id, ] )['id'] group_others_id = create_group( test_client, group_set["id"], [ create_user_with_perms(session, [], prog_course).id, create_user_with_perms(session, [], prog_course).id, ] )['id'] group_empty_id = create_group(test_client, group_set["id"], [])['id'] with logged_in(user_only_own): sub = create_submission(test_client, assig_id) assert sub['user']['group'] with logged_in(user_only_own): # Cannot delete with submission test_client.req( 'delete', f'/api/v1/groups/{group_own_id}', 400, result=error_template ) # Cannot delete non empty group of other members test_client.req( 'delete', f'/api/v1/groups/{group_others_id}', 403, result=error_template ) # Can delete empty group test_client.req('delete', f'/api/v1/groups/{group_empty_id}', 204) with logged_in(user_other_too): # Cannot delete with submission test_client.req( 'delete', f'/api/v1/groups/{group_own_id}', 400, result=error_template ) # Can delete non empty group of other members test_client.req('delete', f'/api/v1/groups/{group_others_id}', 204) with logged_in(teacher_user): test_client.req('delete', f'/api/v1/submissions/{sub["id"]}', 204) with logged_in(user_only_own): # Can delete now submission has been deleted test_client.req('delete', f'/api/v1/groups/{group_own_id}', 204)
def test_delete_group_set( test_client, logged_in, teacher_user, user_with_perms, prog_course, session, request, error_template ): assigs = list(prog_course.assignments)[:2] assert len(assigs) == 2 with logged_in(teacher_user): group_set = create_group_set(test_client, prog_course.id, 2, 4) for assig in assigs: test_client.req( 'patch', f'/api/v1/assignments/{assig.id}', 200, data={'group_set_id': group_set['id']} ) groups = [ create_group( test_client, group_set["id"], [create_user_with_perms(session, [], prog_course).id] ) for _ in range(2) ] perm_err = request.node.get_closest_marker('perm_error') has_err = bool(perm_err) if perm_err: status = 403 else: status = 400 with logged_in(user_with_perms): test_client.req( 'delete', f'/api/v1/group_sets/{group_set["id"]}', status, result=error_template ) with logged_in(teacher_user): test_client.req( 'get', f'/api/v1/group_sets/{group_set["id"]}', 200, ) for assig in assigs: test_client.req( 'patch', f'/api/v1/assignments/{assig.id}', 200, data={'group_set_id': None} ) with logged_in(user_with_perms): # Still fails because there are groups test_client.req( 'delete', f'/api/v1/group_sets/{group_set["id"]}', status, result=error_template ) with logged_in(teacher_user): for group in groups: test_client.req( 'delete', f'/api/v1/groups/{group["id"]}', 204, ) # Should now work as there are no groups anymore if perm_err: status = 403 else: status = 204 with logged_in(user_with_perms): test_client.req( 'delete', f'/api/v1/group_sets/{group_set["id"]}', status, result=error_template if has_err else None ) with logged_in(teacher_user): if not has_err: test_client.req( 'get', f'/api/v1/group_sets/{group_set["id"]}', 404, )
def test_parse_entry_less_groups_than_in_header(): g = helpers.create_group(0) RootGroup().parse(g.to_bytearray(), 2, 1)
def test_seeing_teacher_revision_in_group( test_client, session, logged_in, teacher_user, course, error_template, assignment, describe ): with describe('setup'): user_other_group = create_user_with_perms( session, [ CPerm.can_submit_own_work, CPerm.can_view_own_teacher_files, CPerm.can_see_assignments ], course ) user_with_perm = create_user_with_perms( session, [ CPerm.can_submit_own_work, CPerm.can_view_own_teacher_files, CPerm.can_see_assignments ], course ) user_without_perm = create_user_with_perms( session, [CPerm.can_submit_own_work, CPerm.can_see_assignments], course ) with logged_in(teacher_user): g_set = create_group_set(test_client, course.id, 1, 2) test_client.req( 'patch', f'/api/v1/assignments/{assignment.id}', 200, data={'group_set_id': g_set['id']} ) g1 = create_group( test_client, g_set['id'], [user_with_perm, user_without_perm], ) with logged_in(user_with_perm): sub = create_submission(test_client, assignment.id) # Submission is by the group assert sub['user']['group']['id'] == g1['id'] student_files = test_client.req( 'get', f'/api/v1/submissions/{sub["id"]}/files/', 200 ) with describe('Cannot view teacher files while assig is open' ), logged_in(user_with_perm): test_client.req( 'get', f'/api/v1/submissions/{sub["id"]}/files/?owner=teacher', 403, result=error_template, ) with describe('Can see teacher files after assig is done' ), logged_in(user_with_perm): assignment.set_state_with_string('done') session.commit() test_client.req( 'get', f'/api/v1/submissions/{sub["id"]}/files/?owner=teacher', 200, result=student_files, ) # This marks all files as from the student revision, i.e. there are no # teacher files. m.File.query.filter( m.File.work_id == sub['id'], m.File.parent_id.isnot(None) ).update({'fileowner': m.FileOwner.student}) test_client.req( 'get', f'/api/v1/submissions/{sub["id"]}/files/?owner=teacher', 200, result={ **student_files, 'entries': [], } ) with describe( 'User without permission to see own teacher files gets an error' ), logged_in(user_without_perm): test_client.req( 'get', f'/api/v1/submissions/{sub["id"]}/files/?owner=teacher', 403, result=error_template, ) with describe('User from other group cannot see teacher files' ), logged_in(user_other_group): test_client.req( 'get', f'/api/v1/submissions/{sub["id"]}/files/?owner=teacher', 403, result=error_template, )
def test_change_name_of_group( test_client, logged_in, error_template, prog_course, session, teacher_user ): new_name = f'NEW_NAME-{uuid.uuid4()}' def check_name(name): name_to_check = g1['name'] if name is None else name with logged_in(teacher_user): assert g_model.get_readable_name() == f'group "{name_to_check}"' return test_client.req( 'get', f'/api/v1/groups/{g1["id"]}', 200, result={**g1, 'name': name_to_check} ) u1 = create_user_with_perms( session, [CPerm.can_edit_own_groups], prog_course ) u2 = create_user_with_perms( session, [CPerm.can_edit_own_groups], prog_course ) u3 = create_user_with_perms( session, [CPerm.can_edit_own_groups], prog_course ) with logged_in(teacher_user): g_set = create_group_set(test_client, prog_course.id, 2, 4) g1 = create_group( test_client, g_set['id'], [u1.id, u2.id], ) g_model = LocalProxy( lambda: m.User.query.get(g1['virtual_user']['id']) ) with logged_in(u3): test_client.req( 'post', f'/api/v1/groups/{g1["id"]}/name', 403, data={'name': new_name} ) check_name(None) with logged_in(u1): # u1 is member so it can change the name old_name = g1['name'] test_client.req( 'post', f'/api/v1/groups/{g1["id"]}/name', 200, data={'name': new_name} ) check_name(new_name) # Reset back to old name test_client.req( 'post', f'/api/v1/groups/{g1["id"]}/name', 200, data={'name': old_name} ) check_name(old_name) test_client.req( 'post', f'/api/v1/groups/{g1["id"]}/name', 400, data={'name': 'sh'}, # This name is too short result=error_template ) check_name(None)
def test_parse_group_with_no_parent(): g = helpers.create_group(0) g2 = helpers.create_group(2) RootGroup().parse(g.to_bytearray() + g2.to_bytearray(), 2, 0)
def test_passing_back_all_grades( lti1p3_provider, describe, logged_in, admin_user, watch_signal, stub_function, test_client, session, tomorrow, make_function_spy ): with describe('setup'), logged_in(admin_user): watch_signal(signals.WORK_CREATED, clear_all_but=[]) watch_signal(signals.GRADE_UPDATED, clear_all_but=[]) watch_signal(signals.USER_ADDED_TO_COURSE, clear_all_but=[]) watch_signal(signals.WORK_DELETED, clear_all_but=[]) signal = watch_signal( signals.ASSIGNMENT_STATE_CHANGED, clear_all_but=[m.LTI1p3Provider._passback_grades] ) stub_get_acccess_token = stub_function( pylti1p3.service_connector.ServiceConnector, 'get_access_token', lambda: '' ) stub_passback = make_function_spy( pylti1p3.assignments_grades.AssignmentsGradesService, 'put_grade', pass_self=True ) # Make a session with a response that returns json when the `json` # method is called req_session = requests_stubs.session_maker()() req_session.Response.json = lambda _=None: {} stub_requests_post = stub_function(requests, 'post', req_session.post) course, course_conn = helpers.create_lti1p3_course( test_client, session, lti1p3_provider ) assig = helpers.create_lti1p3_assignment( session, course, state='hidden', deadline=tomorrow ) user1 = helpers.create_lti1p3_user(session, lti1p3_provider) user2 = helpers.create_lti1p3_user(session, lti1p3_provider) user3 = helpers.create_lti1p3_user(session, lti1p3_provider) all_students = [user1, user2, user3] for u in all_students: course_conn.maybe_add_user_to_course(u, ['Learner']) lti_user_ids = [ m.UserLTIProvider.query.filter_by(user=u).one().lti_user_id for u in all_students ] gset = helpers.create_group_set(test_client, course, 1, 2, [assig]) helpers.create_group(test_client, gset, [user1, user3]) sub = helpers.to_db_object( helpers.create_submission(test_client, assig, for_user=user1), m.Work ) sub.set_grade(2.5, admin_user) # Make sure user2 does not have a non deleted submission user2_sub = helpers.create_submission( test_client, assig, for_user=user2 ) test_client.req( 'delete', f'/api/v1/submissions/{helpers.get_id(user2_sub)}', 204 ) with describe('changing assignment state to "open" does not passback'): assig.set_state_with_string('open') assert signal.was_send_once assert not stub_passback.called assert not stub_get_acccess_token.called with describe('changing to done does passback'): assig.set_state_with_string('done') assert signal.was_send_once assert stub_passback.called_amount == len(all_students) # Calls should be cached assert stub_get_acccess_token.called_amount == 1 p1, p2, p3 = stub_passback.args assert p1[0] != p2[0] != p3[0] assert p1[0].get_score_given() == 2.5 assert p2[0].get_score_given() == 2.5 assert {p1[0].get_user_id(), p2[0].get_user_id()} == {lti_user_ids[0], lti_user_ids[2]} # Does not have a submission assert p3[0].get_score_given() is None assert p3[0].get_user_id() == lti_user_ids[1] with describe('toggling to open and done should do a new passback'): assig.set_state_with_string('open') assert signal.was_send_once assert not stub_passback.called assert not stub_get_acccess_token.called assig.set_state_with_string('done') assert signal.was_send_n_times(2) assert stub_passback.called # access token should still be cached assert not stub_get_acccess_token.called
def test_webhooks_group_join_lti(describe, logged_in, session, test_client, monkeypatch, stub_function_class, admin_user, app): with describe('setup'): course = helpers.create_lti_course(session, app) assig = helpers.create_lti_assignment(session, course, state='open') teacher = helpers.create_user_with_role(session, 'Teacher', [course]) student1 = helpers.create_user_with_role(session, 'Student', [course]) student2 = helpers.create_user_with_role(session, 'Student', [course]) url = f'/api/v1/assignments/{assig.id}' with logged_in(teacher): group_set = helpers.create_group_set(test_client, course, 1, 2, [assig]) group = m.Group.query.get( helpers.create_group(test_client, group_set, [])['id']) test_client.req( 'patch', url, 200, data={ 'files_upload_enabled': True, 'webhook_upload_enabled': True, }, ) with describe('Can join group without webhook'): with logged_in(student2): test_client.req( 'post', f'/api/v1/groups/{group.id}/member', 200, data={'username': student2.username}, ) with describe('Cannot make webhook with unfinished group'): with logged_in(student2): test_client.req('post', f'{url}/webhook_settings?webhook_type=git', 400) session.add( m.AssignmentResult( user_id=student2.id, assignment_id=assig.id, sourcedid=str(uuid.uuid4()), )) session.commit() with logged_in(student2): group_webhook = test_client.req( 'post', f'{url}/webhook_settings?webhook_type=git', 200) with describe( 'Cannot join group as it has a webhook but user does not have sourcedid' ): with logged_in(student1): test_client.req( 'post', f'/api/v1/groups/{group.id}/member', 400, data={'username': student1.username}, ) with logged_in(student1): # Cannot create individual webhook test_client.req('post', f'{url}/webhook_settings?webhook_type=git', 400) session.add( m.AssignmentResult( user_id=student1.id, assignment_id=assig.id, sourcedid=str(uuid.uuid4()), )) session.commit() with logged_in(student1): _, rv = test_client.req( 'post', f'/api/v1/groups/{group.id}/member', 200, data={'username': student1.username}, include_response=True, ) assert 'warning' not in rv.headers with logged_in(student1): # And we now get the group webhook config. test_client.req('post', f'{url}/webhook_settings?webhook_type=git', 200, result=group_webhook)
def test_git_in_groups(basic, describe, logged_in, session, test_client, monkeypatch, stub_function_class): with describe('setup'): course, assig, teacher, student = basic student2 = helpers.create_user_with_role(session, 'Student', [course]) stub_clone = stub_function_class() monkeypatch.setattr(p.tasks, 'clone_commit_as_submission', stub_clone) url = f'/api/v1/assignments/{assig.id}' with logged_in(student): student1_webhook_id = test_client.req( 'post', f'{url}/webhook_settings?webhook_type=git', 200)['id'] with logged_in(teacher): group_set = helpers.create_group_set(test_client, course, 1, 2, [assig]) group = m.Group.query.get( helpers.create_group(test_client, group_set, [student2])['id']) with logged_in(student2): group_webhook_id = test_client.req( 'post', f'{url}/webhook_settings?webhook_type=git', 200)['id'] def do_request(webhook_id, user_id, status=200): data = get_request_data('github', 'push') kwargs = {'content_type': 'application/json'} webhook = m.WebhookBase.query.get(webhook_id) assert webhook is not None assert user_id == webhook.user_id signature = hmac.new(webhook.secret.encode(), data.encode(), 'sha1').hexdigest() return test_client.req( 'post', f'/api/v1/webhooks/{webhook_id}', status, real_data=data, headers={ 'X-GitHub-Delivery': str(uuid.uuid4()), 'X-GitHub-Event': 'push', 'X-Hub-Signature': f'sha1={signature}', }, **kwargs, ) with describe('It should work for users not in a group'): do_request(student1_webhook_id, student.id) assert stub_clone.called with describe('It should work for users in a group'): do_request(group_webhook_id, group.virtual_user.id) assert stub_clone.called with describe('After joining group old webhook stops working'): with logged_in(student): _, rv = test_client.req('post', f'/api/v1/groups/{group.id}/member', 200, data={'username': student.username}, include_response=True) assert 'warning' in rv.headers assert 'existing webhook for ' in rv.headers['warning'] do_request(student1_webhook_id, student.id, status=400) assert not stub_clone.called