Skip to content

Commit

Permalink
finish userpage.py tests
Browse files Browse the repository at this point in the history
  • Loading branch information
akrakman committed Nov 18, 2022
1 parent 0aecede commit 65a31bd
Show file tree
Hide file tree
Showing 7 changed files with 195 additions and 81 deletions.
3 changes: 2 additions & 1 deletion src/backend/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,8 @@ def userpage():
return result, 400
return result, 201
user = page.get_user(username) # request.method == "GET"
return json.dumps(user.__dict__), username, 201
data_dict = dataclasses.asdict(user)
return json.dumps(data_dict), username, 201


@app.route("/logout")
Expand Down
Binary file modified src/backend/database/database_test.db
Binary file not shown.
31 changes: 31 additions & 0 deletions src/backend/in
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
============================= test session starts ==============================
platform darwin -- Python 3.10.5, pytest-7.1.3, pluggy-1.0.0
rootdir: /Users/adenkrakman/Desktop/course-project-group-5/src/backend
plugins: cov-4.0.0
collected 47 items

tests/test_app.py .................. [ 38%]
tests/test_decorators.py . [ 40%]
tests/test_login.py ........ [ 57%]
tests/test_mainpage.py .............. [ 87%]
tests/test_userpage.py ...... [100%]

- generated xml file: /Users/adenkrakman/Desktop/course-project-group-5/src/backend/pytest.xml -

---------- coverage: platform darwin, python 3.10.5-final-0 ----------
Name Stmts Miss Cover Missing
-----------------------------------------------------------
app.py 132 0 100%
config.py 1 0 100%
dataholders/apt.py 9 0 100%
dataholders/mainpage_get.py 15 0 100%
dataholders/review.py 7 0 100%
dataholders/user.py 8 0 100%
decorators.py 27 0 100%
pages/login.py 37 0 100%
pages/mainpage.py 100 0 100%
pages/userpage.py 54 0 100%
-----------------------------------------------------------
TOTAL 390 0 100%

============================== 47 passed in 0.32s ==============================
22 changes: 15 additions & 7 deletions src/backend/pages/login.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,13 @@ def register(
if (not username) or (not email) or (not password) or (not phone):
return RegisterResult("Missing information, please try again", False)

regex_email = re.compile(r"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b")
if not regex_email.fullmatch(email):
if not validate_email(email):
return RegisterResult("Invalid email, please try again", False)

if not validate_phone(phone):
return RegisterResult("Invalid phone number, please try again", False)

if len(password) < 8:
if not validate_password(password):
return RegisterResult("Password is too short, please try again", False)

check = self.register.cursor.execute(
Expand Down Expand Up @@ -62,13 +61,22 @@ def logout(self) -> None:
"""Logout"""


def validate_password(password: str) -> bool:
"""Used in Login and User class"""
return len(password) >= 8


def validate_email(email: str) -> bool:
"""Used in Login and User class"""
regex_email = re.compile(r"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b")
return regex_email.fullmatch(email)


def validate_phone(phone: str) -> bool:
"""Used in Login class and in User class"""
"""Used in Login and User class"""
regex_phone = re.compile(
r"^\s*(?:\+?(\d{1,3}))?[-. (]"
r"*(\d{3})[-. )]*(\d{3})[-. ]"
r"*(\d{4})(?: *x(\d+))?\s*$"
)
if not regex_phone.fullmatch(phone):
return False
return True
return regex_phone.fullmatch(phone)
76 changes: 35 additions & 41 deletions src/backend/pages/userpage.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""Contains the UserPage backend"""
from typing import List
from pages.login import validate_phone
from pages.login import validate_phone, validate_email, validate_password
from dataholders.user import User
from dataholders.apt import Apt
from decorators import use_database
Expand All @@ -15,7 +15,7 @@ def __init__(self, username: str) -> None:
self.user = self.get_user(username)

@use_database
def get_user(self, username: str):
def get_user(self, username: str) -> User:
"""Return User object based on username"""
query_sql = username
user_query = self.get_user.cursor.execute(
Expand All @@ -33,74 +33,68 @@ def get_user(self, username: str):
def update_password(self, password: str) -> bool:
"""Updates password based on username"""
# can use Flask-Hashing if we want
if not validate_password(password):
return False
if self.user.password == password:
return True
query_sql = "%" + str(self.user.user_id) + "%"
self.update_password.cursor.execute(
"UPDATE Users \
SET password = ? \
WHERE user_id = ?",
(password, query_sql),
WHERE (username = ?)",
(password, self.username),
)
self.user.password = password
return True

@use_database
def update_email(self, email: str) -> bool:
"""Updates email based on username"""
if not validate_email(email):
return False
if self.user.email == email:
return True

query_sql = email
self.update_email.cursor.execute(
"UPDATE Users \
SET email = ? \
WHERE username = ?",
(query_sql, self.username),
(email, self.username),
)
new_email = self.update_email.cursor.execute(
"SELECT email \
FROM Users \
WHERE (username = ?)",
(self.username,),
).fetchone()[0]
self.user.email = email
return True

return new_email == email
@use_database
def update_phone(self, phone: str) -> bool:
"""Updates User's phone number if valid"""
if not validate_phone(phone):
return False
if self.user.phone == phone:
return True
self.update_phone.cursor.execute(
"UPDATE Users SET phone = ? WHERE (username = ?)",
(phone, self.username),
)
self.user.phone = phone
return True

@use_database
def get_liked(self, user_id: int) -> List[Apt]:
"""Gets liked apartments based on username"""
apts = []
query_sql = "%" + user_id + "%"
liked = self.get_liked.cursor.execute(
"SELECT a.apt_id, a.apt_name, a.apt_address, a.price_min, a.price_max \
From Reviews r INNER JOIN Apartments a \
FROM Reviews r INNER JOIN Apartments a \
ON r.apt_id = a.apt_id \
WHERE r.user_id = ? AND r.vote = ?",
(query_sql, 1),
)

WHERE r.user_id = ? AND r.vote = 1",
(user_id,),
).fetchall()
for apt in liked:
apt_id, apt_name, apt_address, price_min, price_max = apt
query_sql = "%" + apt_id + "%"
apt_id, apt_name, address, price_min, price_max = apt
rating = self.get_liked.cursor.execute(
"SELECT AVG(r.vote) \
From Reviews r \
FROM Reviews r \
WHERE r.apt_id = ?",
(query_sql),
)
apts.append(
Apt(apt_id, apt_name, rating, apt_address, price_min, price_max)
)
(apt_id,),
).fetchone()[0]
rating = round(rating, 3)
apts.append(Apt(apt_id, apt_name, address, rating, price_min, price_max))
return apts

