Skip to content

Commit

Permalink
Add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
faucomte97 committed Nov 14, 2022
1 parent 8186199 commit 8f41cd1
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 60 deletions.
2 changes: 1 addition & 1 deletion game/static/game/css/level_selection.css
Original file line number Diff line number Diff line change
Expand Up @@ -89,4 +89,4 @@
cursor: default;
font-weight: 300;
text-decoration: none;
}
}
57 changes: 57 additions & 0 deletions game/tests/test_level_selection.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,11 @@
from django.test.testcases import TestCase
from django.urls import reverse

from game.models import Level
from game.permissions import can_play_level
from game.tests.utils.level import create_save_level
from game.tests.utils.teacher import add_teacher_to_school, create_school
from game.views.level import _next_level_url


class LevelSelectionTestCase(TestCase):
Expand Down Expand Up @@ -147,3 +150,57 @@ def test_custom_levels_access(self):
assert len(response.context["directly_shared_levels"]) == 1
assert response.context["directly_shared_levels"][0]["owner"] == student2.new_user
assert response.context["indirectly_shared_levels"] == {}

def test_cannot_access_locked_level(self):
email, password = signup_teacher_directly()

teacher = Teacher.objects.get(new_user__email=email)

school = create_school()
add_teacher_to_school(teacher, school, is_admin=True)

class1, _, access_code1 = create_class_directly(email)
class2, _, access_code2 = create_class_directly(email)
_, _, student1 = create_school_student_directly(access_code1)
_, _, student2 = create_school_student_directly(access_code2)

level1 = Level.objects.get(id=1)

level1.locked_for_class.add(class1)

assert not can_play_level(student1.new_user, level1, False)
assert can_play_level(student2.new_user, level1, False)

def test_next_level_for_locked_levels(self):
email, password = signup_teacher_directly()

teacher = Teacher.objects.get(new_user__email=email)

school = create_school()
add_teacher_to_school(teacher, school, is_admin=True)

klass, _, access_code = create_class_directly(email)
_, _, student = create_school_student_directly(access_code)

level1 = Level.objects.get(name="1")
level2 = Level.objects.get(name="2")
level3 = Level.objects.get(name="3")
level4 = Level.objects.get(name="4")
level106 = Level.objects.get(name="106")
level107 = Level.objects.get(name="107")
level108 = Level.objects.get(name="108")
level109 = Level.objects.get(name="109")

level2.locked_for_class.add(klass)
level3.locked_for_class.add(klass)
level107.locked_for_class.add(klass)
level108.locked_for_class.add(klass)
level109.locked_for_class.add(klass)

next_level_url = _next_level_url(level1, student.new_user, False)

assert next_level_url == f"/rapidrouter/{level4.name}/"

next_level_url = _next_level_url(level106, student.new_user, False)

assert next_level_url == f"/rapidrouter/{level109.name}/"
80 changes: 21 additions & 59 deletions game/views/level.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
from __future__ import division
from __future__ import absolute_import
from __future__ import division

from builtins import str
from builtins import object
import json
from builtins import object
from builtins import str

from django.urls import reverse
from django.http import Http404, HttpResponse
from django.shortcuts import render, get_object_or_404
from django.urls import reverse
from django.utils import timezone
from django.utils.safestring import mark_safe
from django.views.decorators.http import require_POST
Expand Down Expand Up @@ -47,7 +47,7 @@ def play_default_level(request, levelName):
return play_level(request, level)


def _next_level_url(level, request, night_mode):
def _next_level_url(level, user, night_mode):
"""
Find the next available level. By default, this will be the `next_level` field in the Level model, but in the case
where the user is a student and the teacher has locked certain levels, then loop until we find the next unlocked
Expand All @@ -58,8 +58,8 @@ def _next_level_url(level, request, night_mode):

next_level = level.next_level

if not request.user.is_anonymous and hasattr(request.user.userprofile, "student"):
student = request.user.userprofile.student
if not user.is_anonymous and hasattr(user.userprofile, "student"):
student = user.userprofile.student
klass = student.class_field

is_next_level_locked = klass in next_level.locked_for_class.all()
Expand Down Expand Up @@ -115,16 +115,10 @@ def play_level(request, level, from_editor=False):
:template:`game/game.html`
"""

night_mode = (
False if not app_settings.NIGHT_MODE_FEATURE_ENABLED else "night" in request.GET
)
night_mode = False if not app_settings.NIGHT_MODE_FEATURE_ENABLED else "night" in request.GET

