6 Commits

4 changed files with 61 additions and 21 deletions

View File

@ -359,10 +359,13 @@ Writing Tests
============= =============
You can write tests with our test client that handles HTTP Digest You can write tests with our test client that handles HTTP Digest
Authentication. Example for a unittest testcase: Authentication.
Example for a unittest_ test case:
:: ::
from flask import Flask
from flask_digest_auth import Client from flask_digest_auth import Client
from flask_testing import TestCase from flask_testing import TestCase
from my_app import create_app from my_app import create_app
@ -385,6 +388,41 @@ Authentication. Example for a unittest testcase:
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
Example for a pytest_ test:
::
import pytest
from flask import Flask
from flask_digest_auth import Client
from my_app import create_app
@pytest.fixture()
def app():
app: Flask = create_app({
"SECRET_KEY": token_urlsafe(32),
"TESTING": True
})
app.test_client_class = Client
yield app
@pytest.fixture()
def client(app):
return app.test_client()
def test_admin(app: Flask, client: Client):
with app.app_context():
response = self.client.get("/admin")
assert response.status_code == 401
response = self.client.get(
"/admin", digest_auth=("my_name", "my_pass"))
assert response.status_code == 200
.. _unittest: https://docs.python.org/3/library/unittest.html
.. _pytest: https://pytest.org
Copyright Copyright
========= =========

View File

@ -17,7 +17,7 @@
[metadata] [metadata]
name = flask-digest-auth name = flask-digest-auth
version = 0.2.0 version = 0.2.1
author = imacat author = imacat
author_email = imacat@mail.imacat.idv.tw author_email = imacat@mail.imacat.idv.tw
description = The Flask HTTP Digest Authentication project. description = The Flask HTTP Digest Authentication project.

View File

@ -35,14 +35,15 @@ _PASSWORD: str = "Circle Of Life"
class User: class User:
"""A dummy user""" """A dummy user"""
def __init__(self, username: str, password_hash: str): def __init__(self, username: str, password: str):
"""Constructs a dummy user. """Constructs a dummy user.
:param username: The username. :param username: The username.
:param password_hash: The password hash. :param password: The clear-text password.
""" """
self.username: str = username self.username: str = username
self.password_hash: str = password_hash self.password_hash: str = make_password_hash(
_REALM, username, password)
self.visits: int = 0 self.visits: int = 0
@ -63,9 +64,8 @@ class AuthenticationTestCase(TestCase):
auth: DigestAuth = DigestAuth(realm=_REALM) auth: DigestAuth = DigestAuth(realm=_REALM)
auth.init_app(app) auth.init_app(app)
user_db: t.Dict[str, User] \ self.user: User = User(_USERNAME, _PASSWORD)
= {_USERNAME: User( user_db: t.Dict[str, User] = {_USERNAME: self.user}
_USERNAME, make_password_hash(_REALM, _USERNAME, _PASSWORD))}
@auth.register_get_password @auth.register_get_password
def get_password_hash(username: str) -> t.Optional[str]: def get_password_hash(username: str) -> t.Optional[str]:
@ -141,7 +141,7 @@ class AuthenticationTestCase(TestCase):
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertEqual(response.data.decode("UTF-8"), self.assertEqual(response.data.decode("UTF-8"),
f"Hello, {_USERNAME}! #2") f"Hello, {_USERNAME}! #2")
self.assertEqual(g.user.visits, 1) self.assertEqual(self.user.visits, 1)
def test_stale_opaque(self) -> None: def test_stale_opaque(self) -> None:
"""Tests the stale and opaque value. """Tests the stale and opaque value.
@ -218,4 +218,4 @@ class AuthenticationTestCase(TestCase):
response = self.client.get(admin_uri) response = self.client.get(admin_uri)
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertEqual(g.user.visits, 2) self.assertEqual(self.user.visits, 2)

View File

