Exemplo n.º 1
0
def test_create_auto_test_suite(basic, test_client, logged_in, describe):
    course, assig_id, teacher, student = basic

    with describe('setup'), logged_in(teacher):
        test = helpers.create_auto_test(test_client, assig_id, 1, 0)
        rubric = test_client.req('put',
                                 f'/api/v1/assignments/{assig_id}/rubrics/',
                                 200,
                                 data=helpers.get_simple_rubric())
        set_id = test['sets'][0]['id']
        url = f'/api/v1/auto_tests/{test["id"]}'

    with describe('students should not be able to create suite'), logged_in(
            student):
        test_client.req('patch', f'{url}/sets/{set_id}/suites/', 403)
        with describe('No suite should be created'), logged_in(teacher):
            test_client.req('get', url, 200, result=test)

    with describe('teachers should be able to create empty suites'), logged_in(
            teacher):
        test_client.req('patch',
                        f'{url}/sets/{set_id}/suites/',
                        200,
                        data={
                            'steps': [],
                            'rubric_row_id': rubric[0]['id'],
                            'network_disabled': False,
                        })

        test_client.req(
            'get',
            url,
            200,
            {
                **test,
                'sets': [{
                    **test['sets'][0],
                    'suites': [{
                        'id': int,
                        'rubric_row': {
                            **rubric[0], 'locked': 'auto_test'
                        },
                        'network_disabled': False,
                        'steps': [],
                        # This is set in the conftest.py
                        'command_time_limit': 3,
                    }],
                }]
            })

    with describe('rubric rows cannot be connected to two suites'), logged_in(
            teacher):
        test_client.req('patch',
                        f'{url}/sets/{set_id}/suites/',
                        409,
                        data={
                            'steps': [],
                            'rubric_row_id': rubric[0]['id'],
                            'network_disabled': False,
                        })
Exemplo n.º 2
0
def test_get_runs_internal_api(test_client, describe, app, session, basic,
                               logged_in):
    with describe('Get runs without password'):
        test_client.req('get', '/api/v-internal/auto_tests/', 400)

    with describe('Get runs when there are no runs'):
        test_client.req('get',
                        '/api/v-internal/auto_tests/?get=tests_to_run',
                        204,
                        headers={
                            'CG-Internal-Api-Password':
                            app.config['AUTO_TEST_PASSWORD']
                        })

    with describe('setup'):
        run = m.AutoTestRun(started_date=None, auto_test=m.AutoTest())
        session.add(run)
        course, assig_id, teacher, student = basic

        with logged_in(teacher):
            test = helpers.create_auto_test(test_client, assig_id)
        run = m.AutoTestRun(started_date=None,
                            auto_test_id=test['id'],
                            is_continuous_feedback_run=True)
        session.commit()

    with describe('With runs but with failing broker'):
        test_client.req('get',
                        '/api/v-internal/auto_tests/?get=tests_to_run',
                        204,
                        headers={
                            'CG-Internal-Api-Password':
                            app.config['AUTO_TEST_PASSWORD']
                        })
Exemplo n.º 3
0
    def make_assig(*args, **kwargs):
        hidden = kwargs.pop('has_hidden', False)
        with logged_in(admin_user):
            assig_id = create_assignment(*args, **kwargs)['id']
            create_auto_test(test_client, assig_id, has_hidden_steps=hidden)
        assig = m.Assignment.query.get(assig_id)
        run = m.AutoTestRun(_job_id=uuid.uuid4(),
                            auto_test=assig.auto_test,
                            batch_run_done=False)
        session.add(run)
        session.commit()

        with logged_in(admin_user):
            sub_id = create_submission(test_client, assig.id)['id']
        assert run.id is not None
        session.add(
            m.AutoTestResult(final_result=False,
                             work_id=sub_id,
                             auto_test_run_id=run.id))
        session.commit()

        return assig