if not permissions.can_play_level(
request.user, level, app_settings.EARLY_ACCESS_FUNCTION(request)
):
return renderError(
request, messages.noPermissionTitle(), messages.notSharedLevel()
)
if not permissions.can_play_level(request.user, level, app_settings.EARLY_ACCESS_FUNCTION(request)):
return renderError(request, messages.noPermissionTitle(), messages.notSharedLevel())

# Set default level description/hint lookups
lesson = "description_level_default"
Expand Down Expand Up @@ -157,19 +151,12 @@ def play_level(request, level, from_editor=False):
if not request.user.is_anonymous and hasattr(request.user.userprofile, "student"):
student = request.user.userprofile.student
attempt = (
Attempt.objects.filter(
level=level,
student=student,
finish_time__isnull=True,
night_mode=night_mode,
)
Attempt.objects.filter(level=level, student=student, finish_time__isnull=True, night_mode=night_mode)
.order_by("-start_time")
.first()
)
if not attempt:
attempt = Attempt(
level=level, student=student, score=None, night_mode=night_mode
)
attempt = Attempt(level=level, student=student, score=None, night_mode=night_mode)
fetch_workspace_from_last_attempt(attempt)
attempt.save()
else:
Expand Down Expand Up @@ -218,21 +205,17 @@ def play_level(request, level, from_editor=False):
"character_height": character_height,
"wreckage_url": wreckage_url,
"night_mode": night_mode_javascript,
"night_mode_feature_enabled": str(
app_settings.NIGHT_MODE_FEATURE_ENABLED
).lower(),
"night_mode_feature_enabled": str(app_settings.NIGHT_MODE_FEATURE_ENABLED).lower(),
"model_solution": model_solution,
"next_level_url": _next_level_url(level, request, night_mode),
"next_level_url": _next_level_url(level, request.user, night_mode),
"flip_night_mode_url": _level_url(level, not night_mode),
},
)


def fetch_workspace_from_last_attempt(attempt):
latest_attempt = (
Attempt.objects.filter(
level=attempt.level, student=attempt.student, night_mode=attempt.night_mode
)
Attempt.objects.filter(level=attempt.level, student=attempt.student, night_mode=attempt.night_mode)
.order_by("-start_time")
.first()
)
Expand All @@ -248,23 +231,15 @@ def delete_level(request, levelID):
level_management.delete_level(level)
success = True

return HttpResponse(
json.dumps({"success": success}), content_type="application/javascript"
)
return HttpResponse(json.dumps({"success": success}), content_type="application/javascript")


def submit_attempt(request):
"""Processes a request on submission of the program solving the current level."""
if (
not request.user.is_anonymous
and request.method == "POST"
and hasattr(request.user.userprofile, "student")
):
if not request.user.is_anonymous and request.method == "POST" and hasattr(request.user.userprofile, "student"):
level = get_object_or_404(Level, id=request.POST.get("level", 1))
student = request.user.userprofile.student
attempt = Attempt.objects.filter(
level=level, student=student, finish_time__isnull=True
).first()
attempt = Attempt.objects.filter(level=level, student=student, finish_time__isnull=True).first()
if attempt:
attempt.score = float(request.POST.get("score"))
attempt.workspace = request.POST.get("workspace")
Expand All @@ -278,10 +253,7 @@ def submit_attempt(request):

def record_best_attempt(attempt):
best_attempt = Attempt.objects.filter(
level=attempt.level,
student=attempt.student,
night_mode=attempt.night_mode,
is_best_attempt=True,
level=attempt.level, student=attempt.student, night_mode=attempt.night_mode, is_best_attempt=True
).first()
if best_attempt and (best_attempt.score <= attempt.score):
best_attempt.is_best_attempt = False
Expand Down Expand Up @@ -328,12 +300,7 @@ def load_workspace(request, workspaceID):
workspace = Workspace.objects.get(id=workspaceID)
if permissions.can_load_workspace(request.user, workspace):
return HttpResponse(
json.dumps(
{
"contents": workspace.contents,
"python_contents": workspace.python_contents,
}
),
json.dumps({"contents": workspace.contents, "python_contents": workspace.python_contents}),
content_type="application/json",
)

Expand Down Expand Up @@ -383,12 +350,7 @@ def load_workspace_solution(request, levelName):
workspace.python_enabled = True

return HttpResponse(
json.dumps(
{
"contents": workspace.contents,
"python_contents": workspace.python_contents,
}
),
json.dumps({"contents": workspace.contents, "python_contents": workspace.python_contents}),
content_type="application/json",
)

Expand Down

0 comments on commit 8f41cd1

Please sign in to comment.