summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMitsuo Tokumori <[email protected]>2023-10-29 06:59:37 -0500
committerMitsuo Tokumori <[email protected]>2023-10-29 06:59:37 -0500
commit9baa55436bc5a98d118a17656bbf25e563522964 (patch)
treedaa01118bd11f5cf13d386d2c727a384563df1da
parent71e7d6516608486f67afad5aad1f7b7f9a45886f (diff)
downloadustayml-9baa55436bc5a98d118a17656bbf25e563522964.tar.gz
ustayml-9baa55436bc5a98d118a17656bbf25e563522964.tar.bz2
ustayml-9baa55436bc5a98d118a17656bbf25e563522964.zip
Add unit tests
-rw-r--r--flaskr/__init__.py6
-rw-r--r--pyproject.toml9
-rw-r--r--tests/conftest.py61
-rw-r--r--tests/data.sql9
-rw-r--r--tests/test_auth.py62
-rw-r--r--tests/test_blog.py93
-rw-r--r--tests/test_db.py33
-rw-r--r--tests/test_factory.py11
8 files changed, 280 insertions, 4 deletions
diff --git a/flaskr/__init__.py b/flaskr/__init__.py
index f2a4584..25c2682 100644
--- a/flaskr/__init__.py
+++ b/flaskr/__init__.py
@@ -24,9 +24,9 @@ def create_app(test_config=None):
pass
# Routes
- # @app.route('/hello')
- # def index():
- # return 'Hello, World!'
+ @app.route('/hello')
+ def hello():
+ return 'Hello, World!'
# Register functions and blueprints
from . import db
diff --git a/pyproject.toml b/pyproject.toml
index ab85115..5156197 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -8,4 +8,11 @@ dependencies = [
[build-system]
requires = ["flit_core<4"]
-build-backend = "flit_core.buildapi" \ No newline at end of file
+build-backend = "flit_core.buildapi"
+
+[tool.pytest.ini_options]
+testpaths = ["tests"]
+
+[tool.coverage.run]
+branch = true
+source = ["flaskr"] \ No newline at end of file
diff --git a/tests/conftest.py b/tests/conftest.py
new file mode 100644
index 0000000..45079a9
--- /dev/null
+++ b/tests/conftest.py
@@ -0,0 +1,61 @@
+"""contains setup functions called fixtures that each test will use"""
+
+import os
+import tempfile
+
+import pytest
+from flaskr import create_app
+from flaskr.db import get_db, init_db
+
+with open(os.path.join(os.path.dirname(__file__), 'data.sql'), 'rb') as f:
+ _data_sql = f.read().decode('utf8')
+
+
+def app():
+ db_fd, db_path = tempfile.mkstemp()
+
+ app = create_app({
+ 'TESTING': True,
+ 'DATABASE': db_path,
+ })
+
+ with app.app_context():
+ init_db()
+ get_db().executescript(_data_sql)
+
+ yield app
+
+ os.close(db_fd)
+ os.unlink(db_path)
+
+
+def client(app):
+ """can make requests to the app without running the server"""
+ return app.test_client()
+
+
+def runner(app):
+ """can all the Click commands registered with the application"""
+ return app.test_cli_runner()
+
+
+class AuthActions(object):
+ def __init__(self, client):
+ self._client = client
+
+ def login(self, username='test', password='test'):
+ return self._client.post(
+ '/auth/login',
+ data={'username': username, 'password': password}
+ )
+
+ def logout(self):
+ return self._client.get('/auth/logout')
+
+
+def auth(client):
+ return AuthActions(client) \ No newline at end of file
diff --git a/tests/data.sql b/tests/data.sql
new file mode 100644
index 0000000..46269e4
--- /dev/null
+++ b/tests/data.sql
@@ -0,0 +1,9 @@
+INSERT INTO user (username, password)
+VALUES
+ ('test', 'pbkdf2:sha256:50000$TCI4GzcX$0de171a4f4dac32e3364c7ddc7c14f3e2fa61f2d17574483f7ffbb431b4acb2f'),
+ ('other', 'pbkdf2:sha256:50000$kJPKsz6N$d2d4784f1b030a9761f5ccaeeaca413f27f2ecb76d6168407af962ddce849f79');
+
+INSERT INTO post (title, body, author_id, created)
+VALUES
+ ('test title', 'test' || x'0a' || 'body', 1, '2018-01-01 00:00:00');
+
diff --git a/tests/test_auth.py b/tests/test_auth.py
new file mode 100644
index 0000000..401b61f
--- /dev/null
+++ b/tests/test_auth.py
@@ -0,0 +1,62 @@
+import pytest
+from flask import g, session
+from flaskr.db import get_db
+
+
+def test_register(client, app):
+ assert client.get('/auth/register').status_code == 200
+ response = client.post(
+ '/auth/register', data={'username': 'a', 'password': 'a'}
+ )
+ assert response.headers["Location"] == "/auth/login"
+
+ with app.app_context():
+ assert get_db().execute(
+ "SELECT * FROM user WHERE username = 'a'",
+ ).fetchone() is not None
+
+
[email protected](('username', 'password', 'message'), (
+ ('', '', b'Username is required.'),
+ ('a', '', b'Password is required.'),
+ ('test', 'test', b'already registered'),
+))
+def test_register_validate_input(client, username, password, message):
+ response = client.post(
+ '/auth/register',
+ data={'username': username, 'password': password}
+ )
+ assert message in response.data
+
+
+def test_login(client, auth):
+ assert client.get('/auth/login').status_code == 200
+ response = auth.login()
+ assert response.headers["Location"] == "/"
+
+ with client:
+ """
+ Using client in a with block allows accessing context variables such as
+ session after the response is returned. Normally, accessing session
+ outside of a request would raise an error.
+ """
+ client.get('/')
+ assert session['user_id'] == 1
+ assert g.user['username'] == 'test'
+
+
[email protected](('username', 'password', 'message'), (
+ ('a', 'test', b'Incorrect username.'),
+ ('test', 'a', b'Incorrect password.'),
+))
+def test_login_validate_input(auth, username, password, message):
+ response = auth.login(username, password)
+ assert message in response.data
+
+
+def test_logout(client, auth):
+ auth.login()
+
+ with client:
+ auth.logout()
+ assert 'user_id' not in session
diff --git a/tests/test_blog.py b/tests/test_blog.py
new file mode 100644
index 0000000..75f67a5
--- /dev/null
+++ b/tests/test_blog.py
@@ -0,0 +1,93 @@
+import pytest
+from flaskr.db import get_db
+
+
+def test_index(client, auth):
+ response = client.get('/')
+ assert b"Log In" in response.data
+ assert b"Register" in response.data
+
+ auth.login()
+ response = client.get('/')
+ assert b'Log Out' in response.data
+ assert b'test title' in response.data
+ assert b'by test on 2018-01-01' in response.data
+ assert b'test\nbody' in response.data
+ assert b'href="/1/update"' in response.data
+
+
+ '/create',
+ '/1/update',
+ '/1/delete',
+))
+def test_login_required(client, path):
+ response = client.post(path)
+ assert response.headers["Location"] == "/auth/login"
+
+
+def test_author_required(app, client, auth):
+ # change the post author to another user
+ with app.app_context():
+ db = get_db()
+ db.execute('UPDATE post SET author_id = 2 WHERE id = 1')
+ db.commit()
+
+ auth.login()
+ # current user can't modify other user's post
+ assert client.post('/1/update').status_code == 403
+ assert client.post('/1/delete').status_code == 403
+ # current user doesn't see edit link
+ assert b'href="/1/update"' not in client.get('/').data
+
+
+ '/2/update',
+ '/2/delete',
+))
+def test_exists_required(client, auth, path):
+ auth.login()
+ assert client.post(path).status_code == 404
+
+
+def test_create(client, auth, app):
+ auth.login()
+ assert client.get('/create').status_code == 200
+ client.post('/create', data={'title': 'created', 'body': ''})
+
+ with app.app_context():
+ db = get_db()
+ count = db.execute('SELECT COUNT(id) FROM post').fetchone()[0]
+ assert count == 2
+
+
+def test_update(client, auth, app):
+ auth.login()
+ assert client.get('/1/update').status_code == 200
+ client.post('/1/update', data={'title': 'updated', 'body': ''})
+
+ with app.app_context():
+ db = get_db()
+ post = db.execute('SELECT * FROM post WHERE id = 1').fetchone()
+ assert post['title'] == 'updated'
+
+
+ '/create',
+ '/1/update',
+))
+def test_create_update_validate(client, auth, path):
+ auth.login()
+ response = client.post(path, data={'title': '', 'body': ''})
+ assert b'Title is required.' in response.data
+
+
+def test_delete(client, auth, app):
+ auth.login()
+ response = client.post('/1/delete')
+ assert response.headers["Location"] == "/"
+
+ with app.app_context():
+ db = get_db()
+ post = db.execute('SELECT * FROM post WHERE id = 1').fetchone()
+ assert post is None \ No newline at end of file
diff --git a/tests/test_db.py b/tests/test_db.py
new file mode 100644
index 0000000..6ac4635
--- /dev/null
+++ b/tests/test_db.py
@@ -0,0 +1,33 @@
+import sqlite3
+
+import pytest
+from flaskr.db import get_db
+
+
+def test_get_close_db(app):
+ with app.app_context():
+ db = get_db()
+ assert db is get_db()
+
+ with pytest.raises(sqlite3.ProgrammingError) as e:
+ db.execute('SELECT 1')
+
+ assert 'closed' in str(e.value)
+
+
+def test_init_db_command(runner, monkeypatch):
+ """
+ This test uses Pytest’s monkeypatch fixture to replace the init_db
+ function with one that records that it’s been called. The runner fixture you
+ wrote above is used to call the init-db command by name.
+ """
+ class Recorder(object):
+ called = False
+
+ def fake_init_db():
+ Recorder.called = True
+
+ monkeypatch.setattr('flaskr.db.init_db', fake_init_db)
+ result = runner.invoke(args=['init-db'])
+ assert 'Initialized' in result.output
+ assert Recorder.called \ No newline at end of file
diff --git a/tests/test_factory.py b/tests/test_factory.py
new file mode 100644
index 0000000..024c19a
--- /dev/null
+++ b/tests/test_factory.py
@@ -0,0 +1,11 @@
+from flaskr import create_app
+
+
+def test_config():
+ assert not create_app().testing
+ assert create_app({'TESTING': True}).testing
+
+
+def test_hello(client):
+ response = client.get('/hello')
+ assert response.data == b'Hello, World!' \ No newline at end of file