Exemplo n.º 4
0
def test_delete_auto_test(basic, test_client, logged_in, describe):
    with describe('setup'):
        course, assig_id, teacher, student = basic
        with logged_in(teacher):
            test = helpers.create_auto_test(test_client, assig_id)
        url = f'/api/v1/auto_tests/{test["id"]}'

    with describe('Students cannot delete auto tests'):
        with logged_in(student):
            test_client.req('delete', url, 403)
        with logged_in(teacher):
            test_client.req('get', url, 200, result=test)

    with describe('Teachers can delete auto tests'), logged_in(teacher):
        test_client.req('delete', url, 204)
        test_client.req('get', url, 404)
Exemplo n.º 5
0
def test_hidden_steps(describe, test_client, logged_in, session, basic):
    with describe('setup'):
        course, assig_id, teacher, student = basic
        with logged_in(teacher):
            test = helpers.create_auto_test(test_client, assig_id)
            for step in m.AutoTestStepBase.query:
                step.hidden = True
            session.commit()

            test_client.req('patch',
                            f'/api/v1/assignments/{assig_id}',
                            200,
                            data={'state': 'done'})

        url = f'/api/v1/auto_tests/{test["id"]}'

    with describe('student cannot see hidden test'):
        with logged_in(student):
            step = {
                'id': int,
                'name': str,
                'type': str,
                'weight': float,
                'hidden': True,
                'data': {
                    '?inputs?': list
                },
            }
            test_client.req('get',
                            url,
                            200,
                            result={
                                '__allow_extra__':
                                True,
                                'sets': [{
                                    '__allow_extra__':
                                    True,
                                    'suites': [{
                                        '__allow_extra__': True,
                                        'steps': [step] * 4
                                    }],
                                }],
                            })
Exemplo n.º 6
0
def test_view_auto_test(basic, test_client, logged_in, describe):
    with describe('setup'):
        course, assig_id, teacher, student = basic
        with logged_in(teacher):
            test = helpers.create_auto_test(test_client, assig_id)
        url = f'/api/v1/auto_tests/{test["id"]}'

        def update_state(state):
            with logged_in(teacher):
                test_client.req('patch',
                                f'/api/v1/assignments/{assig_id}',
                                200,
                                data={'state': state})

        update_state('open')

    with describe('Students cannot see auto test before deadline'), logged_in(
            student):
        test_client.req('get', url, 403)
        test_client.req(
            'get',
            f'/api/v1/assignments/{assig_id}',
            200,
            result={
                '__allow_extra__': True,
                # You are allowed to see the id.
                'auto_test_id': test['id'],
            })

    with describe('Students can se auto test after deadline'):
        update_state('done')

        with logged_in(student):
            test_client.req('get', url, 200, result=test)
            test_client.req('get',
                            f'/api/v1/assignments/{assig_id}',
                            200,
                            result={
                                '__allow_extra__': True,
                                'auto_test_id': test['id'],
                            })
Exemplo n.º 7
0
def test_create_auto_test_set(basic, test_client, logged_in, describe):
    with describe('setup'):
        course, assig_id, teacher, student = basic
        with logged_in(teacher):
            test = helpers.create_auto_test(test_client, assig_id, 0, 0)
            rubric = test_client.req(
                'put',
                f'/api/v1/assignments/{assig_id}/rubrics/',
                200,
                data=helpers.get_simple_rubric())

        url = f'/api/v1/auto_tests/{test["id"]}'

    with describe('Students cannot create suites'), logged_in(student):
        test_client.req('post', f'{url}/sets/', 403)

        with describe('No set should be created'), logged_in(teacher):
            test_client.req('get', url, 200, result=test)

    with describe('Teacher can create sets'), logged_in(teacher):
        test_client.req('post', f'{url}/sets/', 200)

        with describe('Created sets should have correct default values'):
            test_client.req('get',
                            url,
                            200,
                            result={
                                **test,
                                'sets': [
                                    *test['sets'], {
                                        'id': int,
                                        'suites': [],
                                        'stop_points': 0
                                    }
                                ],
                            })
