Compare commits
2 Commits
65dfa9d599
...
eb1e46751d
Author | SHA1 | Date | |
---|---|---|---|
eb1e46751d | |||
1cc99412bc |
@ -62,7 +62,7 @@ addopts = ["--maxfail=2", "-rf"]
|
||||
|
||||
[tool.coverage.run]
|
||||
branch = true
|
||||
omit = ["conftest.py"]
|
||||
omit = ["conftest.py", "spiderweb/tests/*"]
|
||||
|
||||
[tool.coverage.report]
|
||||
# Regexes for lines to exclude from consideration
|
||||
@ -81,6 +81,8 @@ exclude_also = [
|
||||
|
||||
# Don't complain about abstract methods, they aren't run:
|
||||
"@(abc\\.)?abstractmethod",
|
||||
# Type checking lines are never run:
|
||||
"if TYPE_CHECKING:",
|
||||
]
|
||||
|
||||
ignore_errors = true
|
@ -175,6 +175,8 @@ class SpiderwebRouter(LocalServerMixin, MiddlewareMixin, RoutesMixin, FernetMixi
|
||||
)
|
||||
|
||||
if self.staticfiles_dirs:
|
||||
if not isinstance(self.staticfiles_dirs, list):
|
||||
self.staticfiles_dirs = [self.staticfiles_dirs]
|
||||
for static_dir in self.staticfiles_dirs:
|
||||
static_dir = pathlib.Path(static_dir)
|
||||
if not pathlib.Path(self.BASE_DIR / static_dir).exists():
|
||||
|
@ -29,8 +29,10 @@ class HttpResponse:
|
||||
self.data = data
|
||||
self.context = context if context else {}
|
||||
self.status_code = status_code
|
||||
self.headers = headers if headers else {}
|
||||
self.headers = Headers(**{k.lower(): v for k, v in self.headers.items()})
|
||||
self._headers = headers if headers else {}
|
||||
self.headers = Headers()
|
||||
for k, v in self._headers.items():
|
||||
self.headers[k.lower()] = v
|
||||
if not self.headers.get("content-type"):
|
||||
self.headers["content-type"] = "text/html; charset=utf-8"
|
||||
self.headers["server"] = "Spiderweb"
|
||||
|
0
spiderweb/tests/staticfiles/.gitkeep
Normal file
0
spiderweb/tests/staticfiles/.gitkeep
Normal file
@ -0,0 +1 @@
|
||||
hi
|
6
spiderweb/tests/staticfiles/style.css
Normal file
6
spiderweb/tests/staticfiles/style.css
Normal file
@ -0,0 +1,6 @@
|
||||
.body {
|
||||
background-color: #f0f0f0;
|
||||
font-family: Arial, sans-serif;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
149
spiderweb/tests/test_cookies.py
Normal file
149
spiderweb/tests/test_cookies.py
Normal 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")
|
35
spiderweb/tests/test_jinja_extras.py
Normal file
35
spiderweb/tests/test_jinja_extras.py
Normal 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)]
|
@ -7,12 +7,14 @@ from spiderweb.exceptions import (
|
||||
SpiderwebNetworkException,
|
||||
SpiderwebException,
|
||||
ReverseNotFound,
|
||||
GeneralException,
|
||||
)
|
||||
from spiderweb.response import (
|
||||
HttpResponse,
|
||||
JsonResponse,
|
||||
TemplateResponse,
|
||||
RedirectResponse,
|
||||
FileResponse,
|
||||
)
|
||||
from hypothesis import given, strategies as st
|
||||
|
||||
@ -240,3 +242,94 @@ def test_reverse_nonexistent_view():
|
||||
|
||||
with pytest.raises(ReverseNotFound):
|
||||
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"
|
||||
]
|
||||
|
15
spiderweb/tests/test_server.py
Normal file
15
spiderweb/tests/test_server.py
Normal 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")
|
Loading…
Reference in New Issue
Block a user