19.10. Tests Use Case

19.10.1. Use Case - 0x01

import logging
from typing import Literal, Any, NamedTuple
from unittest import IsolatedAsyncioTestCase
import re
from httpx import Response, AsyncClient

BASE_URL = 'http://localhost:8000'

log = logging.getLogger(__name__)


async def request(method: Literal['GET', 'POST', 'PUT', 'DELETE'] = 'GET',
                  path: str = '/',
                  data: dict[str, Any] | None = None,
                  headers: dict[str, Any] | None = None,
                  cookies: dict[str, Any] | None = None,
                  verbose: bool = False,
                  ) -> Response:
    if verbose:
        log.warning(f'Request: {locals()}')
    async with AsyncClient(base_url=BASE_URL) as client:
        return await client.request(
            method=method, url=path, data=data,
            cookies=cookies, headers=headers)


class CSRF(NamedTuple):
    header: str
    token: str


async def get_csrf(url: str) -> CSRF:
    pattern = r'<input type="hidden" name="csrfmiddlewaretoken" value="([a-zA-Z0-9]+)">'
    resp = await request('GET', url)
    if csrf := re.search(pattern, resp.text):
        return CSRF(header=resp.cookies['csrftoken'], token=csrf.group(1))


class IndexTest(IsolatedAsyncioTestCase):
    async def test_index_redirects_to_login_page(self):
        resp = await request('GET', '/')
        self.assertEqual(resp.status_code, 302)
        self.assertEqual(resp.next_request.url.path, '/login/')
        self.assertEqual(resp.headers['Content-Type'], 'text/html; charset=utf-8')


class LoginTest(IsolatedAsyncioTestCase):
    async def test_login_has_form(self):
        resp = await request('GET', '/login/')
        self.assertIn('input type="text" name="username"', resp.text)
        self.assertIn('input type="password" name="password"', resp.text)
        self.assertIn('button type="submit"', resp.text)

    async def test_login_csrf_required(self):
        resp = await request('POST', '/login/', data={'username': 'admin', 'password': 'valid'})
        self.assertEqual(resp.status_code, 403)
        self.assertIn('<title>403 Forbidden</title>', resp.text)
        self.assertIn('CSRF verification failed.', resp.text)

    async def test_login_csrf_exists(self):
        csrf = await get_csrf('/login/')
        self.assertIsNotNone(csrf.header)
        self.assertIsNotNone(csrf.token)
        self.assertRegex(csrf.header, r'^[a-zA-Z0-9]+$')
        self.assertRegex(csrf.token, r'^[a-zA-Z0-9]+$')

    async def test_login_failed(self):
        csrf = await get_csrf('/login/')
        resp = await request(
            method='POST',
            path='/login/',
            data={'username': 'admin', 'password': 'invalid', 'csrfmiddlewaretoken': csrf.token},
            cookies={'csrftoken': csrf.header})
        msg = 'Please enter the correct username and password for a staff account.'
        self.assertIn(msg, resp.text)

    async def test_login_success(self):
        csrf = await get_csrf('/login/')
        resp = await request(
            method='POST',
            path='/login/',
            data={'username': 'admin', 'password': 'valid', 'csrfmiddlewaretoken': csrf.token},
            cookies={'csrftoken': csrf.header})
        msg = 'Please enter the correct username and password for a staff account.'
        self.assertNotIn(msg, resp.text)
        self.assertEqual(resp.status_code, 302)
        self.assertEqual(resp.next_request.url.path, '/accounts/profile/')

19.10.2. Use Case - 0x02

import logging
from django.contrib.auth import get_user_model
from django.test import TestCase


logging.basicConfig(
    level=logging.INFO,
    format='[%(asctime).19s] %(levelname).4s %(message)s',
)

log = logging.getLogger(__name__)


class TestURL(TestCase):
    SHOW_VALID = False
    SHOW_SKIPPED = False
    assert_http_status = []

    def setUp(self):
        super().setUp()
        User = get_user_model()
        self.user = User.objects.create_superuser('admin', 'admin@example.com', 'valid')
        self.client.login(username='admin', password='valid')

    def test_url(self):
        errors = []
        for row in self.assert_http_status:
            url = row['url']
            status = row['status']
            skip = row.get('skip', False)
            if skip:
                if self.SHOW_SKIPPED:
                    msg = 'skip'
                    log.warning(f'{msg:4} {url}')
                continue
            response = self.client.get(url)
            if response.status_code == status:
                if self.SHOW_VALID:
                    log.info(f'{response.status_code:4} {url}')
            else:
                log.error(f'{response.status_code:4} {url}')
                errors.append({'expected': status, 'got': response.status_code, 'url': url})
        if errors:
            from pprint import pformat
            errors = pformat(errors)
            raise AssertionError(f'HTTP errors \n{errors}')
from myproject.tests import TestURL


class HealthTestURL(TestURL):
    assert_http_status = [
        {'status': 200, 'url': '/health/'},
        {'status': 200, 'url': '/health/bloodpressure/'},
        {'status': 200, 'url': '/health/bloodpressure/add/'},
        {'status': 200, 'url': '/health/bloodpressure/1/change/', 'skip': True},
        {'status': 200, 'url': '/health/heartrate/'},
        {'status': 200, 'url': '/health/heartrate/add/'},
        {'status': 200, 'url': '/health/heartrate/1/change/', 'skip': True},
        {'status': 200, 'url': '/health/temperature/'},
        {'status': 200, 'url': '/health/temperature/add/'},
        {'status': 200, 'url': '/health/temperature/1/change/', 'skip': True},
    ]