foerderbarometer/input/utils/testing.py

153 lines
4.0 KiB
Python

from typing import Any, Iterable, Mapping, Union, Tuple, Protocol
from django.apps import apps
from django.conf import settings
from django.http import HttpRequest, HttpResponse
from django.http.response import HttpResponseRedirectBase, StreamingHttpResponse
from django.shortcuts import resolve_url
from django.template import Context
from django.template.response import TemplateResponse
from django.test import Client, SimpleTestCase
from django.urls import reverse, ResolverMatch
from django.utils.http import urlencode
FormData = dict
JSONDict = dict
JSONList = list
RequestData = Union[FormData, JSONDict, JSONList]
QueryParams = Union[Mapping[str, Any], Iterable[Tuple[str, Any]]]
class TestClientResponse(Protocol):
client: Client
request: HttpRequest
templates: list
context: Context
resolver_match: ResolverMatch
def json(self) -> Union[JSONList, JSONDict]: ...
Response = Union[
HttpResponse,
HttpResponseRedirectBase,
StreamingHttpResponse,
TemplateResponse,
TestClientResponse,
]
class ObjectWithGetAbsoluteURLMethod(Protocol):
def get_absolute_url(self) -> str: ...
URL = Union[str, ObjectWithGetAbsoluteURLMethod]
URLArgs = Union[tuple, list]
URLKwargs = dict
def get_url(url: URL, args: URLArgs = None, kwargs: URLKwargs = None) -> str:
"""
Helper to reverse the given url name.
"""
if args or kwargs:
return reverse(url, args=args, kwargs=kwargs)
return resolve_url(url)
def get_handler(test_case: SimpleTestCase, method: str = None, data=None):
if data:
method = str.lower(method or 'POST')
else:
method = str.lower(method or 'GET')
return getattr(test_case.client, method)
def request(
test_case: SimpleTestCase,
url: URL,
status_code: int = None,
expected_url: URL = None,
args: URLArgs = None,
kwargs: URLKwargs = None,
headers: dict = None,
msg: str = None,
query_params: QueryParams = None,
method: str = None,
data: RequestData = None,
**options,
) -> Response:
"""
A helper to make a request with the test case's http client.
The given args and kwargs are used to reverse the url
but not the expected url. When expected url needs
args/kwargs pass an absolute url instead.
All additional kwargs are passed as post parameters.
When posting without parameters just pass post=True.
"""
data = data or options or None
handler = get_handler(test_case, method, data)
url = get_url(url, args, kwargs)
if query_params:
url = f'{url}?%s' % urlencode(query_params, doseq=True)
headers = headers or {}
status_code = status_code or 200
response = handler(url, data=data, **headers)
msg = msg or getattr(response, 'content', None)
if expected_url:
test_case.assertRedirects(
response=response,
expected_url=get_url(expected_url),
target_status_code=status_code,
)
else:
test_case.assertEqual(response.status_code, status_code, msg=msg)
return response
def login(test_case: SimpleTestCase, user=None, password: str = None) -> bool:
"""
Logs in the user trying to use the raw password or the given password.
Force logs in the user when no password is found.
"""
user = user or getattr(test_case, 'user')
password = password or getattr(user, 'raw_password', password)
if password is None:
return test_case.client.force_login(user=user) or True
return test_case.client.login(username=user.username, password=password)
def create_user(username: str, *, model=None, **kwargs):
model = model or apps.get_model(settings.AUTH_USER_MODEL)
password = kwargs.setdefault('password', 'P4sSW0rD')
kwargs.setdefault('email', f'{username}@test.case')
kwargs.setdefault(model.USERNAME_FIELD, username)
user = model.objects.create_user(**kwargs)
user.raw_password = password
return user
def create_superuser(username: str, **kwargs):
kwargs['is_superuser'] = True
kwargs['is_staff'] = True
return create_user(username, **kwargs)