Skip to content

Commit a63fd5d

Browse files
authored
Merge pull request #80 from opensafely-core/template-coverage
Add coverage for django templates
2 parents d323290 + 0bc8f12 commit a63fd5d

9 files changed

Lines changed: 126 additions & 13 deletions

File tree

airlock/templates/file_browser/index.html

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,6 @@
7777
{% endfragment %}
7878

7979
{% #card title=title custom_button=action_button %}
80-
8180
{% if context == "request" %}
8281
{% #description_item title="Status:" %}{{ release_request.status.name }}{% /description_item %}
8382
{% endif %}

airlock/templates/login.html

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,6 @@
22

33
{% block metatitle %}Login | OpenSAFELY Airlock{% endblock metatitle %}
44

5-
{% block breadcrumbs %}
6-
{% url "home" as home_url %}
7-
8-
{% #breadcrumbs %}
9-
{% breadcrumb title="Home" url=home_url %}
10-
{% breadcrumb title="Login" active=True %}
11-
{% /breadcrumbs %}
12-
{% endblock breadcrumbs %}
13-
145
{% block content %}
156
<section class="flex flex-col max-w-2xl gap-y-4">
167
<h1 class="text-3xl break-words font-bold text-slate-900 mt-2 md:mt-0 md:col-span-3 md:text-4xl">

airlock/views.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ def login(request):
3535

3636
if request.method != "POST":
3737
next_url = request.GET.get("next", default_next_url)
38+
if request.user is not None:
39+
return redirect(next_url)
3840
token_login_form = TokenLoginForm()
3941
else:
4042
next_url = request.POST.get("next", default_next_url)
@@ -300,7 +302,7 @@ def request_release_files(request, request_id):
300302
except api.RequestPermissionDenied as exc:
301303
raise PermissionDenied(str(exc))
302304
except requests.HTTPError as err:
303-
if settings.DEBUG: # pragma: nocover
305+
if settings.DEBUG:
304306
return TemplateResponse(
305307
request,
306308
"jobserver-error.html",

pyproject.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,9 @@ exclude_also = [
3232
# this indicates that a method should be defined in a subclass
3333
"raise NotImplementedError",
3434
]
35+
36+
[tool.coverage.django_coverage_plugin]
37+
template_extensions = "html"
38+
exclude_blocks = [
39+
"\\/\\w+"
40+
]

requirements.dev.in

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,6 @@ pytest-cov
99
pytest-django
1010
pytest-playwright
1111
responses
12+
# Currently using our fork of django_coverage_plugin, pending
13+
# upstream PR https://github.com/nedbat/django_coverage_plugin/pull/93
14+
https://github.com/opensafely-core/django_coverage_plugin/archive/153a0ca6c02f7f01831568a546c848c4a3f082cd.zip

requirements.dev.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,9 @@ coverage==7.4.0 \
168168
# via
169169
# coverage
170170
# pytest-cov
171+
django-coverage-plugin @ https://github.com/opensafely-core/django_coverage_plugin/archive/153a0ca6c02f7f01831568a546c848c4a3f082cd.zip \
172+
--hash=sha256:db70db8737dd269c346f7af6e0c735a3d6462b944302f6bc0178c7bac9a4c7ee
173+
# via -r requirements.dev.in
171174
greenlet==3.0.3 \
172175
--hash=sha256:01bc7ea167cf943b4c802068e178bbf70ae2e8c080467070d01bfa02f337ee67 \
173176
--hash=sha256:0448abc479fab28b00cb472d278828b3ccca164531daab4e970a0458786055d6 \

tests/factories.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,11 @@ def write_request_file(request, path, contents=""):
4747
path.write_text(contents)
4848

4949

50-
def create_filegroup(release_request, group_name):
50+
def create_filegroup(release_request, group_name, filepaths=None):
51+
for filepath in filepaths or []:
52+
api.add_file_to_request(
53+
release_request, filepath, User(1, release_request.author), group_name
54+
)
5155
return api._get_or_create_filegroupmetadata(release_request.id, group_name)
5256

5357

tests/integration/test_auth_views.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,22 @@
66
pytestmark = pytest.mark.django_db
77

88

9+
def test_login_get(client):
10+
response = client.get("/login/")
11+
assert response.status_code == 200
12+
assert "token_login_form" in response.context
13+
14+
15+
def test_login_already_logged_in(client):
16+
session_user = {"id": 1, "username": "test"}
17+
session = client.session
18+
session["user"] = session_user
19+
session.save()
20+
response = client.get("/login/")
21+
assert response.status_code == 302
22+
assert response.url == ("/workspaces/")
23+
24+
925
@mock.patch("airlock.login_api.requests.post", autospec=True)
1026
def test_login(requests_post, client, settings):
1127
settings.AIRLOCK_API_TOKEN = "test_api_token"

tests/integration/test_views.py

Lines changed: 90 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from io import BytesIO
2+
13
import pytest
24
import requests
35
from django.contrib import messages
@@ -64,6 +66,14 @@ def test_workspace_view_with_file(client_with_permission, ui_options):
6466
assert "foobar" in response.rendered_content
6567

6668

69+
def test_workspace_view_with_html_file(client_with_permission, ui_options):
70+
factories.write_workspace_file(
71+
"workspace", "file.html", "<html><body>foobar</body></html>"
72+
)
73+
response = client_with_permission.get("/workspaces/view/workspace/file.html")
74+
assert "foobar" in response.rendered_content
75+
76+
6777
def test_workspace_view_with_404(client_with_permission):
6878
factories.create_workspace("workspace")
6979
response = client_with_permission.get("/workspaces/view/workspace/no_such_file.txt")
@@ -78,6 +88,14 @@ def test_workspace_view_redirects_to_directory(client_with_permission):
7888
assert response.headers["Location"] == "/workspaces/view/workspace/some_dir/"
7989

8090

91+
def test_workspace_view_directory_with_sub_directory(client_with_permission):
92+
workspace = factories.create_workspace("workspace")
93+
factories.write_workspace_file(workspace, "sub_dir/file.txt")
94+
response = client_with_permission.get("/workspaces/view/workspace", follow=True)
95+
assert "sub_dir" in response.rendered_content
96+
assert "file.txt" in response.rendered_content
97+
98+
8199
def test_workspace_view_redirects_to_file(client_with_permission):
82100
factories.write_workspace_file("workspace", "file.txt")
83101
response = client_with_permission.get("/workspaces/view/workspace/file.txt/")
@@ -305,13 +323,45 @@ def test_request_view_with_directory(client_with_permission, ui_options):
305323

306324
def test_request_view_with_file(client_with_permission, ui_options):
307325
release_request = factories.create_release_request("workspace")
308-
factories.write_request_file(release_request, "file.txt", "foobar")
326+
factories.write_workspace_file("workspace", "file.txt", "foobar")
327+
factories.create_filegroup(
328+
release_request, group_name="default_group", filepaths=["file.txt"]
329+
)
330+
309331
response = client_with_permission.get(
310332
f"/requests/view/{release_request.id}/file.txt"
311333
)
334+
assert "default_group" in response.rendered_content
312335
assert "foobar" in response.rendered_content
313336

314337

338+
def test_request_view_with_submitted_request(client_with_permission, ui_options):
339+
release_request = factories.create_release_request(
340+
"workspace", status=Status.SUBMITTED
341+
)
342+
response = client_with_permission.get(
343+
f"/requests/view/{release_request.id}", follow=True
344+
)
345+
assert "Reject Request" in response.rendered_content
346+
assert "Release Files" in response.rendered_content
347+
348+
349+
def test_request_view_with_authored_request_file(client_with_permission, ui_options):
350+
release_request = factories.create_release_request(
351+
"workspace",
352+
user=User.from_session(client_with_permission.session),
353+
status=Status.SUBMITTED,
354+
)
355+
factories.write_workspace_file("workspace", "file.txt", "foobar")
356+
factories.create_filegroup(
357+
release_request, group_name="default_group", filepaths=["file.txt"]
358+
)
359+
response = client_with_permission.get(
360+
f"/requests/view/{release_request.id}/file.txt", follow=True
361+
)
362+
assert "Remove this file" in response.rendered_content
363+
364+
315365
def test_request_view_with_404(client_with_permission):
316366
release_request = factories.create_release_request("workspace")
317367
response = client_with_permission.get(
@@ -499,6 +549,45 @@ def test_requests_release_jobserver_403(client_with_permission, release_files_st
499549
assert response.status_code == 403
500550

501551

552+
@pytest.mark.parametrize(
553+
"content_type,content,should_contain_iframe",
554+
[
555+
("text/plain", b"An error from job-server", False),
556+
("text/html", b"<p>An error from job-server</p>", True),
557+
],
558+
)
559+
def test_requests_release_jobserver_403_with_debug(
560+
client_with_permission,
561+
release_files_stubber,
562+
settings,
563+
content_type,
564+
content,
565+
should_contain_iframe,
566+
):
567+
settings.DEBUG = True
568+
release_request = factories.create_release_request(
569+
"workspace",
570+
id="request_id",
571+
status=Status.SUBMITTED,
572+
)
573+
factories.write_request_file(release_request, "test/file.txt", "test")
574+
575+
response = requests.Response()
576+
response.status_code = 403
577+
response.headers = {"Content-Type": content_type}
578+
response.raw = BytesIO(content)
579+
api403 = requests.HTTPError(response=response)
580+
release_files_stubber(release_request, body=api403)
581+
582+
# test 403 is handled
583+
response = client_with_permission.post("/requests/release/request_id")
584+
# DEBUG is on, so we return the job-server error
585+
assert response.status_code == 200
586+
assert "An error from job-server" in response.rendered_content
587+
contains_iframe = "<iframe" in response.rendered_content
588+
assert contains_iframe == should_contain_iframe
589+
590+
502591
def test_requests_release_files_404(client_with_permission, release_files_stubber):
503592
release_request = factories.create_release_request(
504593
"workspace",

0 commit comments

Comments
 (0)