Exemplo n.º 8
0
def test_getting_fixture_no_permission(describe, basic, logged_in, test_client,
                                       session, app):
    with describe('setup'):
        course, assig_id, teacher, student = basic
        with logged_in(teacher):
            test = helpers.create_auto_test(
                test_client,
                assig_id,
                amount_sets=2,
                amount_suites=2,
                amount_fixtures=1,
                stop_points=[0.5, None],
                grade_calculation='partial',
            )

            session.commit()

    fixture = m.AutoTestFixture.query.first()
    test_client.req(
        'get',
        '/api/v-internal/auto_tests/{}/fixtures/{}'.format(
            fixture.auto_test_id, fixture.id),
        403,
        headers={'CG-Internal-Api-Password': app.config['AUTO_TEST_PASSWORD']})
Exemplo n.º 9
0
def test_update_locked_rubric(session, basic, test_client, logged_in, describe,
                              app):
    with describe('setup'):
        course, assig_id, teacher, student = basic
        rubric_data = helpers.get_simple_rubric()
        with logged_in(teacher):
            rubric = test_client.req(
                'put',
                f'/api/v1/assignments/{assig_id}/rubrics/',
                200,
                data=rubric_data)
            items = [
                row.items[0].id
                for row in m.RubricRow.query.filter_by(assignment_id=assig_id)
            ]

            test_client.req('patch',
                            f'/api/v1/assignments/{assig_id}',
                            200,
                            data={'state': 'open'})

        with logged_in(student):
            work = helpers.create_submission(test_client, assig_id)

    with describe('can update non locked rubric rows'):
        with logged_in(teacher):
            test_client.req('patch',
                            f'/api/v1/submissions/{work["id"]}/rubricitems/',
                            204,
                            data={'items': items})

    with describe('cannot update locked rubric rows'):
        with logged_in(teacher):
            test_id = helpers.create_auto_test(test_client,
                                               assig_id,
                                               0,
                                               0,
                                               grade_calculation='full')['id']
            set_id = test_client.req('post',
                                     f'/api/v1/auto_tests/{test_id}/sets/',
                                     200)['id']
            _, res = test_client.req(
                'patch',
                f'/api/v1/auto_tests/{test_id}/sets/{set_id}/suites/',
                200,
                data={
                    'steps': [helpers.get_auto_test_io_step()],
                    'rubric_row_id': rubric[0]['id'],
                    'network_disabled': True,
                },
                include_response=True,
            )
            assert 'Warning' in res.headers

            # This does not fail as we post the same item
            test_client.req('patch',
                            f'/api/v1/submissions/{work["id"]}/rubricitems/',
                            204,
                            data={'items': items})

            items = [
                row.items[1].id
                for row in m.RubricRow.query.filter_by(assignment_id=assig_id)
            ]
            # This does not work as the item has changed
            test_client.req('patch',
                            f'/api/v1/submissions/{work["id"]}/rubricitems/',
                            400,
                            data={'items': items})

    with describe('cannot delete locked rubric rows'), logged_in(teacher):
        res = test_client.req('put',
                              f'/api/v1/assignments/{assig_id}/rubrics/',
                              400,
                              data={'rows': []})
        assert 'cannot delete a locked' in res['message']

    with describe('cannot delete a rubric with locked rows'), logged_in(
            teacher):
        res = test_client.req(
            'delete',
            f'/api/v1/assignments/{assig_id}/rubrics/',
            409,
        )
        assert 'cannot delete a rubric with locked' in res['message']

    with describe('cannot delete items from locked rubric row after run'
                  ), logged_in(teacher):
        print(rubric)
        rubric_data = {'rows': rubric}
        old_rubric_data = copy.deepcopy(rubric_data)
        rubric_data['rows'][0]['items'].pop()
        # Before should work
        rubric = test_client.req('put',
                                 f'/api/v1/assignments/{assig_id}/rubrics/',
                                 200,
                                 data=rubric_data)
        with app.test_request_context('/non_existing', {}):
            assig = m.Assignment.query.get(assig_id)
            assig.auto_test.start_test_run()
            session.commit()
        err = test_client.req('put',
                              f'/api/v1/assignments/{assig_id}/rubrics/',
                              400,
                              data=old_rubric_data)
        assert 'No items can be added or deleted' in err['message']