@use_database
def update_phone(self, phone: str) -> bool:
"""Updates User's phone number if valid"""
if not validate_phone(phone):
return False
self.update_phone.cursor.execute(
"UPDATE Users SET phone = ? WHERE (username = ?)",
(phone, self.username),
)
self.update_phone.connection.commit()
return True
8 changes: 4 additions & 4 deletions src/backend/tests/mainpage_staging.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@ def insert_apartments(self, cursor: sqlite3.Cursor, connection: sqlite3.Connecti
def insert_users(self, cursor: sqlite3.Cursor, connection: sqlite3.Connection):
"""Initialize users for use by test methods"""
args = [
("Minh Phan", "", "", ""),
("Minh", "", "", ""),
("Big_finger", "", "", ""),
("Fig_binger", "", "", ""),
("Minh Phan", "phan_password1", "[email protected]", "111-111-1111"),
("Minh", "minh_password1", "[email protected]", "222-222-2222"),
("Big_finger", "big_password1", "[email protected]", "333-333-3333"),
("Fig_binger", "fig_password1", "[email protected]", "444-444-4444"),
]
cursor.executemany(
"INSERT INTO Users (username, password, email, phone) \
Expand Down
136 changes: 108 additions & 28 deletions src/backend/tests/test_userpage.py
Original file line number Diff line number Diff line change
@@ -1,60 +1,140 @@
"""Test userpage.py"""
import sqlite3
from decorators import use_test
from decorators import use_test, use_database
from tests.mainpage_staging import MainPageStaging
from pages.userpage import UserPage
from pages.login import validate_phone
from dataholders.apt import Apt


class TestUserPage:
"""Test user page class"""

username = "test_username"
alt_username = "alt_username"
userpage = UserPage(username)
phone = "012-345-6789"
invalid_phone = "123-3421-322"
password = "newpassword1234"
invalid_password = "inv2341"
email = "[email protected]"
invalid_email = "testemail@"
userpage = None
main_page_staging = MainPageStaging()

@use_test
def test_valid_phone(self) -> bool:
"""Test update_phone returns True and db entry is the same"""
assert validate_phone(self.phone) is True
assert self.userpage.update_phone(self.phone) is True
@use_database
def initialize(self):
"""Set up tests by inserting a user into the db"""
connection = self.initialize.connection
cursor = self.initialize.cursor
self.insert_users(cursor, connection)
self.userpage = UserPage(self.username)

# mimics update_phone
connection = sqlite3.connect("database/database_test.db")
cursor = connection.cursor()
cursor.execute(
"INSERT INTO Users (username, email, password, phone) \
VALUES (?, ?, ?, ?)",
(self.alt_username, "[email protected]", "password", "011-899-9013"),
)
connection.commit()
def insert_users(self, cursor: sqlite3.Cursor, connection: sqlite3.Connection):
"""Initialize users for testing"""
cursor.execute(
"UPDATE Users SET phone = ? WHERE (username = ?)",
"INSERT INTO Users (username, password, email, phone) \
VALUES (?, ?, ?, ?)",
(
self.phone,
self.alt_username,
self.username,
"beginpassword",
"[email protected]",
"111-555-0022",
),
)
connection.commit()

@use_test
def test_get_user(self):
"""get_user returns correct Use"""
self.initialize()
res = self.userpage.get_user(self.username)
assert res.username == self.username
assert res.password == "beginpassword"
assert res.email == "[email protected]"
assert res.phone == "111-555-0022"
assert self.userpage.get_user("sd") is None

@use_test
def test_valid_password(self):
"""update_password returns True and db entry is correct"""
self.initialize()
res = self.userpage.update_password(self.password)
assert res is True
connection = sqlite3.connect("database/database_test.db")
cursor = connection.cursor()
test_result = cursor.execute(
"SELECT phone FROM Users WHERE (username = ?)", (self.alt_username,)
"SELECT password FROM Users WHERE (username = ?)", (self.username,)
).fetchone()[0]
connection.close()
same_password = self.userpage.update_password(self.password)
assert same_password is True
self.cleanup_db()
assert test_result == self.phone
assert test_result == self.password

@use_test
def test_invalid_phone(self) -> bool:
"""Test update_phone returns False"""
assert validate_phone(self.invalid_phone) is False
def test_valid_email(self):
"""update_email returns True and db entry is correct"""
self.initialize()
res = self.userpage.update_email(self.email)
assert res is True
connection = sqlite3.connect("database/database_test.db")
cursor = connection.cursor()
test_result = cursor.execute(
"SELECT email FROM Users WHERE (username = ?)", (self.username,)
).fetchone()[0]
connection.close()
same_email = self.userpage.update_email(self.email)
assert same_email is True
self.cleanup_db()
assert test_result == self.email

@use_test
def test_invalid_input(self):
"""invalid input returns False"""
self.initialize()
assert self.userpage.update_password(self.invalid_password) is False
assert self.userpage.update_email(self.invalid_email) is False
assert self.userpage.update_phone(self.invalid_phone) is False

@use_test
def cleanup_db(self) -> None:
def test_valid_phone(self):
"""update_phone returns True and db entry is correct"""
self.initialize()
res = self.userpage.update_phone(self.phone)
assert res is True
connection = sqlite3.connect("database/database_test.db")
cursor = connection.cursor()
test_result = cursor.execute(
"SELECT phone FROM Users WHERE (username = ?)", (self.username,)
).fetchone()[0]
connection.close()
same_phone = self.userpage.update_phone(self.phone)
assert same_phone is True
self.cleanup_db()
assert test_result == self.phone

@use_test
def cleanup_db(self):
"""Remove fake data from database"""
connection = sqlite3.connect("database/database_test.db")
cursor = connection.cursor()
cursor.execute("DELETE FROM Users WHERE (username = ?)", (self.username,))
cursor.execute("DELETE FROM Users WHERE (username = ?)", (self.alt_username,))
connection.commit()
connection.close()

@use_test
def test_get_liked(self):
"""returns correct List"""
self.initialize()
self.main_page_staging.initialize_all()
connection = sqlite3.connect("database/database_test.db")
cursor = connection.cursor()
minh_phan_id = cursor.execute(
"SELECT user_id FROM Users WHERE (username = 'Minh Phan')"
).fetchone()[0]
sherman_id = cursor.execute(
"SELECT apt_id FROM Apartments WHERE (apt_name = 'Sherman')"
).fetchone()[0]
res = self.userpage.get_liked(minh_phan_id)
liked = []
liked.append(Apt(sherman_id, "Sherman", "909 S 5th St", 0.333, 5500, 6500))
self.main_page_staging.clean_all()
assert res == liked

3 comments on commit 65a31bd

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Coverage

Coverage Report
FileStmtsMissCover
app.py1320100%
config.py10100%
decorators.py270100%
dataholders
   apt.py90100%
   mainpage_get.py150100%
   review.py70100%
   user.py80100%
pages
   login.py370100%
   mainpage.py1000100%
   userpage.py540100%
TOTAL3900100%

Tests Skipped Failures Errors Time
47 0 💤 0 ❌ 0 🔥 1.162s ⏱️

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Coverage

Coverage Report
FileStmtsMissCover
app.py1320100%
config.py10100%
decorators.py270100%
dataholders
   apt.py90100%
   mainpage_get.py150100%
   review.py70100%
   user.py80100%
pages
   login.py370100%
   mainpage.py1000100%
   userpage.py540100%
TOTAL3900100%

Tests Skipped Failures Errors Time
47 0 💤 0 ❌ 0 🔥 1.294s ⏱️

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Coverage

Coverage Report
FileStmtsMissCover
app.py1320100%
config.py10100%
decorators.py270100%
dataholders
   apt.py90100%
   mainpage_get.py150100%
   review.py70100%
   user.py80100%
pages
   login.py370100%
   mainpage.py1000100%
   userpage.py540100%
TOTAL3900100%

Tests Skipped Failures Errors Time
47 0 💤 0 ❌ 0 🔥 1.156s ⏱️

Please sign in to comment.