@ -21,7 +21,6 @@
import typing as t import typing as t
from secrets import token_urlsafe from secrets import token_urlsafe
import flask_login
from flask import Response, Flask, g, redirect, request from flask import Response, Flask, g, redirect, request
from flask_testing import TestCase from flask_testing import TestCase
from werkzeug.datastructures import WWWAuthenticate, Authorization from werkzeug.datastructures import WWWAuthenticate, Authorization
@ -36,14 +35,15 @@ _PASSWORD: str = "Circle Of Life"
class User: class User:
"""A dummy user.""" """A dummy user."""
def __init__(self, username: str, password_hash: str): def __init__(self, username: str, password: str):
"""Constructs a dummy user. """Constructs a dummy user.
:param username: The username. :param username: The username.
:param password_hash: The password hash. :param password: The clear-text password.
""" """
self.username: str = username self.username: str = username
self.password_hash: str = password_hash self.password_hash: str = make_password_hash(
_REALM, username, password)
self.visits: int = 0 self.visits: int = 0
self.is_authenticated: bool = True self.is_authenticated: bool = True
self.is_active: bool = True self.is_active: bool = True
@ -86,9 +86,8 @@ class FlaskLoginTestCase(TestCase):
auth: DigestAuth = DigestAuth(realm=_REALM) auth: DigestAuth = DigestAuth(realm=_REALM)
auth.init_app(app) auth.init_app(app)
user_db: t.Dict[str, User] \ self.user: User = User(_USERNAME, _PASSWORD)
= {_USERNAME: User( user_db: t.Dict[str, User] = {_USERNAME: self.user}
_USERNAME, make_password_hash(_REALM, _USERNAME, _PASSWORD))}
@auth.register_get_password @auth.register_get_password
def get_password_hash(username: str) -> t.Optional[str]: def get_password_hash(username: str) -> t.Optional[str]:
@ -154,7 +153,7 @@ class FlaskLoginTestCase(TestCase):
:return: None. :return: None.
""" """
if not self.has_flask_login: if not self.has_flask_login:
self.skipTest("Skipped testing Flask-Login integration without it.") self.skipTest("Skipped without Flask-Login.")
response: Response = self.client.get(self.app.url_for("admin-1")) response: Response = self.client.get(self.app.url_for("admin-1"))
self.assertEqual(response.status_code, 401) self.assertEqual(response.status_code, 401)
@ -167,7 +166,7 @@ class FlaskLoginTestCase(TestCase):
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertEqual(response.data.decode("UTF-8"), self.assertEqual(response.data.decode("UTF-8"),
f"Hello, {_USERNAME}! #2") f"Hello, {_USERNAME}! #2")
self.assertEqual(flask_login.current_user.visits, 1) self.assertEqual(self.user.visits, 1)
def test_stale_opaque(self) -> None: def test_stale_opaque(self) -> None:
"""Tests the stale and opaque value. """Tests the stale and opaque value.
@ -175,7 +174,7 @@ class FlaskLoginTestCase(TestCase):
:return: None. :return: None.
""" """
if not self.has_flask_login: if not self.has_flask_login:
self.skipTest("Skipped testing Flask-Login integration without it.") self.skipTest("Skipped without Flask-Login.")
admin_uri: str = self.app.url_for("admin-1") admin_uri: str = self.app.url_for("admin-1")
response: Response response: Response
@ -222,6 +221,9 @@ class FlaskLoginTestCase(TestCase):
:return: None. :return: None.
""" """
if not self.has_flask_login:
self.skipTest("Skipped without Flask-Login.")
admin_uri: str = self.app.url_for("admin-1") admin_uri: str = self.app.url_for("admin-1")
logout_uri: str = self.app.url_for("logout") logout_uri: str = self.app.url_for("logout")
response: Response response: Response
@ -253,4 +255,4 @@ class FlaskLoginTestCase(TestCase):
response = self.client.get(admin_uri) response = self.client.get(admin_uri)
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertEqual(flask_login.current_user.visits, 2) self.assertEqual(self.user.visits, 2)