]> littlesong.place Git - littlesongplace.git/commitdiff
Add integration tests
authorChris Fulljames <christianfulljames@gmail.com>
Mon, 20 Jan 2025 01:55:38 +0000 (20:55 -0500)
committerChris Fulljames <christianfulljames@gmail.com>
Mon, 20 Jan 2025 01:55:38 +0000 (20:55 -0500)
dev-requirements.txt [new file with mode: 0644]
main.py
test.py [new file with mode: 0644]

diff --git a/dev-requirements.txt b/dev-requirements.txt
new file mode 100644 (file)
index 0000000..26b77f6
--- /dev/null
@@ -0,0 +1,2 @@
+-r requirements.txt
+pytest
diff --git a/main.py b/main.py
index 449c7de8f100d06acaeaa36fde475603668988a2..866b08861e2fda5d4a1af6c70f26df722d92449c 100644 (file)
--- a/main.py
+++ b/main.py
@@ -466,7 +466,6 @@ def songs():
             tag=tag,
             song_list=render_template("song-list.html", songs=songs))
 
-# TODO: USE THIS
 def flash_and_log(msg, category=None):
     flash(msg, category)
     username = session["username"] if "username" in session else "N/A"
diff --git a/test.py b/test.py
new file mode 100644 (file)
index 0000000..7942be2
--- /dev/null
+++ b/test.py
@@ -0,0 +1,153 @@
+import tempfile
+from pathlib import Path
+
+import pytest
+from flask import session
+
+import main
+
+@pytest.fixture
+def app():
+    # Use temporary data directory
+    with tempfile.TemporaryDirectory() as data_dir:
+        main.DATA_DIR = Path(data_dir)
+
+        # Initialize Database
+        with main.app.app_context():
+            db = main.get_db()
+            with main.app.open_resource('schema.sql', mode='r') as f:
+                db.cursor().executescript(f.read())
+            db.commit()
+
+        yield main.app
+
+@pytest.fixture
+def client(app):
+    return app.test_client()
+
+################################################################################
+# Signup
+################################################################################
+
+def test_signup_get(client):
+    response = client.get("/signup")
+    assert b"Rules" in response.data
+
+def _post_signup_form(client, username, password, password_confirm=None):
+    if password_confirm is None:
+        password_confirm = password
+    return client.post(
+            "/signup",
+            data=dict(username=username, password=password, password_confirm=password_confirm))
+
+def test_signup_success(client):
+    response = _post_signup_form(client, "user", "password")
+    assert response.status_code == 302
+    assert response.headers["Location"] == "/login"
+
+    response = client.get("/login")
+    assert b"User created" in response.data
+
+def _test_signup_error(client, username, password, password_confirm, msg):
+    response = _post_signup_form(client, username, password, password_confirm)
+    assert response.status_code == 302
+    assert response.headers["Location"] == "None"
+
+    response = client.get("/signup")
+    assert b"User created" not in response.data
+    assert msg in response.data
+
+def test_signup_username_invalid_characters(client):
+    _test_signup_error(client, "user@gmail.com", "password", "password", b"special characters")
+
+def test_signup_username_too_short(client):
+    _test_signup_error(client, "us", "password", "password", b"at least 3 characters")
+
+def test_signup_username_too_long(client):
+    _test_signup_error(client, "a"*31, "password", "password", b"more than 30 characters")
+
+def test_signup_passwords_dont_match(client):
+    _test_signup_error(client, "user", "password", "passwor", b"Passwords do not match")
+
+def test_signup_password_too_short(client):
+    _test_signup_error(client, "user", "passwor", "passwor", b"at least 8 characters")
+
+def test_signup_user_exists(client):
+    # Success the first time
+    response = _post_signup_form(client, "user", "password")
+    assert response.status_code == 302
+    assert response.headers["Location"] == "/login"
+    response = client.get("/login")
+    assert b"User created" in response.data
+
+    # Error the second time
+    _test_signup_error(client, "user", "password", "password", b"already taken")
+
+################################################################################
+# Login/Logout
+################################################################################
+
+def test_login_get(client):
+    response = client.get("/login")
+    assert b"Sign In" in response.data
+
+def _create_user(client, username, password):
+    response = _post_signup_form(client, username, password)
+    assert response.status_code == 302
+    assert response.headers["Location"] == "/login"
+
+def test_login_success(client):
+    _create_user(client, "username", "password")
+    response = client.post("/login", data={"username": "username", "password": "password"})
+    assert response.status_code == 302
+    assert response.headers["Location"] == "/users/username"
+
+    response = client.get("/users/username")
+    assert b"Signed in as username" in response.data
+
+def test_login_invalid_username(client):
+    _create_user(client, "username", "password")
+    response = client.post("/login", data={"username": "incorrect", "password": "password"})
+    assert response.status_code == 200
+    assert b"Invalid username/password" in response.data
+
+def test_login_invalid_username(client):
+    _create_user(client, "username", "password")
+    response = client.post("/login", data={"username": "username", "password": "incorrect"})
+    assert response.status_code == 200
+    assert b"Invalid username/password" in response.data
+
+def test_logout(client, app):
+    with client:
+        _create_user(client, "username", "password")
+        response = client.post("/login", data={"username": "username", "password": "password"})
+        assert response.status_code == 302
+
+        assert session["username"] == "username"
+        assert session["userid"] == 1
+
+        response = client.get("/logout")
+        assert response.status_code == 302
+        assert response.headers["Location"] == "/"
+
+        assert "username" not in session
+        assert "userid" not in session
+
+################################################################################
+# Profile
+################################################################################
+
+# TODO
+
+################################################################################
+# Upload/Edit Song
+################################################################################
+
+# TODO
+
+################################################################################
+# Song Lists
+################################################################################
+
+# TODO
+