Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
edx-proctoring
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
OpenEdx
edx-proctoring
Commits
79da1289
Commit
79da1289
authored
Jun 29, 2015
by
Afzal Wali
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
More tests for the start exam attempt API.
parent
6596441a
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
326 additions
and
44 deletions
+326
-44
edx_proctoring/tests/test_serializer.py
+31
-0
edx_proctoring/tests/test_views.py
+268
-9
edx_proctoring/urls.py
+1
-1
edx_proctoring/views.py
+26
-34
No files found.
edx_proctoring/tests/test_serializer.py
0 → 100644
View file @
79da1289
"""
Tests for the custom StrictBooleanField serializer used by the ProctoredExamSerializer
"""
import
unittest
from
edx_proctoring.serializers
import
ProctoredExamSerializer
class
TestProctoredExamSerializer
(
unittest
.
TestCase
):
"""
Tests for ProctoredExamSerializer
"""
def
test_boolean_fields
(
self
):
"""
Tests the boolean fields. Should cause a validation error in case a field is required.
"""
data
=
{
'course_id'
:
"a/b/c"
,
'exam_name'
:
"midterm1"
,
'content_id'
:
'123aXqe0'
,
'time_limit_mins'
:
90
,
'external_id'
:
'123'
,
'is_proctored'
:
'bla'
,
'is_active'
:
'f'
}
serializer
=
ProctoredExamSerializer
(
data
=
data
)
self
.
assertFalse
(
serializer
.
is_valid
())
self
.
assertDictEqual
(
{
'is_proctored'
:
[
u'This field is required.'
]},
serializer
.
errors
)
edx_proctoring/tests/test_views.py
View file @
79da1289
...
...
@@ -6,6 +6,7 @@ from django.test.client import Client
from
django.core.urlresolvers
import
reverse
,
NoReverseMatch
from
edx_proctoring.models
import
ProctoredExam
from
edx_proctoring.views
import
require_staff
from
django.contrib.auth.models
import
User
from
.utils
import
(
LoggedInTestCase
...
...
@@ -110,29 +111,77 @@ class ProctoredExamViewTests(LoggedInTestCase):
self
.
assertEqual
(
response_data
[
'external_id'
],
exam_data
[
'external_id'
])
self
.
assertEqual
(
response_data
[
'time_limit_mins'
],
exam_data
[
'time_limit_mins'
])
def
test_
get_exam_by_id
(
self
):
def
test_
create_duplicate_exam
(
self
):
"""
Tests the Get Exam by id endpoint
Tests the POST method error handling if a duplicate exam is created.
"""
exam_data
=
{
'course_id'
:
"a/b/c"
,
'exam_name'
:
"midterm1"
,
'content_id'
:
'123aXqe0'
,
'time_limit_mins'
:
90
,
'external_id'
:
'123'
,
'is_proctored'
:
True
,
'is_active'
:
True
}
response
=
self
.
client
.
post
(
reverse
(
'edx_proctoring.proctored_exam.exam'
),
exam_data
)
self
.
assertEqual
(
response
.
status_code
,
200
)
response_data
=
json
.
loads
(
response
.
content
)
self
.
assertGreater
(
response_data
[
'exam_id'
],
0
)
response
=
self
.
client
.
post
(
reverse
(
'edx_proctoring.proctored_exam.exam'
),
exam_data
)
self
.
assertEqual
(
response
.
status_code
,
400
)
def
test_update_existing_exam
(
self
):
"""
Test the PUT method of the exam endpoint to update an existing exam.
"""
# Create an exam.
proctored_exam
=
ProctoredExam
.
objects
.
create
(
course_id
=
'
test_course
'
,
content_id
=
'
test_content
'
,
course_id
=
'
a/b/c
'
,
content_id
=
'
123aXqe0
'
,
exam_name
=
'Test Exam'
,
external_id
=
'123aXqe3'
,
time_limit_mins
=
90
)
exam_id
=
proctored_exam
.
id
updated_exam_data
=
{
'exam_id'
:
exam_id
,
'exam_name'
:
"midterm1"
,
'time_limit_mins'
:
90
,
'external_id'
:
'123'
,
'is_proctored'
:
True
,
'is_active'
:
True
}
response
=
self
.
client
.
put
(
reverse
(
'edx_proctoring.proctored_exam.exam'
),
updated_exam_data
)
self
.
assertEqual
(
response
.
status_code
,
200
)
response_data
=
json
.
loads
(
response
.
content
)
self
.
assertEqual
(
response_data
[
'exam_id'
],
exam_id
)
# Now lookup the exam by giving the exam_id returned and match the data.
response
=
self
.
client
.
get
(
reverse
(
'edx_proctoring.proctored_exam.exam_by_id'
,
kwargs
=
{
'exam_id'
:
proctored_exam
.
id
})
reverse
(
'edx_proctoring.proctored_exam.exam_by_id'
,
kwargs
=
{
'exam_id'
:
response_data
[
'exam_id'
]
})
)
self
.
assertEqual
(
response
.
status_code
,
200
)
response_data
=
json
.
loads
(
response
.
content
)
self
.
assertEqual
(
response_data
[
'course_id'
],
proctored_exam
.
course_id
)
self
.
assertEqual
(
response_data
[
'exam_name'
],
proctored_exam
.
exam_name
)
self
.
assertEqual
(
response_data
[
'content_id'
],
proctored_exam
.
content_id
)
self
.
assertEqual
(
response_data
[
'external_id'
],
proctored_exam
.
external_id
)
self
.
assertEqual
(
response_data
[
'time_limit_mins'
],
proctored_exam
.
time_limit_mins
)
self
.
assertEqual
(
response_data
[
'exam_name'
],
updated_exam_data
[
'exam_name'
])
self
.
assertEqual
(
response_data
[
'external_id'
],
updated_exam_data
[
'external_id'
])
self
.
assertEqual
(
response_data
[
'time_limit_mins'
],
updated_exam_data
[
'time_limit_mins'
])
def
test_decorator_staff_user
(
self
):
"""
...
...
@@ -154,3 +203,213 @@ class ProctoredExamViewTests(LoggedInTestCase):
self
.
user
.
save
()
request
.
user
=
self
.
user
return
request
def
test_update_non_existing_exam
(
self
):
"""
Test the PUT method of the exam endpoint to update an existing exam.
In case the exam_id is not found, it should return a bad request.
"""
exam_id
=
99999
updated_exam_data
=
{
'exam_id'
:
exam_id
,
'exam_name'
:
"midterm1"
,
'time_limit_mins'
:
90
,
'external_id'
:
'123'
,
'is_proctored'
:
True
,
'is_active'
:
True
}
response
=
self
.
client
.
put
(
reverse
(
'edx_proctoring.proctored_exam.exam'
),
updated_exam_data
)
self
.
assertEqual
(
response
.
status_code
,
400
)
response_data
=
json
.
loads
(
response
.
content
)
self
.
assertEqual
(
response_data
,
{
'detail'
:
'The exam_id does not exist.'
})
def
test_get_exam_by_id
(
self
):
"""
Tests the Get Exam by id endpoint
"""
# Create an exam.
proctored_exam
=
ProctoredExam
.
objects
.
create
(
course_id
=
'test_course'
,
content_id
=
'test_content'
,
exam_name
=
'Test Exam'
,
external_id
=
'123aXqe3'
,
time_limit_mins
=
90
)
response
=
self
.
client
.
get
(
reverse
(
'edx_proctoring.proctored_exam.exam_by_id'
,
kwargs
=
{
'exam_id'
:
proctored_exam
.
id
})
)
self
.
assertEqual
(
response
.
status_code
,
200
)
response_data
=
json
.
loads
(
response
.
content
)
self
.
assertEqual
(
response_data
[
'course_id'
],
proctored_exam
.
course_id
)
self
.
assertEqual
(
response_data
[
'exam_name'
],
proctored_exam
.
exam_name
)
self
.
assertEqual
(
response_data
[
'content_id'
],
proctored_exam
.
content_id
)
self
.
assertEqual
(
response_data
[
'external_id'
],
proctored_exam
.
external_id
)
self
.
assertEqual
(
response_data
[
'time_limit_mins'
],
proctored_exam
.
time_limit_mins
)
def
test_get_exam_by_bad_id
(
self
):
"""
Tests the Get Exam by id endpoint
"""
# Create an exam.
response
=
self
.
client
.
get
(
reverse
(
'edx_proctoring.proctored_exam.exam_by_id'
,
kwargs
=
{
'exam_id'
:
99999
})
)
self
.
assertEqual
(
response
.
status_code
,
400
)
response_data
=
json
.
loads
(
response
.
content
)
self
.
assertEqual
(
response_data
[
'detail'
],
'The exam_id does not exist.'
)
def
test_get_exam_by_content_id
(
self
):
"""
Tests the Get Exam by content id endpoint
"""
# Create an exam.
proctored_exam
=
ProctoredExam
.
objects
.
create
(
course_id
=
'a/b/c'
,
content_id
=
'test_content'
,
exam_name
=
'Test Exam'
,
external_id
=
'123aXqe3'
,
time_limit_mins
=
90
)
response
=
self
.
client
.
get
(
reverse
(
'edx_proctoring.proctored_exam.exam_by_content_id'
,
kwargs
=
{
'course_id'
:
proctored_exam
.
course_id
,
'content_id'
:
proctored_exam
.
content_id
})
)
self
.
assertEqual
(
response
.
status_code
,
200
)
response_data
=
json
.
loads
(
response
.
content
)
self
.
assertEqual
(
response_data
[
'course_id'
],
proctored_exam
.
course_id
)
self
.
assertEqual
(
response_data
[
'exam_name'
],
proctored_exam
.
exam_name
)
self
.
assertEqual
(
response_data
[
'content_id'
],
proctored_exam
.
content_id
)
self
.
assertEqual
(
response_data
[
'external_id'
],
proctored_exam
.
external_id
)
self
.
assertEqual
(
response_data
[
'time_limit_mins'
],
proctored_exam
.
time_limit_mins
)
def
test_get_exam_by_bad_content_id
(
self
):
"""
Tests the Get Exam by content id endpoint
"""
# Create an exam.
proctored_exam
=
ProctoredExam
.
objects
.
create
(
course_id
=
'a/b/c'
,
content_id
=
'test_content'
,
exam_name
=
'Test Exam'
,
external_id
=
'123aXqe3'
,
time_limit_mins
=
90
)
response
=
self
.
client
.
get
(
reverse
(
'edx_proctoring.proctored_exam.exam_by_content_id'
,
kwargs
=
{
'course_id'
:
'c/d/e'
,
'content_id'
:
proctored_exam
.
content_id
})
)
self
.
assertEqual
(
response
.
status_code
,
400
)
response_data
=
json
.
loads
(
response
.
content
)
self
.
assertEqual
(
response_data
[
'detail'
],
'The exam with course_id, content_id does not exist.'
)
def
test_get_exam_insufficient_args
(
self
):
"""
Tests the Get Exam by content id endpoint
"""
# Create an exam.
proctored_exam
=
ProctoredExam
.
objects
.
create
(
course_id
=
'a/b/c'
,
content_id
=
'test_content'
,
exam_name
=
'Test Exam'
,
external_id
=
'123aXqe3'
,
time_limit_mins
=
90
)
response
=
self
.
client
.
get
(
reverse
(
'edx_proctoring.proctored_exam.exam_by_content_id'
,
kwargs
=
{
'course_id'
:
proctored_exam
.
course_id
,
'content_id'
:
proctored_exam
.
content_id
})
)
self
.
assertEqual
(
response
.
status_code
,
200
)
response_data
=
json
.
loads
(
response
.
content
)
self
.
assertEqual
(
response_data
[
'course_id'
],
proctored_exam
.
course_id
)
self
.
assertEqual
(
response_data
[
'exam_name'
],
proctored_exam
.
exam_name
)
self
.
assertEqual
(
response_data
[
'content_id'
],
proctored_exam
.
content_id
)
self
.
assertEqual
(
response_data
[
'external_id'
],
proctored_exam
.
external_id
)
self
.
assertEqual
(
response_data
[
'time_limit_mins'
],
proctored_exam
.
time_limit_mins
)
class
TestStudentProctoredExamAttempt
(
LoggedInTestCase
):
"""
Tests for the StudentProctoredExamAttempt
"""
def
setUp
(
self
):
super
(
TestStudentProctoredExamAttempt
,
self
)
.
setUp
()
self
.
user
.
is_staff
=
True
self
.
user
.
save
()
self
.
client
.
login_user
(
self
.
user
)
self
.
student_taking_exam
=
User
()
self
.
student_taking_exam
.
save
()
def
test_start_exam_attempt
(
self
):
"""
Start an exam (create an exam attempt)
"""
# Create an exam.
proctored_exam
=
ProctoredExam
.
objects
.
create
(
course_id
=
'a/b/c'
,
content_id
=
'test_content'
,
exam_name
=
'Test Exam'
,
external_id
=
'123aXqe3'
,
time_limit_mins
=
90
)
attempt_data
=
{
'exam_id'
:
proctored_exam
.
id
,
'user_id'
:
self
.
student_taking_exam
.
id
,
'external_id'
:
proctored_exam
.
external_id
}
response
=
self
.
client
.
post
(
reverse
(
'edx_proctoring.proctored_exam.attempt'
),
attempt_data
)
self
.
assertEqual
(
response
.
status_code
,
200
)
response_data
=
json
.
loads
(
response
.
content
)
self
.
assertGreater
(
response_data
[
'exam_attempt_id'
],
0
)
def
test_restart_exam_attempt
(
self
):
"""
Start an exam that has already started should raise error.
"""
# Create an exam.
proctored_exam
=
ProctoredExam
.
objects
.
create
(
course_id
=
'a/b/c'
,
content_id
=
'test_content'
,
exam_name
=
'Test Exam'
,
external_id
=
'123aXqe3'
,
time_limit_mins
=
90
)
attempt_data
=
{
'exam_id'
:
proctored_exam
.
id
,
'user_id'
:
self
.
student_taking_exam
.
id
,
'external_id'
:
proctored_exam
.
external_id
}
response
=
self
.
client
.
post
(
reverse
(
'edx_proctoring.proctored_exam.attempt'
),
attempt_data
)
self
.
assertEqual
(
response
.
status_code
,
200
)
response_data
=
json
.
loads
(
response
.
content
)
self
.
assertGreater
(
response_data
[
'exam_attempt_id'
],
0
)
response
=
self
.
client
.
post
(
reverse
(
'edx_proctoring.proctored_exam.attempt'
),
attempt_data
)
self
.
assertEqual
(
response
.
status_code
,
400
)
response_data
=
json
.
loads
(
response
.
content
)
self
.
assertEqual
(
response_data
[
'detail'
],
'Error. Trying to start an exam that has already started.'
)
edx_proctoring/urls.py
View file @
79da1289
...
...
@@ -19,7 +19,7 @@ urlpatterns = patterns( # pylint: disable=invalid-name
name
=
'edx_proctoring.proctored_exam.exam_by_id'
),
url
(
r'edx_proctoring/v1/proctored_exam/exam/course_id/{}/content_id/(?P<content_id>
\d
+)$'
.
format
(
r'edx_proctoring/v1/proctored_exam/exam/course_id/{}/content_id/(?P<content_id>
[A-z0-9]
+)$'
.
format
(
settings
.
COURSE_ID_PATTERN
),
views
.
ProctoredExamView
.
as_view
(),
name
=
'edx_proctoring.proctored_exam.exam_by_content_id'
...
...
edx_proctoring/views.py
View file @
79da1289
...
...
@@ -8,7 +8,7 @@ from rest_framework import status
from
rest_framework.response
import
Response
from
edx_proctoring.api
import
create_exam
,
update_exam
,
get_exam_by_id
,
get_exam_by_content_id
,
start_exam_attempt
,
\
stop_exam_attempt
,
add_allowance_for_user
,
remove_allowance_for_user
,
get_active_exams_for_user
from
edx_proctoring.exceptions
import
ProctoredExamNotFoundException
,
ProctoredExamAlreadyExists
,
\
from
edx_proctoring.exceptions
import
ProctoredExamNotFoundException
,
\
StudentExamAttemptAlreadyExistsException
,
StudentExamAttemptDoesNotExistsException
from
edx_proctoring.serializers
import
ProctoredExamSerializer
...
...
@@ -101,22 +101,16 @@ class ProctoredExamView(AuthenticatedAPIView):
"""
serializer
=
ProctoredExamSerializer
(
data
=
request
.
DATA
)
if
serializer
.
is_valid
():
try
:
exam_id
=
create_exam
(
course_id
=
request
.
DATA
.
get
(
'course_id'
,
None
),
content_id
=
request
.
DATA
.
get
(
'content_id'
,
None
),
exam_name
=
request
.
DATA
.
get
(
'exam_name'
,
None
),
time_limit_mins
=
request
.
DATA
.
get
(
'time_limit_mins'
,
None
),
is_proctored
=
request
.
DATA
.
get
(
'is_proctored'
,
None
),
external_id
=
request
.
DATA
.
get
(
'external_id'
,
None
),
is_active
=
request
.
DATA
.
get
(
'is_active'
,
None
)
)
return
Response
({
'exam_id'
:
exam_id
})
except
ProctoredExamAlreadyExists
:
return
Response
(
status
=
status
.
HTTP_400_BAD_REQUEST
,
data
=
{
"detail"
:
"Error Trying to create a duplicate exam."
}
)
exam_id
=
create_exam
(
course_id
=
request
.
DATA
.
get
(
'course_id'
,
None
),
content_id
=
request
.
DATA
.
get
(
'content_id'
,
None
),
exam_name
=
request
.
DATA
.
get
(
'exam_name'
,
None
),
time_limit_mins
=
request
.
DATA
.
get
(
'time_limit_mins'
,
None
),
is_proctored
=
request
.
DATA
.
get
(
'is_proctored'
,
None
),
external_id
=
request
.
DATA
.
get
(
'external_id'
,
None
),
is_active
=
request
.
DATA
.
get
(
'is_active'
,
None
)
)
return
Response
({
'exam_id'
:
exam_id
})
else
:
return
Response
(
status
=
status
.
HTTP_400_BAD_REQUEST
,
...
...
@@ -131,12 +125,12 @@ class ProctoredExamView(AuthenticatedAPIView):
"""
try
:
exam_id
=
update_exam
(
exam_id
=
request
.
DATA
.
get
(
'exam_id'
,
""
),
exam_name
=
request
.
DATA
.
get
(
'exam_name'
,
""
),
time_limit_mins
=
request
.
DATA
.
get
(
'time_limit_mins'
,
""
),
is_proctored
=
request
.
DATA
.
get
(
'is_proctored'
,
Fals
e
),
external_id
=
request
.
DATA
.
get
(
'external_id'
,
""
),
is_active
=
request
.
DATA
.
get
(
'is_active'
,
Fals
e
),
exam_id
=
request
.
DATA
.
get
(
'exam_id'
,
None
),
exam_name
=
request
.
DATA
.
get
(
'exam_name'
,
None
),
time_limit_mins
=
request
.
DATA
.
get
(
'time_limit_mins'
,
None
),
is_proctored
=
request
.
DATA
.
get
(
'is_proctored'
,
Non
e
),
external_id
=
request
.
DATA
.
get
(
'external_id'
,
None
),
is_active
=
request
.
DATA
.
get
(
'is_active'
,
Non
e
),
)
return
Response
({
'exam_id'
:
exam_id
})
except
ProctoredExamNotFoundException
:
...
...
@@ -176,18 +170,16 @@ class ProctoredExamView(AuthenticatedAPIView):
status
=
status
.
HTTP_400_BAD_REQUEST
,
data
=
{
"detail"
:
"The exam with course_id, content_id does not exist."
}
)
else
:
return
Response
(
status
=
status
.
HTTP_400_BAD_REQUEST
,
data
=
{
"detail"
:
"Bad input data."
}
)
class
StudentProctoredExamAttempt
(
AuthenticatedAPIView
):
"""
Endpoint for the StudentProctoredExamAttempt
/edx_proctoring/v1/proctored_exam/exam
/edx_proctoring/v1/proctored_exam/attempt
Supports:
HTTP POST: Starts an exam attempt.
HTTP PUT: Stops an exam attempt.
HTTP GET: Returns the status of an exam attempt.
"""
def
get
(
self
,
request
):
# pylint: disable=unused-argument
...
...
@@ -217,9 +209,9 @@ class StudentProctoredExamAttempt(AuthenticatedAPIView):
"""
try
:
exam_attempt_id
=
start_exam_attempt
(
exam_id
=
request
.
DATA
.
get
(
'exam_id'
,
""
),
user_id
=
request
.
DATA
.
get
(
'user_id'
,
""
),
external_id
=
request
.
DATA
.
get
(
'external_id'
,
""
)
exam_id
=
request
.
DATA
.
get
(
'exam_id'
,
None
),
user_id
=
request
.
DATA
.
get
(
'user_id'
,
None
),
external_id
=
request
.
DATA
.
get
(
'external_id'
,
None
)
)
return
Response
({
'exam_attempt_id'
:
exam_attempt_id
})
...
...
@@ -269,7 +261,7 @@ class ExamAllowanceView(AuthenticatedAPIView):
value
=
request
.
DATA
.
get
(
'value'
,
""
)
))
@
require_staff
@
method_decorator
(
require_staff
)
def
delete
(
self
,
request
):
"""
HTTP DELETE handler. Removes Allowance.
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment