Compare commits

..

2 Commits

Author SHA1 Message Date
eb1e46751d add lots more tests 2024-09-22 20:21:34 -04:00
1cc99412bc 🐛 fix header assignment bug 2024-09-22 20:01:07 -04:00
10 changed files with 309 additions and 4 deletions

View File

@ -62,7 +62,7 @@ addopts = ["--maxfail=2", "-rf"]
[tool.coverage.run] [tool.coverage.run]
branch = true branch = true
omit = ["conftest.py"] omit = ["conftest.py", "spiderweb/tests/*"]
[tool.coverage.report] [tool.coverage.report]
# Regexes for lines to exclude from consideration # Regexes for lines to exclude from consideration
@ -81,6 +81,8 @@ exclude_also = [
# Don't complain about abstract methods, they aren't run: # Don't complain about abstract methods, they aren't run:
"@(abc\\.)?abstractmethod", "@(abc\\.)?abstractmethod",
] # Type checking lines are never run:
"if TYPE_CHECKING:",
]
ignore_errors = true ignore_errors = true

View File

@ -175,6 +175,8 @@ class SpiderwebRouter(LocalServerMixin, MiddlewareMixin, RoutesMixin, FernetMixi
) )
if self.staticfiles_dirs: if self.staticfiles_dirs:
if not isinstance(self.staticfiles_dirs, list):
self.staticfiles_dirs = [self.staticfiles_dirs]
for static_dir in self.staticfiles_dirs: for static_dir in self.staticfiles_dirs:
static_dir = pathlib.Path(static_dir) static_dir = pathlib.Path(static_dir)
if not pathlib.Path(self.BASE_DIR / static_dir).exists(): if not pathlib.Path(self.BASE_DIR / static_dir).exists():

View File

@ -29,8 +29,10 @@ class HttpResponse:
self.data = data self.data = data
self.context = context if context else {} self.context = context if context else {}
self.status_code = status_code self.status_code = status_code
self.headers = headers if headers else {} self._headers = headers if headers else {}
self.headers = Headers(**{k.lower(): v for k, v in self.headers.items()}) self.headers = Headers()
for k, v in self._headers.items():
self.headers[k.lower()] = v
if not self.headers.get("content-type"): if not self.headers.get("content-type"):
self.headers["content-type"] = "text/html; charset=utf-8" self.headers["content-type"] = "text/html; charset=utf-8"
self.headers["server"] = "Spiderweb" self.headers["server"] = "Spiderweb"

View File

View File

@ -0,0 +1 @@
hi

View File

@ -0,0 +1,6 @@
.body {
background-color: #f0f0f0;
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
}

View File

@ -0,0 +1,149 @@
from datetime import datetime
import pytest
from spiderweb import HttpResponse
from spiderweb.exceptions import GeneralException
from spiderweb.tests.helpers import setup
def test_valid_cookie():
app, environ, start_response = setup()
@app.route("/")
def index(request):
resp = HttpResponse("Hello, World!")
resp.set_cookie("cookie", "value")
return resp
response = app(environ, start_response)
assert response == [b"Hello, World!"]
assert start_response.get_headers()["set-cookie"] == "cookie=value"
def test_invalid_cookie_name():
app, environ, start_response = setup()
@app.route("/")
def index(request):
resp = HttpResponse("Hello, World!")
resp.set_cookie("cookie$%^&*name", "value")
return resp
with pytest.raises(GeneralException) as exc:
app(environ, start_response)
assert str(exc.value) == (
"GeneralException() - Cookie name has illegal characters."
" See https://developer.mozilla.org/en-US/docs/Web/HTTP/"
"Headers/Set-Cookie#attributes for information on allowed"
" characters."
)
def test_cookie_with_domain():
app, environ, start_response = setup()
@app.route("/")
def index(request):
resp = HttpResponse("Hello, World!")
resp.set_cookie("cookie", "value", domain="example.com")
return resp
response = app(environ, start_response)
assert response == [b"Hello, World!"]
assert (
start_response.get_headers()["set-cookie"] == "cookie=value; Domain=example.com"
)
def test_cookie_with_expires():
app, environ, start_response = setup()
expiry_time = datetime(2024, 10, 22, 7, 28)
expiry_time_str = expiry_time.strftime("%a, %d %b %Y %H:%M:%S GMT")
@app.route("/")
def index(request):
resp = HttpResponse("Hello, World!")
resp.set_cookie("cookie", "value", expires=expiry_time)
return resp
response = app(environ, start_response)
assert response == [b"Hello, World!"]
assert (
start_response.get_headers()["set-cookie"]
== f"cookie=value; Expires={expiry_time_str}"
)
def test_cookie_with_max_age():
app, environ, start_response = setup()
@app.route("/")
def index(request):
resp = HttpResponse("Hello, World!")
resp.set_cookie("cookie", "value", max_age=3600)
return resp
response = app(environ, start_response)
assert response == [b"Hello, World!"]
assert start_response.get_headers()["set-cookie"] == "cookie=value; Max-Age=3600"
def test_cookie_with_invalid_samesite_attr():
app, environ, start_response = setup()
@app.route("/")
def index(request):
resp = HttpResponse("Hello, World!")
resp.set_cookie("cookie", "value", same_site="invalid")
return resp
with pytest.raises(GeneralException) as exc:
app(environ, start_response)
assert str(exc.value) == (
"GeneralException() - Invalid value invalid for `same_site` cookie"
" attribute. Valid options are 'strict', 'lax', or 'none'."
)
def test_cookie_partitioned_attr():
app, environ, start_response = setup()
@app.route("/")
def index(request):
resp = HttpResponse()
resp.set_cookie("cookie", "value", partitioned=True)
return resp
app(environ, start_response)
assert start_response.get_headers()["set-cookie"] == "cookie=value; Partitioned"
def test_cookie_secure_attr():
app, environ, start_response = setup()
@app.route("/")
def index(request):
resp = HttpResponse()
resp.set_cookie("cookie", "value", secure=True)
return resp
app(environ, start_response)
assert start_response.get_headers()["set-cookie"] == "cookie=value; Secure"
def test_setting_multiple_cookies():
app, environ, start_response = setup()
@app.route("/")
def index(request):
resp = HttpResponse()
resp.set_cookie("cookie1", "value1")
resp.set_cookie("cookie2", "value2")
return resp
app(environ, start_response)
assert start_response.headers[-1] == ("set-cookie", "cookie2=value2")
assert start_response.headers[-2] == ("set-cookie", "cookie1=value1")