Exemplo n.º 10
0
def test_update_auto_test_set(basic, test_client, logged_in, describe):
    test = set_id = suite_url = suite = url = None

    def update_test():
        with logged_in(teacher):
            nonlocal test, set_id, suite, suite_url, url
            old_test = test
            test = test_client.req('get', url, 200)
            assert test['id'] == old_test['id']

            set_id = test['sets'][0]['id']
            assert old_test['sets'][0]['id'] == set_id

            suite = test['sets'][0]['suites'][0]
            suite_url = f'{url}/sets/{set_id}/suites/'

    def update_suite(**new_data):
        test_client.req('patch',
                        suite_url,
                        new_data.pop('error', 200),
                        data={
                            **suite,
                            'rubric_row_id': suite['rubric_row']['id'],
                            **new_data,
                        })
        update_test()

    def update_set(**new_data):
        test_client.req('patch',
                        f'{url}/sets/{set_id}',
                        new_data.pop('error', 200),
                        data={
                            **test['sets'][0],
                            **new_data,
                        })
        update_test()

    with describe('setup'):
        course, assig_id, teacher, student = basic
        with logged_in(teacher):
            test = helpers.create_auto_test(test_client, assig_id)
            url = f'/api/v1/auto_tests/{test["id"]}'
            update_test()

    with describe('update stop points'), logged_in(teacher):
        update_set(stop_points=-1, error=400)
        update_set(stop_points=23, error=400)
        update_set(stop_points=0.5)
        assert test['sets'][0]['stop_points'] == 0.5

    with describe('Set internet can be enabled and disabled'), logged_in(
            teacher):
        update_suite(network_disabled=False)
        assert suite['network_disabled'] is False

        update_suite(network_disabled=True)
        assert suite['network_disabled'] is True

    with describe('Remove steps'), logged_in(teacher):
        assert suite['steps']
        update_suite(steps=[])
        assert suite['steps'] == []

    with describe('Add new step'), logged_in(teacher):
        update_suite(steps=[helpers.get_auto_test_run_program_step()])
        assert suite['steps'][0]['type'] == 'run_program'
        assert isinstance(suite['steps'][0]['id'], int)

    with describe('Unknown step types result in a 404'), logged_in(teacher):
        old_steps = suite['steps']
        update_suite(
            steps=[{
                **helpers.get_auto_test_io_step(), 'type': 'UNKOWN'
            }],
            error=400,
        )
        assert suite['steps'] == old_steps

    with describe('Cannot change the type of a step'), logged_in(teacher):
        old_steps = suite['steps']
        update_suite(steps=[{**old_steps[0], 'type': 'io_test'}], error=404)
        assert suite['steps'] == old_steps

    with describe('Cannot set the time limit to < 1'), logged_in(teacher):
        update_suite(command_time_limit=2, error=200)
        assert suite['command_time_limit'] == 2
        update_suite(command_time_limit=0.5, error=400)
        assert suite['command_time_limit'] == 2
        update_suite(command_time_limit=-1, error=400)
        assert suite['command_time_limit'] == 2
        update_suite(command_time_limit=20, error=200)
        assert suite['command_time_limit'] == 20

    with describe('delete suite'), logged_in(teacher):
        test_client.req('delete', f'{suite_url}{suite["id"]}', 204)
        test_client.req('get',
                        url,
                        200,
                        result={
                            'sets': [{
                                '__allow_extra__': True,
                                'suites': []
                            }],
                            '__allow_extra__': True,
                        })

    with describe('delete set'), logged_in(teacher):
        test_client.req('delete', f'{url}/sets/{set_id}', 204)
        test_client.req('get',
                        url,
                        200,
                        result={
                            'sets': [],
                            '__allow_extra__': True
                        })
