Handle failure task_output that won't fit in the model column.
This commit is contained in:
@@ -22,12 +22,9 @@ from instructor_task.api import (get_running_instructor_tasks,
|
||||
submit_reset_problem_attempts_for_all_students,
|
||||
submit_delete_problem_state_for_all_students)
|
||||
|
||||
from instructor_task.api_helper import (QUEUING,
|
||||
AlreadyRunningError,
|
||||
encode_problem_and_student_input,
|
||||
)
|
||||
from instructor_task.models import InstructorTask
|
||||
from instructor_task.tasks_helper import PROGRESS
|
||||
from instructor_task.api_helper import (AlreadyRunningError,
|
||||
encode_problem_and_student_input)
|
||||
from instructor_task.models import InstructorTask, PROGRESS, QUEUING
|
||||
from instructor_task.tests.test_base import InstructorTaskTestCase
|
||||
from instructor_task.tests.factories import InstructorTaskFactory
|
||||
from instructor_task.views import instructor_task_status, get_task_completion_info
|
||||
@@ -376,9 +373,9 @@ class InstructorTaskSubmitTest(InstructorTaskTestCase):
|
||||
submit_delete_problem_state_for_all_students(request, course_id, problem_url)
|
||||
|
||||
def test_submit_nonrescorable_modules(self):
|
||||
# confirm that a rescore of a non-existent module returns an exception
|
||||
# confirm that a rescore of an existent but unscorable module returns an exception
|
||||
# (Note that it is easier to test a non-rescorable module in test_tasks,
|
||||
# where we are creating real modules.
|
||||
# where we are creating real modules.)
|
||||
problem_url = self.problem_section.location.url()
|
||||
course_id = self.course.id
|
||||
request = None
|
||||
@@ -387,6 +384,28 @@ class InstructorTaskSubmitTest(InstructorTaskTestCase):
|
||||
with self.assertRaises(NotImplementedError):
|
||||
submit_rescore_problem_for_all_students(request, course_id, problem_url)
|
||||
|
||||
def _test_submit_with_long_url(self, task_class, student=None):
|
||||
problem_url_name = 'x' * 255
|
||||
self.define_option_problem(problem_url_name)
|
||||
location = InstructorTaskTestCase.problem_location(problem_url_name)
|
||||
with self.assertRaises(ValueError):
|
||||
if student is not None:
|
||||
task_class(self.create_task_request(self.instructor), self.course.id, location, student)
|
||||
else:
|
||||
task_class(self.create_task_request(self.instructor), self.course.id, location)
|
||||
|
||||
def test_submit_rescore_all_with_long_url(self):
|
||||
self._test_submit_with_long_url(submit_rescore_problem_for_all_students)
|
||||
|
||||
def test_submit_rescore_student_with_long_url(self):
|
||||
self._test_submit_with_long_url(submit_rescore_problem_for_student, self.student)
|
||||
|
||||
def test_submit_reset_all_with_long_url(self):
|
||||
self._test_submit_with_long_url(submit_reset_problem_attempts_for_all_students)
|
||||
|
||||
def test_submit_delete_all_with_long_url(self):
|
||||
self._test_submit_with_long_url(submit_delete_problem_state_for_all_students)
|
||||
|
||||
def _test_submit_task(self, task_class, student=None):
|
||||
problem_url_name = 'H1P1'
|
||||
self.define_option_problem(problem_url_name)
|
||||
|
||||
@@ -25,6 +25,7 @@ from instructor_task.tests.factories import InstructorTaskFactory
|
||||
from instructor_task.tasks import rescore_problem, reset_problem_attempts, delete_problem_state
|
||||
from instructor_task.tasks_helper import UpdateProblemModuleStateError
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
PROBLEM_URL_NAME = "test_urlname"
|
||||
|
||||
@@ -202,7 +203,16 @@ class TestInstructorTasks(InstructorTaskTestCase):
|
||||
entry = InstructorTask.objects.get(id=task_entry.id)
|
||||
self.assertEquals(json.loads(entry.task_output), status)
|
||||
self.assertEquals(entry.task_state, SUCCESS)
|
||||
# TODO: check that entries were reset
|
||||
# check that the correct entry was reset
|
||||
for index, student in enumerate(students):
|
||||
module = StudentModule.objects.get(course_id=self.course.id,
|
||||
student=student,
|
||||
module_state_key=self.problem_url)
|
||||
state = json.loads(module.state)
|
||||
if index == 3:
|
||||
self.assertEquals(state['attempts'], 0)
|
||||
else:
|
||||
self.assertEquals(state['attempts'], initial_attempts)
|
||||
|
||||
def test_reset_with_student_username(self):
|
||||
self._test_reset_with_student(False)
|
||||
@@ -236,7 +246,8 @@ class TestInstructorTasks(InstructorTaskTestCase):
|
||||
self._test_run_with_failure(delete_problem_state, 'We expected this to fail')
|
||||
|
||||
def _test_run_with_long_error_msg(self, task_class):
|
||||
# run with no StudentModules for the problem
|
||||
# run with an error message that is so long it will require
|
||||
# truncation (as well as the jettisoning of the traceback).
|
||||
task_entry = self._create_input_entry()
|
||||
self.define_option_problem(PROBLEM_URL_NAME)
|
||||
expected_message = "x" * 1500
|
||||
@@ -247,12 +258,46 @@ class TestInstructorTasks(InstructorTaskTestCase):
|
||||
# compare with entry in table:
|
||||
entry = InstructorTask.objects.get(id=task_entry.id)
|
||||
self.assertEquals(entry.task_state, FAILURE)
|
||||
# TODO: on MySQL this will actually fail, because it was truncated
|
||||
# when it was persisted. It does not fail on SqlLite3 at the moment,
|
||||
# because it doesn't actually enforce length limits!
|
||||
self.assertGreater(1023, len(entry.task_output))
|
||||
output = json.loads(entry.task_output)
|
||||
self.assertEquals(output['exception'], 'TestTaskFailure')
|
||||
self.assertEquals(output['message'], expected_message)
|
||||
self.assertEquals(output['message'], expected_message[:len(output['message']) - 3] + "...")
|
||||
self.assertTrue('traceback' not in output)
|
||||
|
||||
def test_rescore_with_long_error_msg(self):
|
||||
self._test_run_with_long_error_msg(rescore_problem)
|
||||
|
||||
def test_reset_with_long_error_msg(self):
|
||||
self._test_run_with_long_error_msg(reset_problem_attempts)
|
||||
|
||||
def test_delete_with_long_error_msg(self):
|
||||
self._test_run_with_long_error_msg(delete_problem_state)
|
||||
|
||||
def _test_run_with_short_error_msg(self, task_class):
|
||||
# run with an error message that is short enough to fit
|
||||
# in the output, but long enough that the traceback won't.
|
||||
# Confirm that the traceback is truncated.
|
||||
task_entry = self._create_input_entry()
|
||||
self.define_option_problem(PROBLEM_URL_NAME)
|
||||
expected_message = "x" * 900
|
||||
try:
|
||||
self._run_task_with_mock_celery(task_class, task_entry.id, task_entry.task_id, expected_message)
|
||||
except TestTaskFailure:
|
||||
pass
|
||||
# compare with entry in table:
|
||||
entry = InstructorTask.objects.get(id=task_entry.id)
|
||||
self.assertEquals(entry.task_state, FAILURE)
|
||||
self.assertGreater(1023, len(entry.task_output))
|
||||
output = json.loads(entry.task_output)
|
||||
self.assertEquals(output['exception'], 'TestTaskFailure')
|
||||
self.assertEquals(output['message'], expected_message)
|
||||
self.assertEquals(output['traceback'][-3:], "...")
|
||||
|
||||
def test_rescore_with_short_error_msg(self):
|
||||
self._test_run_with_short_error_msg(rescore_problem)
|
||||
|
||||
def test_reset_with_short_error_msg(self):
|
||||
self._test_run_with_short_error_msg(reset_problem_attempts)
|
||||
|
||||
def test_delete_with_short_error_msg(self):
|
||||
self._test_run_with_short_error_msg(delete_problem_state)
|
||||
|
||||
Reference in New Issue
Block a user