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
Beispiel #2
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
        )
Beispiel #3
0
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])
Beispiel #4
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']
Beispiel #5
0
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])
Beispiel #6
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'],
                },
            )
Beispiel #7
0
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)
Beispiel #8
0
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])
Beispiel #9
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])
Beispiel #10
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'] == []
Beispiel #11
0
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)
Beispiel #12
0
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'}
        )
Beispiel #13
0
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
        )
Beispiel #14
0
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)
Beispiel #15
0
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,
            )
Beispiel #16
0
def test_parse_entry_less_groups_than_in_header():
    g = helpers.create_group(0)

    RootGroup().parse(g.to_bytearray(), 2, 1)
Beispiel #17
0
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,
        )
Beispiel #18
0
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)
Beispiel #19
0
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
Beispiel #21
0
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)
Beispiel #22
0
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