Exemplo n.º 11
0
def test_run_auto_test(monkeypatch_celery, monkeypatch_broker, basic,
                       test_client, logged_in, describe, live_server, lxc_stub,
                       monkeypatch, app, session, stub_function_class,
                       assert_similar, monkeypatch_for_run):
    tmpdir = None

    with describe('setup'):
        course, assig_id, teacher, student1 = basic
        student2 = helpers.create_user_with_role(session, 'Student', [course])

        with logged_in(teacher):
            test = helpers.create_auto_test(
                test_client,
                assig_id,
                amount_sets=2,
                amount_suites=2,
                amount_fixtures=1,
                stop_points=[0.5, None],
                grade_calculation='partial',
            )
            test_client.req('patch',
                            f'/api/v1/assignments/{assig_id}',
                            200,
                            data={'state': 'open'})
        url = f'/api/v1/auto_tests/{test["id"]}'

        _run_student = psef.auto_test._run_student

        monkeypatch.setattr(psef.auto_test, '_get_home_dir',
                            stub_function_class(lambda: tmpdir))

        def next_student(*args, **kwargs):
            nonlocal tmpdir

            with tempfile.TemporaryDirectory() as tdir:
                tmpdir = tdir
                for inner in ['/student', '/fixtures']:
                    shutil.copytree(upper_tmpdir + inner, tdir + inner)
                return _run_student(*args, **kwargs)

        monkeypatch.setattr(psef.auto_test, '_run_student', next_student)

        with logged_in(student1):
            work1 = helpers.create_submission(test_client, assig_id)
            with logged_in(teacher):
                test_client.req('patch',
                                f'/api/v1/submissions/{work1["id"]}',
                                200,
                                data={'grade': 5.0})
        with logged_in(student2):
            prog = """
            import sys
            if len(sys.argv) > 1:
                print("hello {}!".format(sys.argv[1]))
            else:
                print("hello stranger!")
            """
            work2 = helpers.create_submission(
                test_client,
                assig_id,
                submission_data=(io.BytesIO(dedent(prog).encode()), 'test.py'))

    with describe(
            'start_auto_test'), tempfile.TemporaryDirectory() as upper_tmpdir:
        tmpdir = upper_tmpdir

        t = m.AutoTest.query.get(test['id'])
        with logged_in(teacher):
            test_client.req('post',
                            f'{url}/runs/',
                            200,
                            data={
                                'continuous_feedback_run': False,
                            })
            session.commit()
        run = t.test_run

        live_server_url = live_server()
        psef.auto_test.start_polling(app.config, repeat=False)

        with logged_in(teacher, yield_token=True) as token:
            response = requests.get(
                f'{live_server_url}{url}/runs/{run.id}',
                headers={'Authorization': f'Bearer {token}'})
            response.raise_for_status()
            assert_similar(
                response.json(), {
                    'id': run.id,
                    'created_at': str,
                    'state': 'done',
                    'results': [object, object],
                    'setup_stderr': str,
                    'setup_stdout': str,
                    'is_continuous': False,
                })

        res1 = session.query(
            m.AutoTestResult).filter_by(work_id=work1['id']).one().id
        res2 = session.query(
            m.AutoTestResult).filter_by(work_id=work2['id']).one().id

        with logged_in(student1):
            # A student cannot see results before the assignment is done
            test_client.req('get', f'{url}/runs/{run.id}/results/{res1}', 403)

        with logged_in(teacher):
            # A teacher can see the results before done
            test_client.req('get', f'{url}/runs/{run.id}/results/{res1}', 200)
            test_client.req('patch',
                            f'/api/v1/assignments/{assig_id}',
                            200,
                            data={'state': 'done'})

        with logged_in(student1):
            # A student cannot see the results of another student
            test_client.req('get', f'{url}/runs/{run.id}/results/{res2}', 403)

            # You should be able too see your own results
            res = test_client.req('get',
                                  f'{url}/runs/{run.id}/results/{res1}',
                                  200,
                                  result={
                                      'id': int,
                                      'points_achieved': 0,
                                      '__allow_extra__': True
                                  })
            # There should be only two results as all other tests should be
            # skipped
            len(res['step_results']) == 2
            test_client.req('get',
                            f'/api/v1/submissions/{work1["id"]}',
                            200,
                            result={
                                '__allow_extra__': True,
                                'grade': 5.0,
                                'grade_overridden': True,
                            })

        with logged_in(student2):
            # You should be able too see your own results
            res = test_client.req(
                'get',
                f'{url}/runs/{run.id}/results/{res2}',
                200,
                result={
                    # The last two run programs should time out, so 2 * 3 + 2 *
                    # (3 - 1) = 10 points achieved
                    'id': int,
                    'points_achieved': 10,
                    'step_results': list,
                    '__allow_extra__': True,
                })
            # Nothing should be skipped, so all steps added
            len(res['step_results']) == 4 * 2 * 2
            test_client.req('get',
                            f'/api/v1/submissions/{work2["id"]}',
                            200,
                            result={
                                '__allow_extra__': True,
                                'grade': 10.0,
                            })

            # Now you should not be able to see step details anymore
            for step in m.AutoTestStepBase.query:
                step.hidden = True
            session.commit()
            old_step_results = res['step_results']
            res = test_client.req('get', f'{url}/runs/{run.id}/results/{res2}',
                                  200)
            for step_res1, step_res2 in zip(res['step_results'],
                                            old_step_results):
                assert step_res1['id'] == step_res2['id']

                assert step_res1['auto_test_step']['hidden']
                assert not step_res2['auto_test_step']['hidden']

                if step_res1['achieved_points']:
                    assert step_res1['log'] != step_res2['log']
                else:
                    assert step_res1['log'] == {}

    with describe('delete result'):
        with logged_in(student1):
            test_client.req('delete', f'{url}/runs/{run.id}', 403)
        with logged_in(teacher):
            test_client.req('delete', f'{url}/runs/{run.id}', 204)
            test_client.req('get', f'{url}/runs/{run.id}', 404)
            test_client.req('get',
                            f'/api/v1/submissions/{work2["id"]}',
                            200,
                            result={
                                '__allow_extra__': True,
                                'grade': None,
                            })