View File

@ -0,0 +1,35 @@
from spiderweb.constants import DEFAULT_ENCODING
from spiderweb.response import TemplateResponse
from spiderweb.tests.helpers import setup
def test_str_template_with_static_tag():
# test that the static tag works
template = """
<html>
<head>
<title>{{ title }}</title>
<link rel="stylesheet" href="{% static 'style.css' %}">
</head>
<body>
<h1>{{ title }}</h1>
<p>{{ content }}</p>
</body>
</html>
"""
context = {"title": "Test", "content": "This is a test."}
app, environ, start_response = setup(
staticfiles_dirs=["spiderweb/tests/staticfiles"], static_url="blorp"
)
@app.route("/")
def index(request):
return TemplateResponse(request, template_string=template, context=context)
rendered_template = (
template.replace("{% static 'style.css' %}", "/blorp/style.css")
.replace("{{ title }}", "Test")
.replace("{{ content }}", "This is a test.")
)
assert app(environ, start_response) == [bytes(rendered_template, DEFAULT_ENCODING)]

View File

@ -7,12 +7,14 @@ from spiderweb.exceptions import (
SpiderwebNetworkException, SpiderwebNetworkException,
SpiderwebException, SpiderwebException,
ReverseNotFound, ReverseNotFound,
GeneralException,
) )
from spiderweb.response import ( from spiderweb.response import (
HttpResponse, HttpResponse,
JsonResponse, JsonResponse,
TemplateResponse, TemplateResponse,
RedirectResponse, RedirectResponse,
FileResponse,
) )
from hypothesis import given, strategies as st from hypothesis import given, strategies as st
@ -240,3 +242,94 @@ def test_reverse_nonexistent_view():
with pytest.raises(ReverseNotFound): with pytest.raises(ReverseNotFound):
app.reverse("qwer") app.reverse("qwer")
def test_setting_content_type_header():
app, environ, start_response = setup()
@app.route("/")
def index(request):
resp = HttpResponse("Hello, World!", headers={"content-type": "text/html"})
return resp
response = app(environ, start_response)
assert response == [b"Hello, World!"]
assert start_response.get_headers()["content-type"] == "text/html"
def test_httpresponse_str_returns_body():
resp = HttpResponse("Hello, World!")
assert str(resp) == "Hello, World!"
def test_template_response_with_no_templates_raises_errors():
app, environ, start_response = setup()
@app.route("/")
def index(request):
return TemplateResponse(request, "")
with pytest.raises(GeneralException) as exc:
app(environ, start_response)
assert (
str(exc.value) == "GeneralException() - TemplateResponse requires a template."
)
def test_template_response_with_no_template_dirs():
template = TemplateResponse("", "test.html")
with pytest.raises(GeneralException) as exc:
template.render()
assert str(exc.value) == (
"GeneralException() - TemplateResponse has no loader. Did you set templates_dirs?"
)
def test_file_response():
resp = FileResponse("spiderweb/tests/staticfiles/file_for_testing_fileresponse.txt")
assert resp.headers["content-type"] == "text/plain"
assert resp.render() == [b"hi"]
def test_requesting_static_file():
app, environ, start_response = setup(
staticfiles_dirs=["spiderweb/tests/staticfiles"], debug=True
)
environ["PATH_INFO"] = "/static/file_for_testing_fileresponse.txt"
assert app(environ, start_response) == [b"hi"]
def test_requesting_nonexistent_static_file():
app, environ, start_response = setup(
staticfiles_dirs=["spiderweb/tests/staticfiles"], debug=True
)
environ["PATH_INFO"] = "/static/does_not_exist.txt"
assert app(environ, start_response) == [
b"Something went wrong.\n\n"
b"Code: 404\n\n"
b"Msg: Not Found\n\n"
b"Desc: The requested resource could not be found"
]
def test_static_file_with_unsafe_path():
app, environ, start_response = setup(
staticfiles_dirs=["spiderweb/tests/staticfiles"], debug=True
)
environ["PATH_INFO"] = "/static/../__init__.py"
assert app(environ, start_response) == [
b"Something went wrong.\n\n"
b"Code: 404\n\n"
b"Msg: Not Found\n\n"
b"Desc: The requested resource could not be found"
]

View File

@ -0,0 +1,15 @@
import pytest
from spiderweb.exceptions import ConfigError
from spiderweb.tests.helpers import setup
def test_staticfiles_dirs_option():
app, environ, start_response = setup(staticfiles_dirs="spiderweb/tests/staticfiles")
assert app.staticfiles_dirs == ["spiderweb/tests/staticfiles"]
def test_staticfiles_dirs_not_found():
with pytest.raises(ConfigError):
app, environ, start_response = setup(staticfiles_dirs="not/a/real/path")