diff --git a/backend/__tests__/test_app.py b/backend/__tests__/test_app.py index d109d81..f586151 100644 --- a/backend/__tests__/test_app.py +++ b/backend/__tests__/test_app.py @@ -9,8 +9,8 @@ def app(): yield create_app( test_config={ - "DATABASE_URL": "sqlite:///:memory:", - "DATABASE_ECHO": True, + "SQLALCHEMY_DATABASE_URI": "sqlite:///:memory:", + "SQLALCHEMY_DATABASE_ECHO": True, } ) diff --git a/backend/__tests__/test_schema_mutations_friendship.py b/backend/__tests__/test_schema_mutations_friendship.py new file mode 100644 index 0000000..34e0559 --- /dev/null +++ b/backend/__tests__/test_schema_mutations_friendship.py @@ -0,0 +1,110 @@ +# mypy: disable-error-code="index" + +from sqlalchemy import select +from sqlalchemy.orm import Session +import pytest +from .. import models as m +from .conftest import Query, Login, Logout + + +@pytest.mark.asyncio +async def test_addFriend_anon(query: Query): + # anon can't add a friend + result = await query( + 'mutation m { addFriend(username: "Frank") }', + error="Anonymous users can't add friends", + ) + assert result.data["addFriend"] is None + + +@pytest.mark.asyncio +async def test_addFriend_self(query: Query, login: Login): + # log in as alice and add herself as a friend + await login("Alice") + result = await query( + 'mutation m { addFriend(username: "Alice") }', + error="You can't add yourself", + ) + assert result.data["addFriend"] is None + + +@pytest.mark.asyncio +async def test_addFriend_dupe(query: Query, login: Login): + # log in as alice and add frank as a friend + await login("Alice") + result = await query('mutation m { addFriend(username: "Frank") }') + assert result.data["addFriend"] is None + + # try to add frank again + result = await query( + 'mutation m { addFriend(username: "Frank") }', + error="Friend request already sent", + ) + assert result.data["addFriend"] is None + + +@pytest.mark.asyncio +async def test_addFriend_notfound(query: Query, login: Login): + # log in as alice and add a non-existent user + await login("Alice") + result = await query( + 'mutation m { addFriend(username: "NotAUser") }', error="User not found" + ) + assert result.data["addFriend"] is None + + +@pytest.mark.asyncio +async def test_removeFriend_notfound(query: Query, login: Login): + # log in as alice and remove a non-existent user + await login("Alice") + result = await query( + 'mutation m { removeFriend(username: "NotAUser") }', error="User not found" + ) + assert result.data["removeFriend"] is None + + +@pytest.mark.asyncio +async def test_addFriend_e2e(query: Query, login: Login): + # log in as alice and add frank as a friend + await login("Alice") + result = await query('mutation m { addFriend(username: "Frank") }') + assert result.data["addFriend"] is None + + # check the request was created + result = await query("query q { user { friendsOutgoing { username } } }") + assert "Frank" in [ + user["username"] for user in result.data["user"]["friendsOutgoing"] + ] + + # log in as frank and check from his end + await login("Frank") + result = await query("query q { user { friendsIncoming { username } } }") + assert "Alice" in [ + user["username"] for user in result.data["user"]["friendsIncoming"] + ] + + # accept the request + result = await query('mutation m { addFriend(username: "Alice") }') + assert result.data["addFriend"] is None + + # check frank's friends list + result = await query("query q { user { friends { username } } }") + assert "Alice" in [user["username"] for user in result.data["user"]["friends"]] + + # check alice's friends list + await login("Alice") + result = await query("query q { user { friends { username } } }") + assert "Frank" in [user["username"] for user in result.data["user"]["friends"]] + + # remove the friend + result = await query('mutation m { removeFriend(username: "Frank") }') + assert result.data["removeFriend"] is None + + # check alice's friends list + result = await query("query q { user { friends { username } } }") + assert "Frank" not in [user["username"] for user in result.data["user"]["friends"]] + + # check frank's friends list + await login("Frank") + result = await query("query q { user { friends { username } } }") + assert "Alice" not in [user["username"] for user in result.data["user"]["friends"]] diff --git a/backend/__tests__/test_schema_mutations.py b/backend/__tests__/test_schema_mutations_survey.py similarity index 50% rename from backend/__tests__/test_schema_mutations.py rename to backend/__tests__/test_schema_mutations_survey.py index e5f62c3..fca9c4e 100644 --- a/backend/__tests__/test_schema_mutations.py +++ b/backend/__tests__/test_schema_mutations_survey.py @@ -7,222 +7,6 @@ from .conftest import Query, Login, Logout -@pytest.mark.asyncio -async def test_createUser(db: Session, query: Query, subtests): - CREATE_USER = """ - mutation m($username: String!, $password1: String!, $password2: String!, $email: String!) { - createUser( - username: $username - password1: $password1 - password2: $password2 - email: $email - ) { - username - } - } - """ - - with subtests.test("new"): - result = await query( - CREATE_USER, - username="TestUser", - password1="TestPass", - password2="TestPass", - email="", - ) - assert result.data["createUser"]["username"] == "TestUser" - - # check the user was created - user = db.execute( - select(m.User).where( - m.User.username == result.data["createUser"]["username"] - ) - ).scalar_one() - assert user.username == "TestUser" - assert user.check_password("TestPass") - - # check that we were logged in automatically - result = await query("query q { user { username } }") - assert result.data["user"]["username"] == "TestUser" - - with subtests.test("clash"): - result = await query( - CREATE_USER, - username="Alice", - password1="TestPass", - password2="TestPass", - email="", - error="A user with that name already exists", - ) - assert result.data["createUser"] is None - - with subtests.test("login"): - result = await query( - CREATE_USER, - username="Alice", - password1="alicepass", - password2="alicepass", - email="", - ) - assert result.data["createUser"]["username"] == "Alice" - - result = await query("query q { user { username } }") - assert result.data["user"]["username"] == "Alice" - - with subtests.test("badname - none"): - result = await query( - CREATE_USER, - username="", - password1="TestPass", - password2="TestPass", - email="", - error="Username is required", - ) - assert result.data["createUser"] is None - - with subtests.test("badname - long"): - result = await query( - CREATE_USER, - username="12345678901234567890123456789012345678901234567890", - password1="TestPass", - password2="TestPass", - email="", - error="Username needs to be less than 32 characters", - ) - assert result.data["createUser"] is None - - with subtests.test("badname - punctuation"): - result = await query( - CREATE_USER, - username="big waffle!", - password1="TestPass", - password2="TestPass", - email="", - error="Username can only contain letters, numbers, and underscores", - ) - assert result.data["createUser"] is None - - with subtests.test("badpass"): - result = await query( - CREATE_USER, - username="TestUser2", - password1="TestPass", - password2="TestPass2", - email="", - error="Bad password", - ) - assert result.data["createUser"] is None - - -@pytest.mark.asyncio -async def test_login(query): - result = await query( - 'mutation m { login(username: "Alice", password: "badpass") { username } }', - error="User not found", - ) - assert result.data["login"] is None - - -@pytest.mark.asyncio -async def test_login_logout(query: Query, login: Login, logout: Logout): - # anonymous - result = await query("query q { user { username } }") - assert result.data["user"] is None - - # log in - await login("Alice") - - # logged in - result = await query("query q { user { username } }") - assert result.data["user"] == {"username": "Alice"} - - # log out - await logout() - - # anonymous - result = await query("query q { user { username } }") - assert result.data["user"] is None - - -UPDATE_USER = """ - mutation m( - $password: String!, - $username: String!, - $password1: String!, - $password2: String!, - $email: String! - ) { - updateUser( - password: $password, - username: $username, - password1: $password1, - password2: $password2, - email: $email - ) { - username - } - } -""" - - -@pytest.mark.asyncio -async def test_updateUser_anon(query: Query): - result = await query( - UPDATE_USER, - password="", - username="", - password1="", - password2="", - email="", - error="Anonymous users can't save settings", - ) - assert result.data is None - - -@pytest.mark.asyncio -async def test_updateUser_badpass(query: Query, login: Login): - await login("Alice") - result = await query( - UPDATE_USER, - password="asdfasd", - username="", - password1="", - password2="", - email="", - error="Current password incorrect", - ) - assert result.data is None - - -@pytest.mark.asyncio -async def test_updateUser_conflict_username(query: Query, login: Login): - await login("Alice") - result = await query( - UPDATE_USER, - password="alicepass", - username="BoB", - password1="", - password2="", - email="", - error="Another user with that name already exists", - ) - assert result.data is None - - -@pytest.mark.asyncio -async def test_updateUser_valid(query: Query, login: Login): - await login("Alice") - result = await query( - UPDATE_USER, - password="alicepass", - username="Alice2", - password1="pass2", - password2="pass2", - email="foobar@example.com", - ) - assert result.data["updateUser"]["username"] == "Alice2" - - @pytest.mark.asyncio async def test_createSurvey(db: Session, query: Query, login: Login, subtests): CREATE_SURVEY = """ @@ -518,106 +302,3 @@ async def test_saveAnswer_update(db: Session, query: Query, login: Login, subtes .where(m.Answer.question_id == 1) ).scalar_one() assert answer.value == m.WWW.WILL - - -@pytest.mark.asyncio -async def test_addFriend_anon(query: Query): - # anon can't add a friend - result = await query( - 'mutation m { addFriend(username: "Frank") }', - error="Anonymous users can't add friends", - ) - assert result.data["addFriend"] is None - - -@pytest.mark.asyncio -async def test_addFriend_self(query: Query, login: Login): - # log in as alice and add herself as a friend - await login("Alice") - result = await query( - 'mutation m { addFriend(username: "Alice") }', - error="You can't add yourself", - ) - assert result.data["addFriend"] is None - - -@pytest.mark.asyncio -async def test_addFriend_dupe(query: Query, login: Login): - # log in as alice and add frank as a friend - await login("Alice") - result = await query('mutation m { addFriend(username: "Frank") }') - assert result.data["addFriend"] is None - - # try to add frank again - result = await query( - 'mutation m { addFriend(username: "Frank") }', - error="Friend request already sent", - ) - assert result.data["addFriend"] is None - - -@pytest.mark.asyncio -async def test_addFriend_notfound(query: Query, login: Login): - # log in as alice and add a non-existent user - await login("Alice") - result = await query( - 'mutation m { addFriend(username: "NotAUser") }', error="User not found" - ) - assert result.data["addFriend"] is None - - -@pytest.mark.asyncio -async def test_removeFriend_notfound(query: Query, login: Login): - # log in as alice and remove a non-existent user - await login("Alice") - result = await query( - 'mutation m { removeFriend(username: "NotAUser") }', error="User not found" - ) - assert result.data["removeFriend"] is None - - -@pytest.mark.asyncio -async def test_addFriend_e2e(query: Query, login: Login): - # log in as alice and add frank as a friend - await login("Alice") - result = await query('mutation m { addFriend(username: "Frank") }') - assert result.data["addFriend"] is None - - # check the request was created - result = await query("query q { user { friendsOutgoing { username } } }") - assert "Frank" in [ - user["username"] for user in result.data["user"]["friendsOutgoing"] - ] - - # log in as frank and check from his end - await login("Frank") - result = await query("query q { user { friendsIncoming { username } } }") - assert "Alice" in [ - user["username"] for user in result.data["user"]["friendsIncoming"] - ] - - # accept the request - result = await query('mutation m { addFriend(username: "Alice") }') - assert result.data["addFriend"] is None - - # check frank's friends list - result = await query("query q { user { friends { username } } }") - assert "Alice" in [user["username"] for user in result.data["user"]["friends"]] - - # check alice's friends list - await login("Alice") - result = await query("query q { user { friends { username } } }") - assert "Frank" in [user["username"] for user in result.data["user"]["friends"]] - - # remove the friend - result = await query('mutation m { removeFriend(username: "Frank") }') - assert result.data["removeFriend"] is None - - # check alice's friends list - result = await query("query q { user { friends { username } } }") - assert "Frank" not in [user["username"] for user in result.data["user"]["friends"]] - - # check frank's friends list - await login("Frank") - result = await query("query q { user { friends { username } } }") - assert "Alice" not in [user["username"] for user in result.data["user"]["friends"]] diff --git a/backend/__tests__/test_schema_mutations_user.py b/backend/__tests__/test_schema_mutations_user.py new file mode 100644 index 0000000..10051e7 --- /dev/null +++ b/backend/__tests__/test_schema_mutations_user.py @@ -0,0 +1,223 @@ +# mypy: disable-error-code="index" + +from sqlalchemy import select +from sqlalchemy.orm import Session +import pytest +from .. import models as m +from .conftest import Query, Login, Logout + + +@pytest.mark.asyncio +async def test_createUser(db: Session, query: Query, subtests): + CREATE_USER = """ + mutation m($username: String!, $password1: String!, $password2: String!, $email: String!) { + createUser( + username: $username + password1: $password1 + password2: $password2 + email: $email + ) { + username + } + } + """ + + with subtests.test("new"): + result = await query( + CREATE_USER, + username="TestUser", + password1="TestPass", + password2="TestPass", + email="", + ) + assert result.data["createUser"]["username"] == "TestUser" + + # check the user was created + user = db.execute( + select(m.User).where( + m.User.username == result.data["createUser"]["username"] + ) + ).scalar_one() + assert user.username == "TestUser" + assert user.check_password("TestPass") + + # check that we were logged in automatically + result = await query("query q { user { username } }") + assert result.data["user"]["username"] == "TestUser" + + with subtests.test("clash"): + result = await query( + CREATE_USER, + username="Alice", + password1="TestPass", + password2="TestPass", + email="", + error="A user with that name already exists", + ) + assert result.data["createUser"] is None + + with subtests.test("login"): + result = await query( + CREATE_USER, + username="Alice", + password1="alicepass", + password2="alicepass", + email="", + ) + assert result.data["createUser"]["username"] == "Alice" + + result = await query("query q { user { username } }") + assert result.data["user"]["username"] == "Alice" + + with subtests.test("badname - none"): + result = await query( + CREATE_USER, + username="", + password1="TestPass", + password2="TestPass", + email="", + error="Username is required", + ) + assert result.data["createUser"] is None + + with subtests.test("badname - long"): + result = await query( + CREATE_USER, + username="12345678901234567890123456789012345678901234567890", + password1="TestPass", + password2="TestPass", + email="", + error="Username needs to be less than 32 characters", + ) + assert result.data["createUser"] is None + + with subtests.test("badname - punctuation"): + result = await query( + CREATE_USER, + username="big waffle!", + password1="TestPass", + password2="TestPass", + email="", + error="Username can only contain letters, numbers, and underscores", + ) + assert result.data["createUser"] is None + + with subtests.test("badpass"): + result = await query( + CREATE_USER, + username="TestUser2", + password1="TestPass", + password2="TestPass2", + email="", + error="Bad password", + ) + assert result.data["createUser"] is None + + +@pytest.mark.asyncio +async def test_login(query): + result = await query( + 'mutation m { login(username: "Alice", password: "badpass") { username } }', + error="User not found", + ) + assert result.data["login"] is None + + +@pytest.mark.asyncio +async def test_login_logout(query: Query, login: Login, logout: Logout): + # anonymous + result = await query("query q { user { username } }") + assert result.data["user"] is None + + # log in + await login("Alice") + + # logged in + result = await query("query q { user { username } }") + assert result.data["user"] == {"username": "Alice"} + + # log out + await logout() + + # anonymous + result = await query("query q { user { username } }") + assert result.data["user"] is None + + +UPDATE_USER = """ + mutation m( + $password: String!, + $username: String!, + $password1: String!, + $password2: String!, + $email: String! + ) { + updateUser( + password: $password, + username: $username, + password1: $password1, + password2: $password2, + email: $email + ) { + username + } + } +""" + + +@pytest.mark.asyncio +async def test_updateUser_anon(query: Query): + result = await query( + UPDATE_USER, + password="", + username="", + password1="", + password2="", + email="", + error="Anonymous users can't save settings", + ) + assert result.data is None + + +@pytest.mark.asyncio +async def test_updateUser_badpass(query: Query, login: Login): + await login("Alice") + result = await query( + UPDATE_USER, + password="asdfasd", + username="", + password1="", + password2="", + email="", + error="Current password incorrect", + ) + assert result.data is None + + +@pytest.mark.asyncio +async def test_updateUser_conflict_username(query: Query, login: Login): + await login("Alice") + result = await query( + UPDATE_USER, + password="alicepass", + username="BoB", + password1="", + password2="", + email="", + error="Another user with that name already exists", + ) + assert result.data is None + + +@pytest.mark.asyncio +async def test_updateUser_valid(query: Query, login: Login): + await login("Alice") + result = await query( + UPDATE_USER, + password="alicepass", + username="Alice2", + password1="pass2", + password2="pass2", + email="foobar@example.com", + ) + assert result.data["updateUser"]["username"] == "Alice2" diff --git a/backend/__tests__/test_schema_queries.py b/backend/__tests__/test_schema_queries_survey.py similarity index 90% rename from backend/__tests__/test_schema_queries.py rename to backend/__tests__/test_schema_queries_survey.py index 0372cf8..925f025 100644 --- a/backend/__tests__/test_schema_queries.py +++ b/backend/__tests__/test_schema_queries_survey.py @@ -7,68 +7,6 @@ from .conftest import Query, Login, Logout import itertools - -@pytest.mark.asyncio -async def test_user_self(query: Query, login: Login, subtests): - with subtests.test("anon-view-self"): - result = await query("query q { user { username } }") - assert result.data["user"] is None - - with subtests.test("user-view-self"): - await login("Alice") - result = await query( - """ - query q { - user { - username - email - friends { username } - friendsIncoming { username } - friendsOutgoing { username } - } - } - """ - ) - assert result.data["user"] == { - "username": "Alice", - "email": "alice@example.com", - "friends": [ - {"username": "Bob"}, - ], - "friendsIncoming": [ - {"username": "Charlie"}, - ], - "friendsOutgoing": [], - } - - -@pytest.mark.asyncio -async def test_user_others(query: Query, login: Login, subtests): - with subtests.test("anon-view-others"): - result = await query( - 'query q { user(username: "Alice") { username } }', - error="Anonymous users can't view other users", - ) - assert result.data["user"] is None - - with subtests.test("user-view-others-username"): - await login("Alice") - result = await query('query q { user(username: "Bob") { username isFriend } }') - assert result.data["user"] == {"username": "Bob", "isFriend": True} - result = await query( - 'query q { user(username: "Charlie") { username isFriend } }' - ) - assert result.data["user"] == {"username": "Charlie", "isFriend": False} - - with subtests.test("user-view-others-friends"): - await login("Alice") - result = await query( - 'query q { user(username: "Bob") { friends { username } } }', - error="You can only view your own data.", - ) - assert result.data["user"] is None - - @pytest.mark.asyncio async def test_surveys_paging(db: Session, query: Query, subtests): result = await query("query q { surveys { name } }") diff --git a/backend/__tests__/test_schema_queries_user.py b/backend/__tests__/test_schema_queries_user.py new file mode 100644 index 0000000..bd7f356 --- /dev/null +++ b/backend/__tests__/test_schema_queries_user.py @@ -0,0 +1,69 @@ +# mypy: disable-error-code="index" + +from sqlalchemy.orm import Session +import pytest +from .. import models as m +from .. import schema as s +from .conftest import Query, Login, Logout +import itertools + + +@pytest.mark.asyncio +async def test_user_self(query: Query, login: Login, subtests): + with subtests.test("anon-view-self"): + result = await query("query q { user { username } }") + assert result.data["user"] is None + + with subtests.test("user-view-self"): + await login("Alice") + result = await query( + """ + query q { + user { + username + email + friends { username } + friendsIncoming { username } + friendsOutgoing { username } + } + } + """ + ) + assert result.data["user"] == { + "username": "Alice", + "email": "alice@example.com", + "friends": [ + {"username": "Bob"}, + ], + "friendsIncoming": [ + {"username": "Charlie"}, + ], + "friendsOutgoing": [], + } + + +@pytest.mark.asyncio +async def test_user_others(query: Query, login: Login, subtests): + with subtests.test("anon-view-others"): + result = await query( + 'query q { user(username: "Alice") { username } }', + error="Anonymous users can't view other users", + ) + assert result.data["user"] is None + + with subtests.test("user-view-others-username"): + await login("Alice") + result = await query('query q { user(username: "Bob") { username isFriend } }') + assert result.data["user"] == {"username": "Bob", "isFriend": True} + result = await query( + 'query q { user(username: "Charlie") { username isFriend } }' + ) + assert result.data["user"] == {"username": "Charlie", "isFriend": False} + + with subtests.test("user-view-others-friends"): + await login("Alice") + result = await query( + 'query q { user(username: "Bob") { friends { username } } }', + error="You can only view your own data.", + ) + assert result.data["user"] is None