Exemplo n.º 12
0
def test_update_auto_test(basic, test_client, logged_in, describe,
                          assert_similar, app):
    test = url = None

    def update_test(**new_data):
        nonlocal test, url
        res = rv = None

        if new_data:
            data = {}
            for key in list(new_data.keys()):
                if key.startswith('fixture') and key != 'fixtures':
                    data[key] = new_data.pop(key)
            data['json'] = (io.BytesIO(
                json.dumps({
                    **test,
                    **new_data, 'has_new_fixtures': bool(data)
                }).encode()), 'json')

            res, rv = test_client.req(
                'patch',
                url,
                new_data.pop('error', 200),
                real_data=data,
                include_response=True,
            )

        with logged_in(teacher):
            old_test = test
            test = test_client.req('get', url, 200)
            assert test['id'] == old_test['id']

        return res, rv

    with describe('setup'):
        course, assig_id, teacher, student = basic

        with logged_in(teacher):
            # We need to make a submission, otherwise we cannot start tests
            helpers.create_submission(test_client, assig_id)

            test = helpers.create_auto_test(test_client, assig_id)
            url = f'/api/v1/auto_tests/{test["id"]}'
            update_test()

    with describe('students cannot update'), logged_in(student):
        update_test(error=403)

    with describe('update grade calculation'), logged_in(teacher):
        assert test['grade_calculation'] is None
        update_test(grade_calculation='not valid', error=404)
        assert test['grade_calculation'] is None
        update_test(grade_calculation='full')
        assert test['grade_calculation'] == 'full'
        update_test(grade_calculation='partial')

    with describe('update setup script'), logged_in(teacher):
        new_script = uuid.uuid4().hex
        update_test(setup_script=new_script)
        assert test['setup_script'] == new_script
        update_test(setup_script='')
        assert test['setup_script'] == ''

    with describe('run setup script'), logged_in(teacher):
        new_script = uuid.uuid4().hex
        update_test(run_setup_script=new_script)
        assert test['run_setup_script'] == new_script
        update_test(run_setup_script='')
        assert test['run_setup_script'] == ''

    with describe('add fixtures'), logged_in(teacher):
        update_test(fixture1=(io.BytesIO(b'hello'), 'file1'))
        fixture, = test['fixtures']
        assert_similar(fixture, {'hidden': True, 'id': int, 'name': 'file1'})

        res = test_client.get(f'{url}/fixtures/{fixture["id"]}')
        assert res.status_code == 200
        assert res.get_data(as_text=True) == 'hello'

        with logged_in(student):
            # Fixture is hidden so student may not see it
            res = test_client.get(f'{url}/fixtures/{fixture["id"]}')
            assert res.status_code == 403

        test_client.req('delete', f'{url}/fixtures/{fixture["id"]}/hide', 204)

        with logged_in(student):
            # Fixture is not hidden anymore so student may see it
            res = test_client.get(f'{url}/fixtures/{fixture["id"]}')
            assert res.status_code == 200

        update_test(fixtures=[])
        assert test['fixtures'] == []

    with describe('upload same fixture twice'), logged_in(teacher):
        update_test(fixture1=(io.BytesIO(b'hello1'), 'file2'))
        _, rv = update_test(fixture1=(io.BytesIO(b'hello2'), 'file2'))
        assert 'Warning' in rv.headers
        assert 'were renamed' in rv.headers['Warning']

        fixture1, fixture2 = test['fixtures']
        assert_similar(test['fixtures'], [{
            'hidden': True,
            'id': int,
            'name': 'file2'
        }, {
            'hidden': True,
            'id': int,
            'name': 'file2 (1)'
        }])

        res = test_client.get(f'{url}/fixtures/{fixture1["id"]}')
        assert res.get_data(as_text=True) == 'hello1'

        res = test_client.get(f'{url}/fixtures/{fixture2["id"]}')
        assert res.get_data(as_text=True) == 'hello2'

    with describe('cannot update when there are runs'), logged_in(teacher):
        update_test(error=200)
        t = m.AutoTest.query.get(test['id'])
        with app.test_request_context('/'):
            t.start_test_run()
        update_test(error=409)
Exemplo n.º 13
0
def test_start_auto_test_before_complete(basic, test_client, logged_in,
                                         describe, session, monkeypatch_celery,
                                         monkeypatch_broker):
    test = url = None

    def update_test(**new_data):
        nonlocal test, url
        res = rv = None

        if new_data:
            res, rv = test_client.req(
                'patch',
                url,
                new_data.pop('error', 200),
                data=new_data,
                include_response=True,
            )

        with logged_in(teacher):
            old_test = test
            test = test_client.req('get', url, 200)
            assert test['id'] == old_test['id']

        return res, rv

    def start_run(error):
        return test_client.req('post', f'{url}/runs/', error)

    with describe('setup'):
        course, assig_id, teacher, student = basic
        with logged_in(teacher):
            test = helpers.create_auto_test(
                test_client,
                assig_id,
                amount_sets=2,
                amount_suites=2,
                grade_calculation=None,
            )
            url = f'/api/v1/auto_tests/{test["id"]}'
            update_test()

    with describe('no grade calculator'), logged_in(teacher):
        err = start_run(409)
        assert 'no grade_calculation' in err['message']
        update_test(grade_calculation='full')

    with describe('no submissions'), logged_in(teacher):
        err = start_run(409)
        assert 'there are no submissions' in err['message']
        helpers.create_submission(test_client, assig_id)

    with describe('already has a run'), logged_in(teacher):
        start_run(200)
        err = start_run(409)
        assert 'which has runs' in err['message']
        t = m.AutoTest.query.get(test['id'])
        t.runs = []
        session.commit()

    with describe('weight of zero'), logged_in(teacher):
        suite = next(t.all_suites)
        for step in suite.steps:
            step.weight = 0
        session.commit()
        err = start_run(409)
        assert 'has zero amount of points' in err['message']

        suite.steps[0].weight = 1
        session.commit()

    with describe('no steps in some suites'), logged_in(teacher):
        suite.steps = []
        session.commit()
        err = start_run(409)
        assert 'has no steps in some suites' in err['message']

    with describe('no suites'), logged_in(teacher):
        for s in t.sets:
            s.suites = []
        session.commit()
        err = start_run(409)
        assert 'has no suites' in err['message']

    with describe('no sets'), logged_in(teacher):
        t.sets = []
        session.commit()
        err = start_run(409)
        assert 'has no sets' in err['message']
