From 7e3828b06de5e737ac97b814b3d6372654010712 Mon Sep 17 00:00:00 2001 From: SepehrBazyar Date: Wed, 6 Apr 2022 18:32:47 +0430 Subject: [PATCH 01/30] feat: added test package and conftest module --- tests/__init__.py | 0 tests/conftest.py | 0 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 tests/__init__.py create mode 100644 tests/conftest.py diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..e69de29 From 9283e8dd9cfa3ddee1940f100bd11fe7e548622b Mon Sep 17 00:00:00 2001 From: SepehrBazyar Date: Wed, 6 Apr 2022 18:57:24 +0430 Subject: [PATCH 02/30] feat: added dependency package for testing --- core/config.py | 1 + requirements.txt | 14 +++++++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/core/config.py b/core/config.py index 3eec09b..3374f99 100644 --- a/core/config.py +++ b/core/config.py @@ -15,6 +15,7 @@ class Settings(BaseSettings): # Databases POSTGRESQL_URL: str + SQLITE_TEST_URL: str # Media USER_AVATAR_PATH: str diff --git a/requirements.txt b/requirements.txt index 563df35..039f005 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,6 +6,7 @@ argon2-cffi==21.3.0 argon2-cffi-bindings==21.2.0 asgiref==3.5.0 asyncpg==0.25.0 +attrs==21.4.0 bcrypt==3.2.0 certifi==2021.10.8 cffi==1.15.0 @@ -19,11 +20,14 @@ email-validator==1.1.3 fastapi==0.75.0 fastapi-utils==0.2.1 greenlet==1.1.2 -h11==0.13.0 +h11==0.12.0 +httpcore==0.14.7 httptools==0.2.0 +httpx==0.22.0 idna==3.3 importlib-metadata==4.11.3 importlib-resources==5.4.0 +iniconfig==1.1.1 itsdangerous==2.1.1 Jinja2==3.1.0 Mako==1.2.0 @@ -31,22 +35,30 @@ MarkupSafe==2.1.1 orjson==3.6.7 orm==0.3.1 ormar==0.10.25 +packaging==21.3 passlib==1.7.4 +pluggy==1.0.0 psycopg2-binary==2.9.3 +py==1.11.0 pyasn1==0.4.8 pycparser==2.21 pydantic==1.9.0 +pyparsing==3.0.7 +pytest==7.1.1 +pytest-asyncio==0.18.3 python-dotenv==0.19.2 python-jose==3.3.0 python-magic==0.4.25 python-multipart==0.0.5 PyYAML==5.4.1 requests==2.27.1 +rfc3986==1.5.0 rsa==4.8 six==1.16.0 sniffio==1.2.0 SQLAlchemy==1.4.31 starlette==0.17.1 +tomli==2.0.1 typesystem==0.3.1 typing-extensions==4.1.1 ujson==4.3.0 From 8dc2cd7ca22be599c948644e023c9d49bea201a7 Mon Sep 17 00:00:00 2001 From: SepehrBazyar Date: Thu, 7 Apr 2022 01:36:51 +0430 Subject: [PATCH 03/30] refactor: added bool flag to set test environents --- core/config.py | 3 ++- core/versioning.py | 2 +- db/postgresql.py | 4 +++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/core/config.py b/core/config.py index 3374f99..fa62818 100644 --- a/core/config.py +++ b/core/config.py @@ -11,7 +11,8 @@ class Settings(BaseSettings): ALLOWED_HOSTS: str REFRESH_TOKEN_EXPIRE_DAYS: int ACCESS_TOKEN_EXPIRE_MINUTES: int - DEBUG: bool + DEVELOPMENT: bool + TESTING: bool # Databases POSTGRESQL_URL: str diff --git a/core/versioning.py b/core/versioning.py index fda00dc..928aaf4 100644 --- a/core/versioning.py +++ b/core/versioning.py @@ -30,7 +30,7 @@ def __init__( }, } - if settings.DEBUG: + if settings.DEVELOPMENT: self.app = FastAPI(**__kwargs) else: self.app = FastAPI(docs_url=None, redoc_url=None, **__kwargs) diff --git a/db/postgresql.py b/db/postgresql.py index 8d3af23..4e5fad7 100644 --- a/db/postgresql.py +++ b/db/postgresql.py @@ -5,7 +5,9 @@ from core import settings -database = Database(settings.POSTGRESQL_URL) +DATABASE_URL = settings.SQLITE_TEST_URL if settings.TESTING else settings.POSTGRESQL_URL + +database = Database(url=DATABASE_URL) metadata = MetaData() From a2aeed2ab62f464d78e5fc9c9b96e1302e5adb49 Mon Sep 17 00:00:00 2001 From: SepehrBazyar Date: Thu, 7 Apr 2022 01:54:52 +0430 Subject: [PATCH 04/30] feat: add session fixture for creating db --- tests/conftest.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tests/conftest.py b/tests/conftest.py index e69de29..234b576 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -0,0 +1,21 @@ +from pytest import fixture +from sqlalchemy import create_engine +from core import settings +from db import metadata + + +engine = create_engine( + url=settings.SQLITE_TEST_URL, + connect_args={ + "check_same_thread": False, + }, +) + + +@fixture(autouse=True, scope="session") +def create_test_database(): + """Basic SetUp and TearDown for Clearing DataBase""" + + metadata.create_all(bind=engine) + yield + metadata.drop_all(bind=engine) From ba1d26d29b0603ca1978e6a48d96778c1b962483 Mon Sep 17 00:00:00 2001 From: SepehrBazyar Date: Thu, 7 Apr 2022 02:29:06 +0430 Subject: [PATCH 05/30] test: write first test for create new user --- tests/test_user.py | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 tests/test_user.py diff --git a/tests/test_user.py b/tests/test_user.py new file mode 100644 index 0000000..5b5f245 --- /dev/null +++ b/tests/test_user.py @@ -0,0 +1,11 @@ +from pytest import mark +from schemas import UserInDBSchema +from models import User + + +@mark.asyncio +class TestUser: + async def test_create_user(self): + form = UserInDBSchema(mobile="9123456789", password="1234567890") + user = await User.sign_up(form=form) + assert user.id is not None From 190cf32443bcc81cf68aedde3cb67f645be4c970 Mon Sep 17 00:00:00 2001 From: SepehrBazyar Date: Fri, 8 Apr 2022 22:27:03 +0430 Subject: [PATCH 06/30] fix: use async engine for fixture create all --- tests/conftest.py | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 234b576..c461513 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,21 +1,27 @@ from pytest import fixture -from sqlalchemy import create_engine +from sqlalchemy.ext.asyncio import create_async_engine +from asyncio import get_event_loop from core import settings from db import metadata -engine = create_engine( - url=settings.SQLITE_TEST_URL, - connect_args={ - "check_same_thread": False, - }, -) +engine = create_async_engine(settings.SQLITE_TEST_URL, echo=True) @fixture(autouse=True, scope="session") -def create_test_database(): +async def create_test_database(): """Basic SetUp and TearDown for Clearing DataBase""" - metadata.create_all(bind=engine) - yield - metadata.drop_all(bind=engine) + async with engine.begin() as conn: + await conn.run_sync(metadata.create_all) + yield + await conn.run_sync(metadata.drop_all) + + +@fixture(autouse=True, scope="session") +def event_loop(): + """Override the Event Loop Tests for Set Async Fixture Tests""" + + loop = get_event_loop() + yield loop + loop.close() From 24f78e3df1b97572efd40b29d6f52b75dad3aa33 Mon Sep 17 00:00:00 2001 From: SepehrBazyar Date: Sat, 9 Apr 2022 01:09:38 +0430 Subject: [PATCH 07/30] feat: added setup cfg file for capute warnings --- setup.cfg | 5 +++++ tests/conftest.py | 6 +++--- tests/test_user.py | 4 +--- 3 files changed, 9 insertions(+), 6 deletions(-) create mode 100644 setup.cfg diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..6a9fa00 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,5 @@ +[tool:pytest] +asyncio_mode = auto +filterwarnings = + ignore:.*U.*mode is deprecated:DeprecationWarning +addopts = -p no:warnings diff --git a/tests/conftest.py b/tests/conftest.py index c461513..bd79334 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,11 +1,11 @@ -from pytest import fixture -from sqlalchemy.ext.asyncio import create_async_engine +from pytest_asyncio import fixture +from sqlalchemy.ext.asyncio import AsyncEngine, create_async_engine from asyncio import get_event_loop from core import settings from db import metadata -engine = create_async_engine(settings.SQLITE_TEST_URL, echo=True) +engine: AsyncEngine = create_async_engine(settings.SQLITE_TEST_URL, echo=True) @fixture(autouse=True, scope="session") diff --git a/tests/test_user.py b/tests/test_user.py index 5b5f245..c878104 100644 --- a/tests/test_user.py +++ b/tests/test_user.py @@ -1,9 +1,7 @@ -from pytest import mark -from schemas import UserInDBSchema from models import User +from schemas import UserInDBSchema -@mark.asyncio class TestUser: async def test_create_user(self): form = UserInDBSchema(mobile="9123456789", password="1234567890") From de3c600c19b1c5ed0ab017d31eda3ec0c8348c70 Mon Sep 17 00:00:00 2001 From: SepehrBazyar Date: Sat, 9 Apr 2022 01:21:45 +0430 Subject: [PATCH 08/30] refactor: change structure files in tests package --- tests/api/__init__.py | 0 tests/models/__init__.py | 0 tests/{ => models}/test_user.py | 0 3 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 tests/api/__init__.py create mode 100644 tests/models/__init__.py rename tests/{ => models}/test_user.py (100%) diff --git a/tests/api/__init__.py b/tests/api/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/models/__init__.py b/tests/models/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/test_user.py b/tests/models/test_user.py similarity index 100% rename from tests/test_user.py rename to tests/models/test_user.py From 75493adedcd851c1dc3ff2cabb828dfc5ea3c014 Mon Sep 17 00:00:00 2001 From: SepehrBazyar Date: Sat, 9 Apr 2022 13:51:06 +0430 Subject: [PATCH 09/30] test: resume test case user model classes --- tests/models/test_user.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/tests/models/test_user.py b/tests/models/test_user.py index c878104..dd59335 100644 --- a/tests/models/test_user.py +++ b/tests/models/test_user.py @@ -1,9 +1,17 @@ +from uuid import UUID from models import User from schemas import UserInDBSchema -class TestUser: +class TestUserModel: + """Test Cases Class to Methods of User Class Model Entity ORM""" + + __MOBILE, __PASSWORD = "9123456789", "qwerty1234" + async def test_create_user(self): - form = UserInDBSchema(mobile="9123456789", password="1234567890") + form = UserInDBSchema(mobile=self.__MOBILE, password=self.__PASSWORD) user = await User.sign_up(form=form) - assert user.id is not None + + assert user is not None + assert isinstance(user.id, UUID) + assert user.mobile == self.__MOBILE From 1f3f5dc3939cf0d96513cb842d269d2b9227b2ac Mon Sep 17 00:00:00 2001 From: SepehrBazyar Date: Sat, 9 Apr 2022 16:28:15 +0430 Subject: [PATCH 10/30] feat: added new function fixture to clean database --- tests/conftest.py | 14 +++++++++++++- tests/models/test_user.py | 13 +++++++++++-- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index bd79334..bc9f761 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -10,7 +10,7 @@ @fixture(autouse=True, scope="session") async def create_test_database(): - """Basic SetUp and TearDown for Clearing DataBase""" + """SetUp & TearDown Fixture to Create & Delete Database by Metadata""" async with engine.begin() as conn: await conn.run_sync(metadata.create_all) @@ -18,6 +18,18 @@ async def create_test_database(): await conn.run_sync(metadata.drop_all) +@fixture(autouse=True, scope="function") +async def clean_tables_database(): + """Clearing Database Tables to Delete All Rows for Each Test Case""" + + try: + yield + finally: + async with engine.begin() as conn: + for table in reversed(metadata.sorted_tables): + await conn.execute(table.delete()) + + @fixture(autouse=True, scope="session") def event_loop(): """Override the Event Loop Tests for Set Async Fixture Tests""" diff --git a/tests/models/test_user.py b/tests/models/test_user.py index dd59335..5d0cebc 100644 --- a/tests/models/test_user.py +++ b/tests/models/test_user.py @@ -1,4 +1,5 @@ from uuid import UUID +from core import Level from models import User from schemas import UserInDBSchema @@ -8,10 +9,18 @@ class TestUserModel: __MOBILE, __PASSWORD = "9123456789", "qwerty1234" - async def test_create_user(self): + async def test_create_simple_user(self): form = UserInDBSchema(mobile=self.__MOBILE, password=self.__PASSWORD) user = await User.sign_up(form=form) assert user is not None assert isinstance(user.id, UUID) - assert user.mobile == self.__MOBILE + + async def test_create_duplicate_user(self): + form = UserInDBSchema(mobile=self.__MOBILE, password=self.__PASSWORD) + + user1 = await User.sign_up(form=form) + assert user1 is not None + + user2 = await User.sign_up(form=form) + assert user2 is None From 9cca37ab8e052fa38bdd1d4342814ddb8c260236 Mon Sep 17 00:00:00 2001 From: SepehrBazyar Date: Sat, 9 Apr 2022 17:02:44 +0430 Subject: [PATCH 11/30] test: added two test check level and passwords --- tests/models/test_user.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tests/models/test_user.py b/tests/models/test_user.py index 5d0cebc..a7075ab 100644 --- a/tests/models/test_user.py +++ b/tests/models/test_user.py @@ -24,3 +24,21 @@ async def test_create_duplicate_user(self): user2 = await User.sign_up(form=form) assert user2 is None + + async def test_create_admin_user(self): + form = UserInDBSchema( + level=Level.ADMIN, + mobile=self.__MOBILE, + password=self.__PASSWORD, + ) + user = await User.sign_up(form=form) + + assert user.level_ is Level.ADMIN + assert isinstance(user.level, str) + + async def test_login_password_user(self): + form = UserInDBSchema(mobile=self.__MOBILE, password=self.__PASSWORD) + user = await User.sign_up(form=form) + + assert await user.sign_in(password=self.__PASSWORD) is True + assert await user.sign_in(password="wrongpassword") is False From ef834ee41717798dd8c4db4f104d15ffae64b0ac Mon Sep 17 00:00:00 2001 From: SepehrBazyar Date: Sat, 9 Apr 2022 17:54:59 +0430 Subject: [PATCH 12/30] test: mocked save avatar function for test it --- tests/models/test_user.py | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/tests/models/test_user.py b/tests/models/test_user.py index a7075ab..99a98df 100644 --- a/tests/models/test_user.py +++ b/tests/models/test_user.py @@ -1,5 +1,7 @@ +from pytest import MonkeyPatch from uuid import UUID -from core import Level +from pathlib import Path +from core import settings, Level from models import User from schemas import UserInDBSchema @@ -8,6 +10,7 @@ class TestUserModel: """Test Cases Class to Methods of User Class Model Entity ORM""" __MOBILE, __PASSWORD = "9123456789", "qwerty1234" + __AVATAR = "iVBORw0KGgoAAAANSUhEUgAAASIAAAEiAQMAAABncE31AAAABlBMVEXMADPMmQAZ5W44AAAFPklEQVR4nJSawa3kNgyGbfgwR5fgFlJAAL9StoQccwgiBznkuCWkhRQQYKeUV8Ic9zBYBpIlm7Qo+YsPgwf6w7OGI1P8SQ7sCiLP8vdHA9gM9dN5b9ya1M8n9WhTy0kt7Scuz/Nem5pfx7J+dKj3sawO9ZDj2e8eVf4MHWqSvLBRvveod/mnHWqUvLBFXl3qme90qEH2u6PIZ5d678vqUmFf2HKx1dTzsuccak0LGyXe6FLvtKwutUhc2P7Zp57R1KVmEXnFZf24o96POyoCEh/7rqnNUuJQ5poi8U1EbRznmsoTu1T6eumL3lHpO3apIT4sPvbzjkpe7VMhAqJd2KCe1tHetUYXrpUfayrtjD61xN951hvHp/a92qfm+NtMeuP4VHo7bqhH8qfeOD71tNHLvabkg7W7cSIVP5cbao/Nc3/jDOMec26oIT1r6m+cYdij3B31JX32N045Ij8aZ2W6NvW/vrSgeHIe6/qlRU2b+o6/tqjHpvz1W4uaN+X731vUsqnfMbSodVN7IrRekLCp/RVanpVN7dXQ+JVG2dS+D419MSWqvEOhsRMfsqn3sbX350SVdzs03rZlDyQ5ToTG+72WcLPulO+wUM6I/OE7LJ8RORYGP2qOkTrjavDj9JSpHKODfzI8RDYV74MfUuZM5bMj+OEpekmdQ8EPiGuhvhXKc1g0q/OxEfSlUEuhHIelVEOd28E92KZC5RwguEdpSkhUPhHEc9hcqJybBPEcFm9tKs+xCYpyV6JKzhTEc1jIt0r+FdyzVDKVn56oymFjolReGLxMYMrUkWMGL/dIv80fKl8N4jhszlRxS6Iqh0V3yZ8qj45U5bB1p86cPFFXhyXjXyq/T4arw2SnjgXs1KfjLvmqdEeiXo675KvSMIn67rhL/lbZc5DaYfNOKc8k6uKw5C759/rPLw5L7pJ/rl/ncFjrHLR2VzNWdlczVlrS1YyVlnQ1Y6UlXc1YaUlXM1Za0tWMlZZ0NWOlJV3NWGlJVzNWWtLVjJWWdDVjrSU9zThUdk8zDpXd04xDZfc041DZPc04VHZPMw6V3dOMQ2X3NGNt9zRjbfc0Y233NGNtt5oxHC+rtVvNeFLWbjXjhTrsVjOelLVbzWgppSWNZjypi5Y0mtFSSksazagoqyWNZrSU0pJGMyrKakmjGS2ltKTRjIqyWtJoRkNpLWk0o6KsljSa0VBaSxrNqCirJY1mNJTWkkYzKspqSaMZDaXsVjMqympJoxk1ZbWk1oyKumhJrRk1ZbWk1oyaslryQ31qStvp/2LrQt+R+Yv5nv2ObE+w/cX2Ktv37B1i7yN7t1mcYDGHxS8WC1lcRTGaxXt2drBziJ1pXk21trOzlp3bLAdg+QTLTView3Imln+xXI7lhSjHZPkqy31ZHs1ycpbfM63AdAfTMEwPMW3FdBrTfPnR6c3TW6e23GvRvMBku9O1TCMzvc20O6sDsJoCrE+wWgerm7AaDKvnsNoQqzOxmhWsf7FaGqvLsRofqxey2iOrY7KaKKyvslotq/uyGjKrR7PaNquTs5o7rN+zXgDrK7AeBet3sN4J68Owng7sD7FeE+tbsR4Y66ex3hzr87Ge4VBpxtbFepmsL4p6rKxfy3q/rI/MetKsv8165azvznr4bB6AzRawOQU288DmJ9gsBpvrQDMibN6Eza6wOZjmTE09xSP+FA+b9fm/c0NsBonNM7HZKDZnxWa22PwXmyVjc2loxo3Ny7HZOzbHx2YC2Xwhm1Vkc49shpLNY7LZTjYn2p45Rdd/AQAA//9/35AUGS3FfQAAAABJRU5ErkJggg==" # noqa: E501 async def test_create_simple_user(self): form = UserInDBSchema(mobile=self.__MOBILE, password=self.__PASSWORD) @@ -36,6 +39,25 @@ async def test_create_admin_user(self): assert user.level_ is Level.ADMIN assert isinstance(user.level, str) + async def test_create_avatar_user(self, monkeypatch: MonkeyPatch): + get_path = lambda mobile: str(Path(settings.USER_AVATAR_PATH) / f"{mobile}.png") + + async def mock_save_avatar(phone_number: str, avatar: bytes) -> str: + """Mocked Saved Binary Buffer Avatar Method with Returned the Path""" + + return get_path(mobile=phone_number) + + monkeypatch.setattr(target=User, name="save_avatar", value=mock_save_avatar) + + form = UserInDBSchema( + avatar=self.__AVATAR, + mobile=self.__MOBILE, + password=self.__PASSWORD, + ) + user = await User.sign_up(form=form) + + assert user.avatar == get_path(mobile=self.__MOBILE) + async def test_login_password_user(self): form = UserInDBSchema(mobile=self.__MOBILE, password=self.__PASSWORD) user = await User.sign_up(form=form) From f58e898b2a0ad906cab6a6b40f5539cddc083141 Mon Sep 17 00:00:00 2001 From: SepehrBazyar Date: Sat, 9 Apr 2022 18:09:58 +0430 Subject: [PATCH 13/30] test: write test successfull change password --- tests/models/test_user.py | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/tests/models/test_user.py b/tests/models/test_user.py index 99a98df..84f5c03 100644 --- a/tests/models/test_user.py +++ b/tests/models/test_user.py @@ -3,7 +3,10 @@ from pathlib import Path from core import settings, Level from models import User -from schemas import UserInDBSchema +from schemas import ( + UserInDBSchema, + ChangePasswordSchema, +) class TestUserModel: @@ -64,3 +67,19 @@ async def test_login_password_user(self): assert await user.sign_in(password=self.__PASSWORD) is True assert await user.sign_in(password="wrongpassword") is False + + async def test_change_password_successfull_user(self): + form = UserInDBSchema(mobile=self.__MOBILE, password=self.__PASSWORD) + user = await User.sign_up(form=form) + + new_password = "newpassword" + passwords_form = ChangePasswordSchema( + old_password=self.__PASSWORD, + new_password=new_password, + confirm_password=new_password, + ) + result = await user.change_password(passwords=passwords_form) + + assert result is True + assert await user.sign_in(password=new_password) is True + assert await user.sign_in(password=self.__PASSWORD) is False From 153e3bbb2beb21ebc45bf031821ae6de4ac5951b Mon Sep 17 00:00:00 2001 From: SepehrBazyar Date: Sat, 9 Apr 2022 18:12:46 +0430 Subject: [PATCH 14/30] test: write test for fail change passwords --- tests/models/test_user.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/models/test_user.py b/tests/models/test_user.py index 84f5c03..0553bdf 100644 --- a/tests/models/test_user.py +++ b/tests/models/test_user.py @@ -68,6 +68,22 @@ async def test_login_password_user(self): assert await user.sign_in(password=self.__PASSWORD) is True assert await user.sign_in(password="wrongpassword") is False + async def test_change_password_failed_user(self): + form = UserInDBSchema(mobile=self.__MOBILE, password=self.__PASSWORD) + user = await User.sign_up(form=form) + + new_password = "newpassword" + passwords_form = ChangePasswordSchema( + old_password="wrongpassword", + new_password=new_password, + confirm_password=new_password, + ) + result = await user.change_password(passwords=passwords_form) + + assert result is False + assert await user.sign_in(password=new_password) is False + assert await user.sign_in(password=self.__PASSWORD) is True + async def test_change_password_successfull_user(self): form = UserInDBSchema(mobile=self.__MOBILE, password=self.__PASSWORD) user = await User.sign_up(form=form) From 8855013e64ccbce1823d5b22cce49c6726983930 Mon Sep 17 00:00:00 2001 From: SepehrBazyar Date: Sat, 9 Apr 2022 18:33:12 +0430 Subject: [PATCH 15/30] test: writed two test for states of edit model --- tests/models/test_user.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/tests/models/test_user.py b/tests/models/test_user.py index 0553bdf..bade172 100644 --- a/tests/models/test_user.py +++ b/tests/models/test_user.py @@ -5,6 +5,7 @@ from models import User from schemas import ( UserInDBSchema, + UserUpdateSchema, ChangePasswordSchema, ) @@ -67,6 +68,27 @@ async def test_login_password_user(self): assert await user.sign_in(password=self.__PASSWORD) is True assert await user.sign_in(password="wrongpassword") is False + + async def test_edit_successfull_activate_user(self): + form = UserInDBSchema(mobile=self.__MOBILE, password=self.__PASSWORD) + user = await User.sign_up(form=form) + + edited_form = UserUpdateSchema(is_active=False) + result = await user.edit(update_form=edited_form) + + assert result is True + + async def test_edit_failed_phone_number_user(self): + form1 = UserInDBSchema(mobile=self.__MOBILE, password=self.__PASSWORD) + user1 = await User.sign_up(form=form1) + + form2 = UserInDBSchema(mobile="9987654321", password=self.__PASSWORD) + user2 = await User.sign_up(form=form2) + + edited_form = UserUpdateSchema(mobile=self.__MOBILE) + result = await user2.edit(update_form=edited_form) + + assert result is False async def test_change_password_failed_user(self): form = UserInDBSchema(mobile=self.__MOBILE, password=self.__PASSWORD) From 2720247d9d8f5d5dfd7be47648734247a6955c66 Mon Sep 17 00:00:00 2001 From: SepehrBazyar Date: Sat, 9 Apr 2022 18:41:53 +0430 Subject: [PATCH 16/30] refactor: change parent class for constants of user --- tests/models/test_user.py | 56 +++++++++++++++++++++++---------------- 1 file changed, 33 insertions(+), 23 deletions(-) diff --git a/tests/models/test_user.py b/tests/models/test_user.py index bade172..676b968 100644 --- a/tests/models/test_user.py +++ b/tests/models/test_user.py @@ -10,21 +10,31 @@ ) -class TestUserModel: - """Test Cases Class to Methods of User Class Model Entity ORM""" +class UserConstants: + """Constants Parameters of User for Inheritanced in Child Test Case Classes""" + + _MOBILE, _PASSWORD = "9123456789", "qwerty1234" + _AVATAR = "iVBORw0KGgoAAAANSUhEUgAAASIAAAEiAQMAAABncE31AAAABlBMVEXMADPMmQAZ5W44AAAFPklEQVR4nJSawa3kNgyGbfgwR5fgFlJAAL9StoQccwgiBznkuCWkhRQQYKeUV8Ic9zBYBpIlm7Qo+YsPgwf6w7OGI1P8SQ7sCiLP8vdHA9gM9dN5b9ya1M8n9WhTy0kt7Scuz/Nem5pfx7J+dKj3sawO9ZDj2e8eVf4MHWqSvLBRvveod/mnHWqUvLBFXl3qme90qEH2u6PIZ5d678vqUmFf2HKx1dTzsuccak0LGyXe6FLvtKwutUhc2P7Zp57R1KVmEXnFZf24o96POyoCEh/7rqnNUuJQ5poi8U1EbRznmsoTu1T6eumL3lHpO3apIT4sPvbzjkpe7VMhAqJd2KCe1tHetUYXrpUfayrtjD61xN951hvHp/a92qfm+NtMeuP4VHo7bqhH8qfeOD71tNHLvabkg7W7cSIVP5cbao/Nc3/jDOMec26oIT1r6m+cYdij3B31JX32N045Ij8aZ2W6NvW/vrSgeHIe6/qlRU2b+o6/tqjHpvz1W4uaN+X731vUsqnfMbSodVN7IrRekLCp/RVanpVN7dXQ+JVG2dS+D419MSWqvEOhsRMfsqn3sbX350SVdzs03rZlDyQ5ToTG+72WcLPulO+wUM6I/OE7LJ8RORYGP2qOkTrjavDj9JSpHKODfzI8RDYV74MfUuZM5bMj+OEpekmdQ8EPiGuhvhXKc1g0q/OxEfSlUEuhHIelVEOd28E92KZC5RwguEdpSkhUPhHEc9hcqJybBPEcFm9tKs+xCYpyV6JKzhTEc1jIt0r+FdyzVDKVn56oymFjolReGLxMYMrUkWMGL/dIv80fKl8N4jhszlRxS6Iqh0V3yZ8qj45U5bB1p86cPFFXhyXjXyq/T4arw2SnjgXs1KfjLvmqdEeiXo675KvSMIn67rhL/lbZc5DaYfNOKc8k6uKw5C759/rPLw5L7pJ/rl/ncFjrHLR2VzNWdlczVlrS1YyVlnQ1Y6UlXc1YaUlXM1Za0tWMlZZ0NWOlJV3NWGlJVzNWWtLVjJWWdDVjrSU9zThUdk8zDpXd04xDZfc041DZPc04VHZPMw6V3dOMQ2X3NGNt9zRjbfc0Y233NGNtt5oxHC+rtVvNeFLWbjXjhTrsVjOelLVbzWgppSWNZjypi5Y0mtFSSksazagoqyWNZrSU0pJGMyrKakmjGS2ltKTRjIqyWtJoRkNpLWk0o6KsljSa0VBaSxrNqCirJY1mNJTWkkYzKspqSaMZDaXsVjMqympJoxk1ZbWk1oyKumhJrRk1ZbWk1oyaslryQ31qStvp/2LrQt+R+Yv5nv2ObE+w/cX2Ktv37B1i7yN7t1mcYDGHxS8WC1lcRTGaxXt2drBziJ1pXk21trOzlp3bLAdg+QTLTView3Imln+xXI7lhSjHZPkqy31ZHs1ycpbfM63AdAfTMEwPMW3FdBrTfPnR6c3TW6e23GvRvMBku9O1TCMzvc20O6sDsJoCrE+wWgerm7AaDKvnsNoQqzOxmhWsf7FaGqvLsRofqxey2iOrY7KaKKyvslotq/uyGjKrR7PaNquTs5o7rN+zXgDrK7AeBet3sN4J68Owng7sD7FeE+tbsR4Y66ex3hzr87Ge4VBpxtbFepmsL4p6rKxfy3q/rI/MetKsv8165azvznr4bB6AzRawOQU288DmJ9gsBpvrQDMibN6Eza6wOZjmTE09xSP+FA+b9fm/c0NsBonNM7HZKDZnxWa22PwXmyVjc2loxo3Ny7HZOzbHx2YC2Xwhm1Vkc49shpLNY7LZTjYn2p45Rdd/AQAA//9/35AUGS3FfQAAAABJRU5ErkJggg==" # noqa: E501 + + +class TestUserSchema(UserConstants): + """Test Cases Class to Schemas of User Class Model Entity Pydantic""" - __MOBILE, __PASSWORD = "9123456789", "qwerty1234" - __AVATAR = "iVBORw0KGgoAAAANSUhEUgAAASIAAAEiAQMAAABncE31AAAABlBMVEXMADPMmQAZ5W44AAAFPklEQVR4nJSawa3kNgyGbfgwR5fgFlJAAL9StoQccwgiBznkuCWkhRQQYKeUV8Ic9zBYBpIlm7Qo+YsPgwf6w7OGI1P8SQ7sCiLP8vdHA9gM9dN5b9ya1M8n9WhTy0kt7Scuz/Nem5pfx7J+dKj3sawO9ZDj2e8eVf4MHWqSvLBRvveod/mnHWqUvLBFXl3qme90qEH2u6PIZ5d678vqUmFf2HKx1dTzsuccak0LGyXe6FLvtKwutUhc2P7Zp57R1KVmEXnFZf24o96POyoCEh/7rqnNUuJQ5poi8U1EbRznmsoTu1T6eumL3lHpO3apIT4sPvbzjkpe7VMhAqJd2KCe1tHetUYXrpUfayrtjD61xN951hvHp/a92qfm+NtMeuP4VHo7bqhH8qfeOD71tNHLvabkg7W7cSIVP5cbao/Nc3/jDOMec26oIT1r6m+cYdij3B31JX32N045Ij8aZ2W6NvW/vrSgeHIe6/qlRU2b+o6/tqjHpvz1W4uaN+X731vUsqnfMbSodVN7IrRekLCp/RVanpVN7dXQ+JVG2dS+D419MSWqvEOhsRMfsqn3sbX350SVdzs03rZlDyQ5ToTG+72WcLPulO+wUM6I/OE7LJ8RORYGP2qOkTrjavDj9JSpHKODfzI8RDYV74MfUuZM5bMj+OEpekmdQ8EPiGuhvhXKc1g0q/OxEfSlUEuhHIelVEOd28E92KZC5RwguEdpSkhUPhHEc9hcqJybBPEcFm9tKs+xCYpyV6JKzhTEc1jIt0r+FdyzVDKVn56oymFjolReGLxMYMrUkWMGL/dIv80fKl8N4jhszlRxS6Iqh0V3yZ8qj45U5bB1p86cPFFXhyXjXyq/T4arw2SnjgXs1KfjLvmqdEeiXo675KvSMIn67rhL/lbZc5DaYfNOKc8k6uKw5C759/rPLw5L7pJ/rl/ncFjrHLR2VzNWdlczVlrS1YyVlnQ1Y6UlXc1YaUlXM1Za0tWMlZZ0NWOlJV3NWGlJVzNWWtLVjJWWdDVjrSU9zThUdk8zDpXd04xDZfc041DZPc04VHZPMw6V3dOMQ2X3NGNt9zRjbfc0Y233NGNtt5oxHC+rtVvNeFLWbjXjhTrsVjOelLVbzWgppSWNZjypi5Y0mtFSSksazagoqyWNZrSU0pJGMyrKakmjGS2ltKTRjIqyWtJoRkNpLWk0o6KsljSa0VBaSxrNqCirJY1mNJTWkkYzKspqSaMZDaXsVjMqympJoxk1ZbWk1oyKumhJrRk1ZbWk1oyaslryQ31qStvp/2LrQt+R+Yv5nv2ObE+w/cX2Ktv37B1i7yN7t1mcYDGHxS8WC1lcRTGaxXt2drBziJ1pXk21trOzlp3bLAdg+QTLTView3Imln+xXI7lhSjHZPkqy31ZHs1ycpbfM63AdAfTMEwPMW3FdBrTfPnR6c3TW6e23GvRvMBku9O1TCMzvc20O6sDsJoCrE+wWgerm7AaDKvnsNoQqzOxmhWsf7FaGqvLsRofqxey2iOrY7KaKKyvslotq/uyGjKrR7PaNquTs5o7rN+zXgDrK7AeBet3sN4J68Owng7sD7FeE+tbsR4Y66ex3hzr87Ge4VBpxtbFepmsL4p6rKxfy3q/rI/MetKsv8165azvznr4bB6AzRawOQU288DmJ9gsBpvrQDMibN6Eza6wOZjmTE09xSP+FA+b9fm/c0NsBonNM7HZKDZnxWa22PwXmyVjc2loxo3Ny7HZOzbHx2YC2Xwhm1Vkc49shpLNY7LZTjYn2p45Rdd/AQAA//9/35AUGS3FfQAAAABJRU5ErkJggg==" # noqa: E501 + pass + + +class TestUserModel(UserConstants): + """Test Cases Class to Methods of User Class Model Entity ORM""" async def test_create_simple_user(self): - form = UserInDBSchema(mobile=self.__MOBILE, password=self.__PASSWORD) + form = UserInDBSchema(mobile=self._MOBILE, password=self._PASSWORD) user = await User.sign_up(form=form) assert user is not None assert isinstance(user.id, UUID) async def test_create_duplicate_user(self): - form = UserInDBSchema(mobile=self.__MOBILE, password=self.__PASSWORD) + form = UserInDBSchema(mobile=self._MOBILE, password=self._PASSWORD) user1 = await User.sign_up(form=form) assert user1 is not None @@ -35,8 +45,8 @@ async def test_create_duplicate_user(self): async def test_create_admin_user(self): form = UserInDBSchema( level=Level.ADMIN, - mobile=self.__MOBILE, - password=self.__PASSWORD, + mobile=self._MOBILE, + password=self._PASSWORD, ) user = await User.sign_up(form=form) @@ -54,23 +64,23 @@ async def mock_save_avatar(phone_number: str, avatar: bytes) -> str: monkeypatch.setattr(target=User, name="save_avatar", value=mock_save_avatar) form = UserInDBSchema( - avatar=self.__AVATAR, - mobile=self.__MOBILE, - password=self.__PASSWORD, + avatar=self._AVATAR, + mobile=self._MOBILE, + password=self._PASSWORD, ) user = await User.sign_up(form=form) - assert user.avatar == get_path(mobile=self.__MOBILE) + assert user.avatar == get_path(mobile=self._MOBILE) async def test_login_password_user(self): - form = UserInDBSchema(mobile=self.__MOBILE, password=self.__PASSWORD) + form = UserInDBSchema(mobile=self._MOBILE, password=self._PASSWORD) user = await User.sign_up(form=form) - assert await user.sign_in(password=self.__PASSWORD) is True + assert await user.sign_in(password=self._PASSWORD) is True assert await user.sign_in(password="wrongpassword") is False async def test_edit_successfull_activate_user(self): - form = UserInDBSchema(mobile=self.__MOBILE, password=self.__PASSWORD) + form = UserInDBSchema(mobile=self._MOBILE, password=self._PASSWORD) user = await User.sign_up(form=form) edited_form = UserUpdateSchema(is_active=False) @@ -79,19 +89,19 @@ async def test_edit_successfull_activate_user(self): assert result is True async def test_edit_failed_phone_number_user(self): - form1 = UserInDBSchema(mobile=self.__MOBILE, password=self.__PASSWORD) + form1 = UserInDBSchema(mobile=self._MOBILE, password=self._PASSWORD) user1 = await User.sign_up(form=form1) - form2 = UserInDBSchema(mobile="9987654321", password=self.__PASSWORD) + form2 = UserInDBSchema(mobile="9987654321", password=self._PASSWORD) user2 = await User.sign_up(form=form2) - edited_form = UserUpdateSchema(mobile=self.__MOBILE) + edited_form = UserUpdateSchema(mobile=self._MOBILE) result = await user2.edit(update_form=edited_form) assert result is False async def test_change_password_failed_user(self): - form = UserInDBSchema(mobile=self.__MOBILE, password=self.__PASSWORD) + form = UserInDBSchema(mobile=self._MOBILE, password=self._PASSWORD) user = await User.sign_up(form=form) new_password = "newpassword" @@ -104,15 +114,15 @@ async def test_change_password_failed_user(self): assert result is False assert await user.sign_in(password=new_password) is False - assert await user.sign_in(password=self.__PASSWORD) is True + assert await user.sign_in(password=self._PASSWORD) is True async def test_change_password_successfull_user(self): - form = UserInDBSchema(mobile=self.__MOBILE, password=self.__PASSWORD) + form = UserInDBSchema(mobile=self._MOBILE, password=self._PASSWORD) user = await User.sign_up(form=form) new_password = "newpassword" passwords_form = ChangePasswordSchema( - old_password=self.__PASSWORD, + old_password=self._PASSWORD, new_password=new_password, confirm_password=new_password, ) @@ -120,4 +130,4 @@ async def test_change_password_successfull_user(self): assert result is True assert await user.sign_in(password=new_password) is True - assert await user.sign_in(password=self.__PASSWORD) is False + assert await user.sign_in(password=self._PASSWORD) is False From 87636340c63265e458837200052a89fcc7b059a1 Mon Sep 17 00:00:00 2001 From: SepehrBazyar Date: Sat, 9 Apr 2022 19:30:28 +0430 Subject: [PATCH 17/30] test: write new tests for user schemas pydantic --- tests/models/test_user.py | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/tests/models/test_user.py b/tests/models/test_user.py index 676b968..54e3fcd 100644 --- a/tests/models/test_user.py +++ b/tests/models/test_user.py @@ -1,7 +1,8 @@ -from pytest import MonkeyPatch +from pytest import MonkeyPatch, raises +from pydantic import ValidationError from uuid import UUID from pathlib import Path -from core import settings, Level +from core import settings, pwd_context, Level from models import User from schemas import ( UserInDBSchema, @@ -20,7 +21,25 @@ class UserConstants: class TestUserSchema(UserConstants): """Test Cases Class to Schemas of User Class Model Entity Pydantic""" - pass + def test_hashed_password_user(self): + form = UserInDBSchema( + mobile=self._MOBILE, + password=self._PASSWORD, + ) + + assert pwd_context.verify(self._PASSWORD, form.password) + + def test_empty_update_user(self): + with raises(ValidationError): + UserUpdateSchema() + + def test_unmatched_passwords_user(self): + with raises(ValidationError): + ChangePasswordSchema( + old_password="oldpassword", + new_password="newpassword", + confirm_password="unmatchpassword", + ) class TestUserModel(UserConstants): From e5ff3679a8e1271bb4e8a1e8c74cfbe1421298e2 Mon Sep 17 00:00:00 2001 From: SepehrBazyar Date: Sat, 9 Apr 2022 19:41:39 +0430 Subject: [PATCH 18/30] test: writing tests for validation avatar fields --- tests/models/test_user.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/models/test_user.py b/tests/models/test_user.py index 54e3fcd..7bcf354 100644 --- a/tests/models/test_user.py +++ b/tests/models/test_user.py @@ -2,6 +2,7 @@ from pydantic import ValidationError from uuid import UUID from pathlib import Path +from base64 import b64encode from core import settings, pwd_context, Level from models import User from schemas import ( @@ -33,6 +34,17 @@ def test_empty_update_user(self): with raises(ValidationError): UserUpdateSchema() + def test_avatar_invalid_string_user(self): + with raises(ValidationError): + UserUpdateSchema(avatar="invalid_avatar") + + def test_avatar_invalid_encoded_user(self): + with raises(ValidationError): + UserUpdateSchema(avatar=b64encode("invalid_avatar".encode()).decode()) + + def test_avatar_valid_encoded_user(self): + UserUpdateSchema(avatar=self._AVATAR) + def test_unmatched_passwords_user(self): with raises(ValidationError): ChangePasswordSchema( From b91eb83d9b64ffad7e29b5da4a4b85bd5f6d070f Mon Sep 17 00:00:00 2001 From: SepehrBazyar Date: Sat, 9 Apr 2022 20:30:39 +0430 Subject: [PATCH 19/30] test: write first test for check api requests --- tests/api/v1_0_0/__init__.py | 0 tests/api/v1_0_0/test_user.py | 7 +++++++ tests/conftest.py | 11 +++++++++++ 3 files changed, 18 insertions(+) create mode 100644 tests/api/v1_0_0/__init__.py create mode 100644 tests/api/v1_0_0/test_user.py diff --git a/tests/api/v1_0_0/__init__.py b/tests/api/v1_0_0/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/api/v1_0_0/test_user.py b/tests/api/v1_0_0/test_user.py new file mode 100644 index 0000000..0979cde --- /dev/null +++ b/tests/api/v1_0_0/test_user.py @@ -0,0 +1,7 @@ +from httpx import AsyncClient + + +class TestUserRoutes: + async def test_user(self, client: AsyncClient): + response = await client.get("/v1.0.0/user/") + assert response.status_code == 401 diff --git a/tests/conftest.py b/tests/conftest.py index bc9f761..9213f38 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,8 +1,11 @@ +from httpx import AsyncClient from pytest_asyncio import fixture from sqlalchemy.ext.asyncio import AsyncEngine, create_async_engine from asyncio import get_event_loop +from typing import Generator from core import settings from db import metadata +from main import app engine: AsyncEngine = create_async_engine(settings.SQLITE_TEST_URL, echo=True) @@ -30,6 +33,14 @@ async def clean_tables_database(): await conn.execute(table.delete()) +@fixture(scope="session") +async def client() -> Generator[AsyncClient, None, None]: + """Session Generator Fixture to Yielding Async Client for Requesting APIs""" + + async with AsyncClient(app=app, base_url=settings.BASE_URL) as client: + yield client + + @fixture(autouse=True, scope="session") def event_loop(): """Override the Event Loop Tests for Set Async Fixture Tests""" From 92b3a03c2a8f092734c256cf042cbe7fe4711c88 Mon Sep 17 00:00:00 2001 From: SepehrBazyar Date: Sat, 9 Apr 2022 21:50:09 +0430 Subject: [PATCH 20/30] fix: write function fixture to get jwt token --- tests/api/v1_0_0/test_user.py | 14 ++++++++++--- tests/conftest.py | 39 +++++++++++++++++++++++++++++++++-- 2 files changed, 48 insertions(+), 5 deletions(-) diff --git a/tests/api/v1_0_0/test_user.py b/tests/api/v1_0_0/test_user.py index 0979cde..879401c 100644 --- a/tests/api/v1_0_0/test_user.py +++ b/tests/api/v1_0_0/test_user.py @@ -1,7 +1,15 @@ from httpx import AsyncClient +from typing import Dict +from ...conftest import VERSIONS + + +URL_STR = VERSIONS.get("1.0.0") + "user/" class TestUserRoutes: - async def test_user(self, client: AsyncClient): - response = await client.get("/v1.0.0/user/") - assert response.status_code == 401 + async def test_get_user( + self, + client: AsyncClient, + admin_token_headers: Dict[str, str], + ): + response = await client.get(URL_STR, headers=admin_token_headers) diff --git a/tests/conftest.py b/tests/conftest.py index 9213f38..b5d254f 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -2,14 +2,36 @@ from pytest_asyncio import fixture from sqlalchemy.ext.asyncio import AsyncEngine, create_async_engine from asyncio import get_event_loop -from typing import Generator -from core import settings +from typing import Dict, Generator +from core import settings, Level from db import metadata +from models import User +from schemas import UserInDBSchema from main import app engine: AsyncEngine = create_async_engine(settings.SQLITE_TEST_URL, echo=True) +FIRST_ADMIN = { + "username": "9928917653", + "password": "sepehrbazyar", +} + +VERSIONS = { + "1.0.0": "/v1.0.0/", +} + + +async def insert_first_admin(): + """Inserting First Admin User to Access Protected Routes View""" + + form = UserInDBSchema( + level=Level.ADMIN, + mobile=FIRST_ADMIN.get("username"), + password=FIRST_ADMIN.get("password"), + ) + await User.sign_up(form=form) + @fixture(autouse=True, scope="session") async def create_test_database(): @@ -26,6 +48,7 @@ async def clean_tables_database(): """Clearing Database Tables to Delete All Rows for Each Test Case""" try: + await insert_first_admin() yield finally: async with engine.begin() as conn: @@ -41,6 +64,18 @@ async def client() -> Generator[AsyncClient, None, None]: yield client +@fixture(scope="session") +async def admin_token_headers(client: AsyncClient) -> Dict[str, str]: + """PASS""" + + await insert_first_admin() + response = await client.post(f"/user/login/", data=FIRST_ADMIN) + tokens: Dict[str, str] = response.json() + return { + "Authorization": f"Bearer {tokens.get('access_token')}", + } + + @fixture(autouse=True, scope="session") def event_loop(): """Override the Event Loop Tests for Set Async Fixture Tests""" From f9c2cdb0c10cacdf06fe8d2a1ee191ea8a53328e Mon Sep 17 00:00:00 2001 From: SepehrBazyar Date: Sun, 10 Apr 2022 08:58:16 +0430 Subject: [PATCH 21/30] refactor: change first admin user for authorize --- tests/conftest.py | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index b5d254f..18ff4fa 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -18,21 +18,12 @@ } VERSIONS = { - "1.0.0": "/v1.0.0/", + version: f"/v{version}/" for version in [ + "1.0.0", + ] } -async def insert_first_admin(): - """Inserting First Admin User to Access Protected Routes View""" - - form = UserInDBSchema( - level=Level.ADMIN, - mobile=FIRST_ADMIN.get("username"), - password=FIRST_ADMIN.get("password"), - ) - await User.sign_up(form=form) - - @fixture(autouse=True, scope="session") async def create_test_database(): """SetUp & TearDown Fixture to Create & Delete Database by Metadata""" @@ -48,7 +39,6 @@ async def clean_tables_database(): """Clearing Database Tables to Delete All Rows for Each Test Case""" try: - await insert_first_admin() yield finally: async with engine.begin() as conn: @@ -64,11 +54,22 @@ async def client() -> Generator[AsyncClient, None, None]: yield client +@fixture(autouse=True, scope="function") +async def insert_first_admin(): + """Fixture to Inserting First Admin User for Access Protected Routes View""" + + form = UserInDBSchema( + level=Level.ADMIN, + mobile=FIRST_ADMIN.get("username"), + password=FIRST_ADMIN.get("password"), + ) + await User.sign_up(form=form) + + @fixture(scope="session") async def admin_token_headers(client: AsyncClient) -> Dict[str, str]: - """PASS""" + """Sent Authorize Data to Login Super Admin Get Access Token""" - await insert_first_admin() response = await client.post(f"/user/login/", data=FIRST_ADMIN) tokens: Dict[str, str] = response.json() return { From dac48bc8e226c615df4e8395b9ca8674f1e40dbd Mon Sep 17 00:00:00 2001 From: SepehrBazyar Date: Sun, 10 Apr 2022 09:39:13 +0430 Subject: [PATCH 22/30] test: complete first api route test get user list --- tests/api/v1_0_0/test_user.py | 21 +++++++++++++++++---- tests/conftest.py | 2 +- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/tests/api/v1_0_0/test_user.py b/tests/api/v1_0_0/test_user.py index 879401c..95399c7 100644 --- a/tests/api/v1_0_0/test_user.py +++ b/tests/api/v1_0_0/test_user.py @@ -1,15 +1,28 @@ +from fastapi import status from httpx import AsyncClient -from typing import Dict -from ...conftest import VERSIONS +from typing import Dict, Any +from core import Level +from ...conftest import VERSIONS, FIRST_ADMIN -URL_STR = VERSIONS.get("1.0.0") + "user/" +URL_STR: str = VERSIONS.get("1.0.0") + "user/" class TestUserRoutes: - async def test_get_user( + async def test_list_user( self, client: AsyncClient, admin_token_headers: Dict[str, str], ): response = await client.get(URL_STR, headers=admin_token_headers) + content: Dict[str, Any] = response.json() + + assert response.status_code == status.HTTP_200_OK + + assert content["count"] == 1 + assert content["next"] is None + assert content["previous"] is None + + assert content["results"][0]["mobile"] == FIRST_ADMIN.get("username") + assert content["results"][0]["level"] == Level.ADMIN.value + assert content["results"][0]["is_active"] is True diff --git a/tests/conftest.py b/tests/conftest.py index 18ff4fa..6ed9709 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -66,7 +66,7 @@ async def insert_first_admin(): await User.sign_up(form=form) -@fixture(scope="session") +@fixture(scope="function") async def admin_token_headers(client: AsyncClient) -> Dict[str, str]: """Sent Authorize Data to Login Super Admin Get Access Token""" From fac696b7eab4423aad8f5640b9dab7c814f146a1 Mon Sep 17 00:00:00 2001 From: SepehrBazyar Date: Sun, 10 Apr 2022 09:43:51 +0430 Subject: [PATCH 23/30] test: write create user test but not passed yet --- tests/api/v1_0_0/test_user.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tests/api/v1_0_0/test_user.py b/tests/api/v1_0_0/test_user.py index 95399c7..8aa1ca1 100644 --- a/tests/api/v1_0_0/test_user.py +++ b/tests/api/v1_0_0/test_user.py @@ -26,3 +26,18 @@ async def test_list_user( assert content["results"][0]["mobile"] == FIRST_ADMIN.get("username") assert content["results"][0]["level"] == Level.ADMIN.value assert content["results"][0]["is_active"] is True + + async def test_create_user( + self, + client: AsyncClient, + admin_token_headers: Dict[str, str], + ): + response = await client.post( + url=URL_STR, + headers=admin_token_headers, + data={ + "mobile": "9123456789", + "password": "secretpassword", + }, + ) + assert response.status_code == status.HTTP_201_CREATED From 7054a57d39d5c710e21ac10f3d0c7207460797f9 Mon Sep 17 00:00:00 2001 From: SepehrBazyar Date: Mon, 11 Apr 2022 09:36:32 +0430 Subject: [PATCH 24/30] fix: sent key value in json arguments tests post --- tests/api/v1_0_0/test_user.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/api/v1_0_0/test_user.py b/tests/api/v1_0_0/test_user.py index 8aa1ca1..f11d8e7 100644 --- a/tests/api/v1_0_0/test_user.py +++ b/tests/api/v1_0_0/test_user.py @@ -35,9 +35,10 @@ async def test_create_user( response = await client.post( url=URL_STR, headers=admin_token_headers, - data={ + json={ "mobile": "9123456789", "password": "secretpassword", }, ) + assert response.status_code == status.HTTP_201_CREATED From 364a2f313bc6901e9fd128677b1008c3fe2bb003 Mon Sep 17 00:00:00 2001 From: SepehrBazyar Date: Mon, 11 Apr 2022 09:42:25 +0430 Subject: [PATCH 25/30] refactor: set db name and force roll back for tests --- db/postgresql.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/db/postgresql.py b/db/postgresql.py index 4e5fad7..e318f93 100644 --- a/db/postgresql.py +++ b/db/postgresql.py @@ -5,10 +5,13 @@ from core import settings -DATABASE_URL = settings.SQLITE_TEST_URL if settings.TESTING else settings.POSTGRESQL_URL - -database = Database(url=DATABASE_URL) metadata = MetaData() +if not settings.TESTING: + DB_NAME = "PostgreSQL" + database = Database(url=settings.POSTGRESQL_URL) +else: + DB_NAME = "SQLite" + database = Database(url=settings.SQLITE_TEST_URL, force_rollback=True) class MainMeta(ModelMeta): @@ -25,13 +28,13 @@ async def connect_to_postgresql(): try: await database.execute("SELECT 1") except Exception as e: - logger.error(f"PostgreSQL Connection Failed {e}.") + logger.error(f"{DB_NAME} Connection Failed {e}.") else: - logger.info("PostgreSQL Connected.") + logger.info(f"{DB_NAME} Connected.") async def close_postgresql_connection(): """Shutdown Event Handler for Disconnect to PostgreSQL Database""" await database.disconnect() - logger.info("PostgreSQL Closed.") + logger.info(f"{DB_NAME} Closed.") From 33c73c5b4f5d1af32f0326cb0a26089dfb5ad5f4 Mon Sep 17 00:00:00 2001 From: SepehrBazyar Date: Mon, 11 Apr 2022 09:46:52 +0430 Subject: [PATCH 26/30] feat: mounted static media files for avatars --- main.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/main.py b/main.py index 1b3a2c8..9299670 100644 --- a/main.py +++ b/main.py @@ -1,4 +1,5 @@ import uvicorn +from fastapi.staticfiles import StaticFiles from core import VersionFastAPI from db import ( connect_to_postgresql, @@ -23,5 +24,10 @@ for version in versions: app.mount(f"/v{version.version}", version) + +# Mount Media Static Files +app.mount("/media", StaticFiles(directory="media"), name="media") + + if __name__ == "__main__": uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True, log_level="debug") From 3c60838b45464510e01305f0de01dce6005a2795 Mon Sep 17 00:00:00 2001 From: SepehrBazyar Date: Tue, 12 Apr 2022 10:30:09 +0430 Subject: [PATCH 27/30] refactor: remove db name for event handlers --- db/postgresql.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/db/postgresql.py b/db/postgresql.py index e318f93..e9f34d1 100644 --- a/db/postgresql.py +++ b/db/postgresql.py @@ -7,10 +7,8 @@ metadata = MetaData() if not settings.TESTING: - DB_NAME = "PostgreSQL" database = Database(url=settings.POSTGRESQL_URL) else: - DB_NAME = "SQLite" database = Database(url=settings.SQLITE_TEST_URL, force_rollback=True) @@ -28,13 +26,13 @@ async def connect_to_postgresql(): try: await database.execute("SELECT 1") except Exception as e: - logger.error(f"{DB_NAME} Connection Failed {e}.") + logger.error(f"PostgreSQL Connection Failed {e}.") else: - logger.info(f"{DB_NAME} Connected.") + logger.info(f"PostgreSQL Connected.") async def close_postgresql_connection(): """Shutdown Event Handler for Disconnect to PostgreSQL Database""" await database.disconnect() - logger.info(f"{DB_NAME} Closed.") + logger.info(f"PostgreSQL Closed.") From c673e0f115b93dfd8e59b8bd82a1ec3cdff6e884 Mon Sep 17 00:00:00 2001 From: SepehrBazyar Date: Tue, 12 Apr 2022 10:31:23 +0430 Subject: [PATCH 28/30] Revert "feat: mounted static media files for avatars" This reverts commit 33c73c5b4f5d1af32f0326cb0a26089dfb5ad5f4. --- main.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/main.py b/main.py index 9299670..1b3a2c8 100644 --- a/main.py +++ b/main.py @@ -1,5 +1,4 @@ import uvicorn -from fastapi.staticfiles import StaticFiles from core import VersionFastAPI from db import ( connect_to_postgresql, @@ -24,10 +23,5 @@ for version in versions: app.mount(f"/v{version.version}", version) - -# Mount Media Static Files -app.mount("/media", StaticFiles(directory="media"), name="media") - - if __name__ == "__main__": uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True, log_level="debug") From cfea27f90810cf0c702b5aabc6c8809243198317 Mon Sep 17 00:00:00 2001 From: SepehrBazyar Date: Fri, 22 Apr 2022 17:21:07 +0430 Subject: [PATCH 29/30] test: write a new tests for login user send datas --- tests/api/v1_0_0/test_user.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/api/v1_0_0/test_user.py b/tests/api/v1_0_0/test_user.py index f11d8e7..69bd6ac 100644 --- a/tests/api/v1_0_0/test_user.py +++ b/tests/api/v1_0_0/test_user.py @@ -9,6 +9,20 @@ class TestUserRoutes: + """Test Cases Class for Test APIs of User Entity Model Routes""" + + async def test_login_successfull_user( + self, + client: AsyncClient, + ): + response = await client.post(url=URL_STR + "login/", data=FIRST_ADMIN) + content: Dict[str, Any] = response.json() + + assert response.status_code == status.HTTP_200_OK + assert "token_type" in content + assert "access_token" in content + assert "refresh_token" in content + async def test_list_user( self, client: AsyncClient, From f8f604e8829e3b93f9211cfbc94be9bfc2905b96 Mon Sep 17 00:00:00 2001 From: SepehrBazyar Date: Fri, 22 Apr 2022 17:33:49 +0430 Subject: [PATCH 30/30] test: write tests for check refresh tokens --- tests/api/v1_0_0/test_user.py | 69 ++++++++++++++++++++++++++++++++++- 1 file changed, 67 insertions(+), 2 deletions(-) diff --git a/tests/api/v1_0_0/test_user.py b/tests/api/v1_0_0/test_user.py index 69bd6ac..4fba175 100644 --- a/tests/api/v1_0_0/test_user.py +++ b/tests/api/v1_0_0/test_user.py @@ -11,28 +11,93 @@ class TestUserRoutes: """Test Cases Class for Test APIs of User Entity Model Routes""" + async def test_login_failed_username_user( + self, + client: AsyncClient, + ): + response = await client.post( + url=URL_STR + "login/", + data={ + "username": "wrongusername", + "password": FIRST_ADMIN.get("password"), + }, + ) + + assert response.status_code == status.HTTP_400_BAD_REQUEST + + async def test_login_failed_password_user( + self, + client: AsyncClient, + ): + response = await client.post( + url=URL_STR + "login/", + data={ + "username": FIRST_ADMIN.get("username"), + "password": "wrongpassword", + }, + ) + + assert response.status_code == status.HTTP_400_BAD_REQUEST + async def test_login_successfull_user( self, client: AsyncClient, ): response = await client.post(url=URL_STR + "login/", data=FIRST_ADMIN) - content: Dict[str, Any] = response.json() assert response.status_code == status.HTTP_200_OK + + content: Dict[str, Any] = response.json() + assert "token_type" in content assert "access_token" in content assert "refresh_token" in content + async def test_refresh_failed_user( + self, + client: AsyncClient, + ): + response = await client.post( + url=URL_STR + "refresh/", + json={ + "refresh": "fakerefreshtoken", + }, + ) + + assert response.status_code == status.HTTP_400_BAD_REQUEST + + async def test_refresh_successfull_user( + self, + client: AsyncClient, + ): + response = await client.post(url=URL_STR + "login/", data=FIRST_ADMIN) + tokens: Dict[str, Any] = response.json() + + response = await client.post( + url=URL_STR + "refresh/", + json={ + "refresh": tokens.get("refresh_token"), + }, + ) + + assert response.status_code == status.HTTP_200_OK + + content: Dict[str, Any] = response.json() + + assert "token_type" in content + assert "access_token" in content + async def test_list_user( self, client: AsyncClient, admin_token_headers: Dict[str, str], ): response = await client.get(URL_STR, headers=admin_token_headers) - content: Dict[str, Any] = response.json() assert response.status_code == status.HTTP_200_OK + content: Dict[str, Any] = response.json() + assert content["count"] == 1 assert content["next"] is None assert content["previous"] is None