|
1
|
|
|
''' |
|
2
|
|
|
Tets cases focusing on the frontend views ('GET') accessed by students. |
|
3
|
|
|
''' |
|
4
|
|
|
|
|
5
|
|
|
from django.contrib.auth.models import User |
|
6
|
|
|
from lti import ToolConsumer |
|
7
|
|
|
|
|
8
|
|
|
from opensubmit.tests.cases import SubmitStudentScenarioTestCase |
|
9
|
|
|
from .helpers.user import get_student_dict |
|
10
|
|
|
from .helpers.submission import create_validated_submission |
|
11
|
|
|
from .helpers.testmachine import create_test_machine |
|
12
|
|
|
|
|
13
|
|
|
|
|
14
|
|
|
class Student(SubmitStudentScenarioTestCase): |
|
15
|
|
|
def test_assignment_description_url_on_dashboard(self): |
|
16
|
|
|
self.create_submissions() |
|
17
|
|
|
response = self.c.get('/dashboard/') |
|
18
|
|
|
# Check for assignment description links of open assignments |
|
19
|
|
|
for assignment in self.all_assignments: |
|
20
|
|
|
if assignment.can_create_submission(self.user): |
|
21
|
|
|
self.assertContains(response, assignment.url()) |
|
22
|
|
|
# Check for assignment description links of active submissions |
|
23
|
|
|
for sub in self.submissions: |
|
24
|
|
|
self.assertContains(response, sub.assignment.url()) |
|
25
|
|
|
|
|
26
|
|
|
def test_can_see_submissions(self): |
|
27
|
|
|
self.create_submissions() |
|
28
|
|
|
cases = { |
|
29
|
|
|
self.open_assignment_sub: (200, ), |
|
30
|
|
|
self.soft_deadline_passed_assignment_sub: (200, ), |
|
31
|
|
|
self.hard_deadline_passed_assignment_sub: (200, ), |
|
32
|
|
|
} |
|
33
|
|
|
for submission in cases: |
|
34
|
|
|
response = self.c.get('/details/%s/' % submission.pk) |
|
35
|
|
|
expected_responses = cases[submission] |
|
36
|
|
|
self.assertIn(response.status_code, expected_responses) |
|
37
|
|
|
|
|
38
|
|
|
def test_validation_result_rendering(self): |
|
39
|
|
|
sub = create_validated_submission(self.user, self.open_file_assignment) |
|
40
|
|
|
response = self.c.get('/details/%s/' % sub.pk) |
|
41
|
|
|
assert(sub.get_validation_result().result) |
|
42
|
|
|
# Search for headline |
|
43
|
|
|
self.assertContains(response, "Validity test result:") |
|
44
|
|
|
# Search for content |
|
45
|
|
|
self.assertContains(response, sub.get_validation_result().result) |
|
46
|
|
|
|
|
47
|
|
|
def test_can_see_only_enabled_course_assignments(self): |
|
48
|
|
|
# One default course registration in cases.py |
|
49
|
|
|
assignments_before = len(self.user.profile.open_assignments()) |
|
50
|
|
|
# Become part of another course |
|
51
|
|
|
self.another_course.participants.add(self.user.profile) |
|
52
|
|
|
assignments_after = len(self.user.profile.open_assignments()) |
|
53
|
|
|
self.assertNotEqual(assignments_before, assignments_after) |
|
54
|
|
|
|
|
55
|
|
|
def test_enable_course_with_get_in_dashboard_url(self): |
|
56
|
|
|
student = self.user |
|
57
|
|
|
|
|
58
|
|
|
# One default course registration in cases.py |
|
59
|
|
|
assignments_before = len(student.profile.open_assignments()) |
|
60
|
|
|
|
|
61
|
|
|
# Provide GET parameter to enable course to root dir, which performs |
|
62
|
|
|
# a redirection to the dashboard |
|
63
|
|
|
response = self.c.get('/dashboard/?course=%u' % self.another_course.pk) |
|
64
|
|
|
self.assertEqual(response.status_code, 200) |
|
65
|
|
|
|
|
66
|
|
|
# Check if course is enabled now, based on assignment count |
|
67
|
|
|
assignments_after = len(student.profile.open_assignments()) |
|
68
|
|
|
self.assertNotEqual(assignments_before, assignments_after) |
|
69
|
|
|
|
|
70
|
|
|
def test_enable_course_with_get_in_root_url(self): |
|
71
|
|
|
student = self.user |
|
72
|
|
|
|
|
73
|
|
|
# One default course registration in cases.py |
|
74
|
|
|
assignments_before = len(student.profile.open_assignments()) |
|
75
|
|
|
|
|
76
|
|
|
# Provide GET parameter to enable course to root dir, which performs |
|
77
|
|
|
# a redirection to the dashboard |
|
78
|
|
|
response = self.c.get('/?course=%u' % |
|
79
|
|
|
self.another_course.pk, follow=True) |
|
80
|
|
|
self.assertRedirects(response, '/dashboard/') |
|
81
|
|
|
|
|
82
|
|
|
# Check if course is enabled now, based on assignment count |
|
83
|
|
|
assignments_after = len(student.profile.open_assignments()) |
|
84
|
|
|
self.assertNotEqual(assignments_before, assignments_after) |
|
85
|
|
|
|
|
86
|
|
|
def test_enable_course_with_get_for_fresh_user(self): |
|
87
|
|
|
student = self.user |
|
88
|
|
|
|
|
89
|
|
|
student.first_name = "" |
|
90
|
|
|
student.save() |
|
91
|
|
|
|
|
92
|
|
|
# One default course registration in cases.py |
|
93
|
|
|
assignments_before = len(student.profile.open_assignments()) |
|
94
|
|
|
self.assertEqual(assignments_before, |
|
95
|
|
|
len(self.all_student_visible_assignments)) |
|
96
|
|
|
|
|
97
|
|
|
# Go to root URL with course demand, |
|
98
|
|
|
# message for missing details should show up |
|
99
|
|
|
response = self.c.get('/?course=%u' % |
|
100
|
|
|
self.another_course.pk, follow=True) |
|
101
|
|
|
self.assertContains(response, "incomplete") |
|
102
|
|
|
|
|
103
|
|
|
# Check if course is enabled now, based on assignment count |
|
104
|
|
|
assignments_after = len(student.profile.open_assignments()) |
|
105
|
|
|
self.assertNotEqual(assignments_before, assignments_after) |
|
106
|
|
|
|
|
107
|
|
|
def test_cannot_see_other_users(self): |
|
108
|
|
|
self.create_submissions() |
|
109
|
|
|
self.create_and_login_user(get_student_dict(1)) |
|
110
|
|
|
cases = { |
|
111
|
|
|
self.open_assignment_sub: (403, ), |
|
112
|
|
|
self.soft_deadline_passed_assignment_sub: (403, ), |
|
113
|
|
|
self.hard_deadline_passed_assignment_sub: (403, ), |
|
114
|
|
|
} |
|
115
|
|
|
for submission in cases: |
|
116
|
|
|
response = self.c.get('/details/%s/' % submission.pk) |
|
117
|
|
|
expected_responses = cases[submission] |
|
118
|
|
|
self.assertIn(response.status_code, expected_responses) |
|
119
|
|
|
|
|
120
|
|
|
def test_index_view(self): |
|
121
|
|
|
# User is already logged in, expecting dashboard redirect |
|
122
|
|
|
response = self.c.get('/') |
|
123
|
|
|
self.assertEqual(response.status_code, 302) |
|
124
|
|
|
|
|
125
|
|
|
def test_index_without_login_view(self): |
|
126
|
|
|
self.c.logout() |
|
127
|
|
|
response = self.c.get('/') |
|
128
|
|
|
self.assertEqual(response.status_code, 200) |
|
129
|
|
|
|
|
130
|
|
|
def test_logout_view(self): |
|
131
|
|
|
# User is already logged in, expecting redirect |
|
132
|
|
|
response = self.c.get('/logout/') |
|
133
|
|
|
self.assertEqual(response.status_code, 302) |
|
134
|
|
|
|
|
135
|
|
|
def test_new_submission_view(self): |
|
136
|
|
|
response = self.c.get('/assignments/%s/new/' % self.open_assignment.pk) |
|
137
|
|
|
self.assertEqual(response.status_code, 200) |
|
138
|
|
|
|
|
139
|
|
|
def test_settings_view(self): |
|
140
|
|
|
response = self.c.get('/settings/') |
|
141
|
|
|
self.assertEqual(response.status_code, 200) |
|
142
|
|
|
# Send new data, saving should redirect to dashboard |
|
143
|
|
|
response = self.c.post('/settings/', |
|
144
|
|
|
{'username': 'foobar', |
|
145
|
|
|
'first_name': 'Foo', |
|
146
|
|
|
'last_name': 'Bar', |
|
147
|
|
|
'email': '[email protected]'} |
|
148
|
|
|
) |
|
149
|
|
|
self.assertEqual(response.status_code, 302) |
|
150
|
|
|
u = User.objects.get(pk=self.user.pk) |
|
151
|
|
|
self.assertEqual(u.username, 'foobar') |
|
152
|
|
|
|
|
153
|
|
|
def test_courses_view(self): |
|
154
|
|
|
response = self.c.get('/courses/') |
|
155
|
|
|
self.assertEqual(response.status_code, 200) |
|
156
|
|
|
|
|
157
|
|
|
def test_change_courses_view(self): |
|
158
|
|
|
response = self.c.get('/courses/') |
|
159
|
|
|
self.assertEqual(response.status_code, 200) |
|
160
|
|
|
# Send new data, saving should redirect to dashboard |
|
161
|
|
|
course_ids = [str(self.course.pk), str(self.another_course.pk)] |
|
162
|
|
|
response = self.c.post('/courses/', {'courses': course_ids}) |
|
163
|
|
|
self.assertEqual(response.status_code, 302) |
|
164
|
|
|
|
|
165
|
|
|
def test_enforced_user_settings_view(self): |
|
166
|
|
|
self.user.first_name = "" |
|
167
|
|
|
self.user.save() |
|
168
|
|
|
response = self.c.get('/dashboard/') |
|
169
|
|
|
self.assertContains(response, "incomplete") |
|
170
|
|
|
|
|
171
|
|
|
def test_dashboard_view(self): |
|
172
|
|
|
response = self.c.get('/dashboard/') |
|
173
|
|
|
self.assertEqual(response.status_code, 200) |
|
174
|
|
|
|
|
175
|
|
|
def test_machine_view(self): |
|
176
|
|
|
machine = create_test_machine('127.0.0.1') |
|
177
|
|
|
response = self.c.get('/machine/%u/' % machine.pk) |
|
178
|
|
|
self.assertEqual(response.status_code, 200) |
|
179
|
|
|
|
|
180
|
|
|
def test_cannot_use_teacher_backend(self): |
|
181
|
|
|
response = self.c.get('/teacher/opensubmit/submission/') |
|
182
|
|
|
# 302: can access the model in principle |
|
183
|
|
|
# 403: can never access the app label |
|
184
|
|
|
self.assertEqual(response.status_code, 302) |
|
185
|
|
|
|
|
186
|
|
|
def test_student_becomes_course_owner_by_signal(self): |
|
187
|
|
|
# Before rights assignment |
|
188
|
|
|
response = self.c.get( |
|
189
|
|
|
'/teacher/opensubmit/course/%u/change/' % (self.course.pk)) |
|
190
|
|
|
# 302: can access the model in principle |
|
191
|
|
|
# 403: can never access the app label |
|
192
|
|
|
self.assertEqual(response.status_code, 302) |
|
193
|
|
|
# Assign rights |
|
194
|
|
|
old_owner = self.course.owner |
|
195
|
|
|
self.course.owner = self.user |
|
196
|
|
|
self.course.save() |
|
197
|
|
|
# After rights assignment |
|
198
|
|
|
response = self.c.get( |
|
199
|
|
|
'/teacher/opensubmit/course/%u/change/' % (self.course.pk)) |
|
200
|
|
|
self.assertEqual(response.status_code, 200) |
|
201
|
|
|
# Take away rights |
|
202
|
|
|
self.course.owner = old_owner |
|
203
|
|
|
self.course.save() |
|
204
|
|
|
# After rights removal |
|
205
|
|
|
response = self.c.get( |
|
206
|
|
|
'/teacher/opensubmit/course/%u/change/' % (self.course.pk)) |
|
207
|
|
|
# 302: can access the model in principle |
|
208
|
|
|
# 403: can never access the app label |
|
209
|
|
|
self.assertEqual(response.status_code, 302) |
|
210
|
|
|
|
|
211
|
|
|
def test_student_becomes_tutor(self): |
|
212
|
|
|
# Before rights assignment |
|
213
|
|
|
response = self.c.get('/teacher/opensubmit/submission/') |
|
214
|
|
|
# 302: can access the model in principle |
|
215
|
|
|
# 403: can never access the app label |
|
216
|
|
|
self.assertEqual(response.status_code, 302) |
|
217
|
|
|
# Assign rights |
|
218
|
|
|
self.course.tutors.add(self.user) |
|
219
|
|
|
self.course.save() |
|
220
|
|
|
# After rights assignment |
|
221
|
|
|
response = self.c.get('/teacher/opensubmit/submission/') |
|
222
|
|
|
self.assertEqual(response.status_code, 200) # Access allowed |
|
223
|
|
|
# Take away rights |
|
224
|
|
|
self.course.tutors.remove(self.user) |
|
225
|
|
|
self.course.save() |
|
226
|
|
|
# After rights removal |
|
227
|
|
|
response = self.c.get('/teacher/opensubmit/submission/') |
|
228
|
|
|
# 302: can access the model in principle |
|
229
|
|
|
# 403: can never access the app label |
|
230
|
|
|
self.assertEqual(response.status_code, 302) |
|
231
|
|
|
|
|
232
|
|
|
def test_lti_config_info(self): |
|
233
|
|
|
response = self.c.get('/lti/config/') |
|
234
|
|
|
self.assertEqual(response.status_code, 200) |
|
235
|
|
|
|
|
236
|
|
|
def test_working_lti_credentials(self): |
|
237
|
|
|
|
|
238
|
|
|
self.course.lti_key = 'foo' |
|
239
|
|
|
self.course.lti_secret = 'bar' |
|
240
|
|
|
self.course.save() |
|
241
|
|
|
|
|
242
|
|
|
url = "http://testserver/lti/" |
|
243
|
|
|
|
|
244
|
|
|
consumer = ToolConsumer( |
|
245
|
|
|
consumer_key=self.course.lti_key, |
|
246
|
|
|
consumer_secret=self.course.lti_secret, |
|
247
|
|
|
launch_url=url, |
|
248
|
|
|
params={ |
|
249
|
|
|
'lti_message_type': 'basic-lti-launch-request', |
|
250
|
|
|
'resource_link_id': 1 |
|
251
|
|
|
} |
|
252
|
|
|
) |
|
253
|
|
|
|
|
254
|
|
|
response = self.c.post(url, consumer.generate_launch_data()) |
|
255
|
|
|
self.assertEqual(302, response.status_code) |
|
256
|
|
|
|
|
257
|
|
|
def test_wrong_lti_credentials(self): |
|
258
|
|
|
url = "http://testserver/lti/" |
|
259
|
|
|
|
|
260
|
|
|
consumer = ToolConsumer( |
|
261
|
|
|
consumer_key="foo", |
|
262
|
|
|
consumer_secret="bar", |
|
263
|
|
|
launch_url=url, |
|
264
|
|
|
params={ |
|
265
|
|
|
'lti_message_type': 'basic-lti-launch-request', |
|
266
|
|
|
'resource_link_id': 1 |
|
267
|
|
|
} |
|
268
|
|
|
) |
|
269
|
|
|
|
|
270
|
|
|
response = self.c.post(url, consumer.generate_launch_data()) |
|
271
|
|
|
self.assertEqual(403, response.status_code) |
|
272
|
|
|
|