Exemplo n.º 14
0
def test_continuous_feedback_auto_test(monkeypatch_celery, monkeypatch_broker,
                                       basic, test_client, logged_in, describe,
                                       live_server, lxc_stub, monkeypatch, app,
                                       session, stub_function_class,
                                       assert_similar, monkeypatch_for_run):
    tmpdir = None

    with describe('setup'):
        course, assig_id, teacher, student = basic

        with logged_in(teacher):
            test = helpers.create_auto_test(
                test_client,
                assig_id,
                amount_sets=2,
                amount_suites=2,
                amount_fixtures=1,
                stop_points=[0.5, None],
                grade_calculation='partial',
            )
            test_client.req('patch',
                            f'/api/v1/assignments/{assig_id}',
                            200,
                            data={'state': 'open'})

        url = f'/api/v1/auto_tests/{test["id"]}'
        live_server()

        with logged_in(student):
            prog = """
            ls
            """
            helpers.create_submission(test_client,
                                      assig_id,
                                      submission_data=(io.BytesIO(
                                          dedent(prog).encode()), 'test.py'))

        _run_student = psef.auto_test._run_student

        monkeypatch.setattr(psef.auto_test, '_get_home_dir',
                            stub_function_class(lambda: tmpdir))

        def next_student(*args, **kwargs):
            nonlocal tmpdir

            with tempfile.TemporaryDirectory() as tdir:
                tmpdir = tdir
                for inner in ['/student', '/fixtures']:
                    shutil.copytree(upper_tmpdir + inner, tdir + inner)
                return _run_student(*args, **kwargs)

        monkeypatch.setattr(psef.auto_test, '_run_student', next_student)

    with describe(
            'start_auto_test'), tempfile.TemporaryDirectory() as upper_tmpdir:
        tmpdir = upper_tmpdir

        t = m.AutoTest.query.get(test['id'])
        psef.auto_test.CODEGRADE_USER = getpass.getuser()
        session.commit()

        with logged_in(teacher):
            test_client.req('post',
                            f'{url}/runs/',
                            200,
                            data={
                                'continuous_feedback_run': True,
                            })
            session.commit()

        run = t.continuous_feedback_run
        assert run is not None

        # Old submission should not be started directly
        assert session.query(m.AutoTestResult).all() == []

        with logged_in(student):
            prog = """
            import sys
            if len(sys.argv) > 1:
                print("hello {}!".format(sys.argv[1]))
            else:
                print("hello stranger!")
            """
            work = helpers.create_submission(
                test_client,
                assig_id,
                submission_data=(io.BytesIO(dedent(prog).encode()), 'test.py'))

        assert len(session.query(m.AutoTestResult).all()) == 1

        psef.auto_test.start_polling(app.config, repeat=False)
        res = session.query(m.AutoTestResult).one()

        with logged_in(student):
            # A student can see results before the assignment is done
            test_client.req('get',
                            f'{url}/runs/{run.id}/results/{res.id}',
                            200,
                            result={
                                'state': 'passed',
                                '__allow_extra__': True,
                            })

        with logged_in(teacher):
            # Make sure rubric is not filled in
            test_client.req('get',
                            f'/api/v1/submissions/{work["id"]}',
                            200,
                            result={
                                '__allow_extra__': True,
                                'grade': None,
                                'grade_overridden': False,
                            })

        with pytest.raises(psef.errors.APIException):
            t.start_continuous_feedback_run()