Ved Gupta commited on
Commit
15ea62b
·
0 Parent(s):

initial commit

Browse files
.gitignore ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ __pycache__/
2
+ *.pyc
3
+ *.pyo
4
+ *.egg-info/
5
+ dist/
6
+ build/
7
+ .env
8
+ .idea/
9
+ .vscode/
10
+ *.log
11
+ *.swp
12
+ .DS_Store
Dockerfile ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM tiangolo/uvicorn-gunicorn-fastapi:python3.8
2
+
3
+ COPY ./app /app/app
4
+
5
+ COPY ./requirements.txt /app
6
+
7
+ RUN pip install --no-cache-dir -r /app/requirements.txt
8
+
9
+ EXPOSE 80
10
+
11
+ CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80"]
Pipfile ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [[source]]
2
+ url = "https://pypi.org/simple"
3
+ verify_ssl = true
4
+ name = "pypi"
5
+
6
+ [packages]
7
+ fastapi = "*"
8
+ uvicorn = {extras = ["standard"], version = "*"}
9
+ python-dotenv = "*"
10
+ sqlalchemy = "*"
11
+ psycopg2-binary = "*"
12
+ pytest = "*"
13
+ pytest-cov = "*"
14
+ faker = "*"
15
+ requests-mock = "*"
16
+
17
+ [dev-packages]
18
+
19
+ [requires]
20
+ python_version = "3.9"
README.md ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # my-fastapi-project
2
+
3
+ This is a production level project structure for a Python FastAPI project.
4
+
5
+ ## Project Structure
6
+
7
+ ```
8
+ my-fastapi-project
9
+ ├── app
10
+ │ ├── __init__.py
11
+ │ ├── api
12
+ │ │ ├── __init__.py
13
+ │ │ ├── endpoints
14
+ │ │ │ ├── __init__.py
15
+ │ │ │ ├── items.py
16
+ │ │ │ └── users.py
17
+ │ │ └── models
18
+ │ │ ├── __init__.py
19
+ │ │ ├── item.py
20
+ │ │ └── user.py
21
+ │ ├── core
22
+ │ │ ├── __init__.py
23
+ │ │ ├── config.py
24
+ │ │ ├── security.py
25
+ │ │ └── database.py
26
+ │ ├── tests
27
+ │ │ ├── __init__.py
28
+ │ │ ├── conftest.py
29
+ │ │ ├── test_api
30
+ │ │ │ ├── __init__.py
31
+ │ │ │ ├── test_items.py
32
+ │ │ │ └── test_users.py
33
+ │ │ └── test_core
34
+ │ │ ├── __init__.py
35
+ │ │ ├── test_config.py
36
+ │ │ ├── test_security.py
37
+ │ │ └── test_database.py
38
+ │ └── main.py
39
+ ├── .env
40
+ ├── .gitignore
41
+ ├── Dockerfile
42
+ ├── requirements.txt
43
+ ├── README.md
44
+ └── .vscode
45
+ ├── settings.json
46
+ └── launch.json
47
+ ```
48
+
49
+ ## Description
50
+
51
+ The project structure is organized as follows:
52
+
53
+ - `app`: contains the main application code.
54
+ - `app/api`: contains the API endpoints.
55
+ - `app/api/endpoints`: contains the endpoint functions.
56
+ - `app/api/models`: contains the data models.
57
+ - `app/core`: contains the core application code.
58
+ - `app/core/config.py`: contains the application configuration.
59
+ - `app/core/security.py`: contains the security functions.
60
+ - `app/core/database.py`: contains the database connection code.
61
+ - `app/tests`: contains the test code.
62
+ - `app/tests/test_api`: contains the API endpoint tests.
63
+ - `app/tests/test_core`: contains the core application tests.
64
+ - `app/main.py`: contains the main application entry point.
65
+ - `.env`: contains environment variables.
66
+ - `.gitignore`: specifies files and directories to ignore in Git.
67
+ - `Dockerfile`: specifies the Docker image configuration.
68
+ - `requirements.txt`: specifies the Python dependencies.
69
+ - `README.md`: this file.
70
+ - `.vscode`: contains Visual Studio Code configuration files.
app/__init__.py ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI
2
+
3
+ from app.api.api import api_router
4
+ from app.core.config import settings
5
+
6
+
7
+ def create_app() -> FastAPI:
8
+ app = FastAPI(title=settings.PROJECT_NAME, version=settings.PROJECT_VERSION)
9
+ app.include_router(api_router, prefix=settings.API_V1_STR)
10
+ return app
11
+
12
+
13
+ app = create_app()
app/api/__init__.py ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ # File: my-fastapi-project/app/api/__init__.py
2
+
3
+ from fastapi import FastAPI
4
+ from .endpoints import items, users
5
+ from .models import item, user
6
+
7
+ app = FastAPI()
8
+
9
+ app.include_router(items.router)
10
+ app.include_router(users.router)
app/api/endpoints/__init__.py ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ # File: my-fastapi-project/app/api/endpoints/__init__.py
2
+
3
+ from fastapi import APIRouter
4
+
5
+ from . import items, users
6
+
7
+ router = APIRouter()
8
+
9
+ router.include_router(items.router, prefix="/items", tags=["items"])
10
+ router.include_router(users.router, prefix="/users", tags=["users"])
app/api/endpoints/items.py ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import APIRouter
2
+
3
+ router = APIRouter()
4
+
5
+
6
+ @router.get("/")
7
+ async def read_items():
8
+ return [{"name": "Item Foo"}, {"name": "item Bar"}]
9
+
10
+
11
+ @router.get("/{item_id}")
12
+ async def read_item(item_id: int, q: str = None):
13
+ return {"item_id": item_id, "q": q}
app/api/endpoints/users.py ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import APIRouter
2
+
3
+ from app.api.models.user import UserInDB
4
+ from app.api.db import database
5
+
6
+ users_router = r = APIRouter()
7
+
8
+
9
+ @r.post("/", response_model=UserInDB, status_code=201)
10
+ async def create_user(user: UserInDB):
11
+ query = UserInDB.insert().values(
12
+ username=user.username,
13
+ email=user.email,
14
+ hashed_password=user.hashed_password
15
+ )
16
+ user_id = await database.execute(query)
17
+ return {**user.dict(), "id": user_id}
18
+
19
+
20
+ @r.get("/{id}/", response_model=UserInDB)
21
+ async def read_user(id: int):
22
+ query = UserInDB.select().where(UserInDB.c.id == id)
23
+ user = await database.fetch_one(query)
24
+ return user
25
+
26
+
27
+ @r.put("/{id}/", response_model=UserInDB)
28
+ async def update_user(id: int, user: UserInDB):
29
+ query = (
30
+ UserInDB
31
+ .update()
32
+ .where(UserInDB.c.id == id)
33
+ .values(
34
+ username=user.username,
35
+ email=user.email,
36
+ hashed_password=user.hashed_password
37
+ )
38
+ .returning(UserInDB.c.id)
39
+ )
40
+ user_id = await database.execute(query)
41
+ return {**user.dict(), "id": user_id}
42
+
43
+
44
+ @r.delete("/{id}/", response_model=int)
45
+ async def delete_user(id: int):
46
+ query = UserInDB.delete().where(UserInDB.c.id == id)
47
+ user_id = await database.execute(query)
48
+ return user_id
app/api/models/__init__.py ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ from .item import Item
2
+ from .user import User
app/api/models/item.py ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Optional
2
+ from pydantic import BaseModel
3
+
4
+
5
+ class ItemBase(BaseModel):
6
+ title: str
7
+ description: Optional[str] = None
8
+
9
+
10
+ class ItemCreate(ItemBase):
11
+ pass
12
+
13
+
14
+ class Item(ItemBase):
15
+ id: int
16
+ owner_id: int
17
+
18
+ class Config:
19
+ orm_mode = True
app/api/models/user.py ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from pydantic import BaseModel
2
+
3
+ class UserBase(BaseModel):
4
+ email: str
5
+
6
+ class UserCreate(UserBase):
7
+ password: str
8
+
9
+ class User(UserBase):
10
+ id: int
11
+ is_active: bool
12
+
13
+ class Config:
14
+ orm_mode = True
app/core/__init__.py ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ from .config import settings
2
+ from .database import Base, engine, SessionLocal
3
+ from .security import get_password_hash, verify_password
4
+
5
+ __all__ = ["settings", "Base", "engine", "SessionLocal", "get_password_hash", "verify_password"]
app/core/config.py ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Any, Dict, List, Optional, Union
2
+ from pydantic import AnyHttpUrl, BaseSettings, validator
3
+
4
+
5
+ class Settings(BaseSettings):
6
+ API_V1_STR: str = "/api/v1"
7
+ PROJECT_NAME: str = "Whisper API"
8
+ SECRET_KEY: str
9
+ ACCESS_TOKEN_EXPIRE_MINUTES: int = 60
10
+ SERVER_NAME: str
11
+ SERVER_HOST: AnyHttpUrl
12
+ POSTGRES_SERVER: str
13
+ POSTGRES_USER: str
14
+ POSTGRES_PASSWORD: str
15
+ POSTGRES_DB: str
16
+
17
+ @validator("SECRET_KEY", pre=True)
18
+ def secret_key_must_be_set(cls, v: Optional[str], values: Dict[str, Any]) -> str:
19
+ if not v:
20
+ raise ValueError("SECRET_KEY must be set")
21
+ return v
22
+
23
+ @validator("SERVER_NAME", pre=True)
24
+ def server_name_must_be_set(cls, v: Optional[str], values: Dict[str, Any]) -> str:
25
+ if not v:
26
+ raise ValueError("SERVER_NAME must be set")
27
+ return v
28
+
29
+ @validator("SERVER_HOST", pre=True)
30
+ def server_host_must_be_set(cls, v: Optional[str], values: Dict[str, Any]) -> AnyHttpUrl:
31
+ if not v:
32
+ raise ValueError("SERVER_HOST must be set")
33
+ return v
34
+
35
+ @validator("POSTGRES_SERVER", pre=True)
36
+ def postgres_server_must_be_set(cls, v: Optional[str], values: Dict[str, Any]) -> str:
37
+ if not v:
38
+ raise ValueError("POSTGRES_SERVER must be set")
39
+ return v
40
+
41
+ @validator("POSTGRES_USER", pre=True)
42
+ def postgres_user_must_be_set(cls, v: Optional[str], values: Dict[str, Any]) -> str:
43
+ if not v:
44
+ raise ValueError("POSTGRES_USER must be set")
45
+ return v
46
+
47
+ @validator("POSTGRES_PASSWORD", pre=True)
48
+ def postgres_password_must_be_set(cls, v: Optional[str], values: Dict[str, Any]) -> str:
49
+ if not v:
50
+ raise ValueError("POSTGRES_PASSWORD must be set")
51
+ return v
52
+
53
+ @validator("POSTGRES_DB", pre=True)
54
+ def postgres_db_must_be_set(cls, v: Optional[str], values: Dict[str, Any]) -> str:
55
+ if not v:
56
+ raise ValueError("POSTGRES_DB must be set")
57
+ return v
58
+
59
+
60
+ settings = Settings()
app/core/database.py ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sqlalchemy import create_engine
2
+ from sqlalchemy.ext.declarative import declarative_base
3
+ from sqlalchemy.orm import sessionmaker
4
+
5
+ from app.core.config import settings
6
+
7
+ SQLALCHEMY_DATABASE_URL = settings.database_url
8
+
9
+ engine = create_engine(SQLALCHEMY_DATABASE_URL)
10
+
11
+ SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
12
+
13
+ Base = declarative_base()
app/core/security.py ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ from passlib.context import CryptContext
2
+
3
+ pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
4
+
5
+ ALGORITHM = "HS256"
6
+ ACCESS_TOKEN_EXPIRE_MINUTES = 30
7
+ SECRET_KEY = "mysecretkey"
app/main.py ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI
2
+ from app.api.api_v1.api import router as api_router
3
+ from app.core.config import settings
4
+ from app.core.errors import http_error_handler
5
+ from app.core.errors import http422_error_handler
6
+ from fastapi.middleware.cors import CORSMiddleware
7
+
8
+ app = FastAPI(title=settings.PROJECT_NAME, openapi_url=f"{settings.API_V1_STR}/openapi.json")
9
+
10
+ # Set all CORS enabled origins
11
+ if settings.BACKEND_CORS_ORIGINS:
12
+ app.add_middleware(
13
+ CORSMiddleware,
14
+ allow_origins=[str(origin) for origin in settings.BACKEND_CORS_ORIGINS],
15
+ allow_credentials=True,
16
+ allow_methods=["*"],
17
+ allow_headers=["*"],
18
+ )
19
+
20
+ # Include routers
21
+ app.include_router(api_router, prefix=settings.API_V1_STR)
22
+
23
+ # Error handlers
24
+ app.add_exception_handler(422, http422_error_handler)
25
+ app.add_exception_handler(500, http_error_handler)
app/tests/__init__.py ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # File: my-fastapi-project/app/tests/__init__.py
2
+
3
+ # Import necessary modules
4
+ import pytest
5
+ from fastapi.testclient import TestClient
6
+ from sqlalchemy import create_engine
7
+ from sqlalchemy.orm import sessionmaker
8
+
9
+ from app.core.config import settings
10
+ from app.main import app
11
+ from app.tests.utils.utils import override_get_db
12
+
13
+ # Create test database
14
+ SQLALCHEMY_DATABASE_URL = settings.TEST_DATABASE_URL
15
+ engine = create_engine(SQLALCHEMY_DATABASE_URL)
16
+ TestingSessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
17
+
18
+ # Define test client
19
+ @pytest.fixture(scope="module")
20
+ def test_client():
21
+ with TestClient(app) as client:
22
+ yield client
23
+
24
+ # Define test database
25
+ @pytest.fixture(scope="module")
26
+ def test_db():
27
+ db = TestingSessionLocal()
28
+ yield db
29
+ db.close()
30
+
31
+ # Override get_db function for testing
32
+ @pytest.fixture(autouse=True)
33
+ def override_get_db(monkeypatch):
34
+ monkeypatch.setattr("app.api.dependencies.get_db", override_get_db)
app/tests/conftest.py ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pytest
2
+ from fastapi.testclient import TestClient
3
+ from sqlalchemy import create_engine
4
+ from sqlalchemy.orm import sessionmaker
5
+
6
+ from app.core.config import settings
7
+ from app.db.base import Base
8
+ from app.db.session import SessionLocal
9
+
10
+
11
+ @pytest.fixture(scope="module")
12
+ def test_app():
13
+ # set up test app with client
14
+ from app.main import app
15
+ client = TestClient(app)
16
+ # set up test database
17
+ SQLALCHEMY_DATABASE_URL = settings.SQLALCHEMY_DATABASE_TEST_URL
18
+ engine = create_engine(SQLALCHEMY_DATABASE_URL)
19
+ TestingSessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
20
+ Base.metadata.create_all(bind=engine)
21
+ # yield the app and database session to the test
22
+ try:
23
+ yield client, TestingSessionLocal()
24
+ finally:
25
+ # clean up test database
26
+ Base.metadata.drop_all(bind=engine)
27
+
28
+
29
+ @pytest.fixture(scope="function")
30
+ def db_session():
31
+ # set up a new database session for each test
32
+ session = SessionLocal()
33
+ try:
34
+ yield session
35
+ finally:
36
+ session.close()
app/tests/test_api/__init__.py ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # File: my-fastapi-project/app/tests/test_api/__init__.py
2
+
3
+ from fastapi.testclient import TestClient
4
+ from my_fastapi_project.app.api import app
5
+
6
+ client = TestClient(app)
7
+
8
+ def test_read_main():
9
+ response = client.get("/")
10
+ assert response.status_code == 200
11
+ assert response.json() == {"msg": "Hello World"}
app/tests/test_api/test_items.py ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi.testclient import TestClient
2
+ from app.main import app
3
+
4
+ client = TestClient(app)
5
+
6
+ def test_create_item():
7
+ data = {"name": "test", "description": "test description"}
8
+ response = client.post("/items/", json=data)
9
+ assert response.status_code == 200
10
+ assert response.json()["name"] == "test"
11
+ assert response.json()["description"] == "test description"
12
+
13
+ def test_read_item():
14
+ response = client.get("/items/1")
15
+ assert response.status_code == 200
16
+ assert response.json()["name"] == "test"
17
+ assert response.json()["description"] == "test description"
18
+
19
+ def test_read_all_items():
20
+ response = client.get("/items/")
21
+ assert response.status_code == 200
22
+ assert len(response.json()) == 1
23
+
24
+ def test_update_item():
25
+ data = {"name": "updated test", "description": "updated test description"}
26
+ response = client.put("/items/1", json=data)
27
+ assert response.status_code == 200
28
+ assert response.json()["name"] == "updated test"
29
+ assert response.json()["description"] == "updated test description"
30
+
31
+ def test_delete_item():
32
+ response = client.delete("/items/1")
33
+ assert response.status_code == 200
34
+ assert response.json() == {"detail": "Item deleted successfully"}
app/tests/test_api/test_users.py ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi.testclient import TestClient
2
+ from app.core.config import settings
3
+
4
+ client = TestClient(settings.app)
5
+
6
+
7
+ def test_create_user():
8
+ data = {"email": "test@example.com", "password": "password"}
9
+ response = client.post("/users/", json=data)
10
+ assert response.status_code == 200
11
+ assert response.json()["email"] == "test@example.com"
12
+
13
+
14
+ def test_create_user_invalid_email():
15
+ data = {"email": "invalid_email", "password": "password"}
16
+ response = client.post("/users/", json=data)
17
+ assert response.status_code == 422
18
+ assert "value_error.email" in response.json()["detail"][0]["type"]
19
+
20
+
21
+ def test_create_user_invalid_password():
22
+ data = {"email": "test@example.com", "password": "short"}
23
+ response = client.post("/users/", json=data)
24
+ assert response.status_code == 422
25
+ assert "ensure this value has at least 6 characters" in response.json()["detail"][0]["msg"]
26
+
27
+
28
+ def test_read_user():
29
+ response = client.get("/users/1")
30
+ assert response.status_code == 200
31
+ assert response.json()["email"] == "test@example.com"
32
+
33
+
34
+ def test_read_user_not_found():
35
+ response = client.get("/users/999")
36
+ assert response.status_code == 404
37
+ assert response.json()["detail"] == "User not found"
38
+
39
+
40
+ def test_update_user():
41
+ data = {"email": "new_email@example.com", "password": "new_password"}
42
+ response = client.put("/users/1", json=data)
43
+ assert response.status_code == 200
44
+ assert response.json()["email"] == "new_email@example.com"
45
+
46
+
47
+ def test_update_user_not_found():
48
+ data = {"email": "new_email@example.com", "password": "new_password"}
49
+ response = client.put("/users/999", json=data)
50
+ assert response.status_code == 404
51
+ assert response.json()["detail"] == "User not found"
52
+
53
+
54
+ def test_delete_user():
55
+ response = client.delete("/users/1")
56
+ assert response.status_code == 200
57
+ assert response.json() == {"msg": "User deleted"}
58
+
59
+
60
+ def test_delete_user_not_found():
61
+ response = client.delete("/users/999")
62
+ assert response.status_code == 404
63
+ assert response.json()["detail"] == "User not found"
app/tests/test_core/__init__.py ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi.testclient import TestClient
2
+ from app.core.config import settings
3
+
4
+
5
+ def test_app():
6
+ from app.main import app
7
+ client = TestClient(app)
8
+ response = client.get("/")
9
+ assert response.status_code == 200
10
+ assert response.json() == {"message": "Hello World"}
11
+
12
+
13
+ def test_config():
14
+ assert settings.app_name == "My FastAPI Project"
15
+ assert settings.log_level == "debug"
16
+ assert settings.max_connection_count == 10
app/tests/test_core/test_config.py ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ from app.core.config import settings
2
+
3
+
4
+ def test_settings():
5
+ assert settings.API_V1_STR == "/api/v1"
6
+ assert settings.PROJECT_NAME == "My FastAPI Project"
7
+ assert settings.SQLALCHEMY_DATABASE_URI == "sqlite:///./test.db"
8
+ assert settings.ACCESS_TOKEN_EXPIRE_MINUTES == 30
app/tests/test_core/test_database.py ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sqlalchemy.orm import Session
2
+ from app.core.database import Base
3
+ from app.models.user import User
4
+ from app.models.item import Item
5
+
6
+
7
+ def test_create_user(db: Session):
8
+ user = User(email="test@example.com", password="password123")
9
+ db.add(user)
10
+ db.commit()
11
+ db.refresh(user)
12
+ assert user.id is not None
13
+ assert user.email == "test@example.com"
14
+ assert user.is_active is True
15
+
16
+
17
+ def test_create_item(db: Session):
18
+ item = Item(name="test item", description="test description")
19
+ db.add(item)
20
+ db.commit()
21
+ db.refresh(item)
22
+ assert item.id is not None
23
+ assert item.name == "test item"
24
+ assert item.description == "test description"
app/tests/test_core/test_security.py ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import HTTPException
2
+ from app.core.security import verify_password, get_password_hash
3
+
4
+ def test_password_hashing():
5
+ password = "testpassword"
6
+ hashed_password = get_password_hash(password)
7
+ assert hashed_password != password
8
+
9
+ def test_password_verification():
10
+ password = "testpassword"
11
+ hashed_password = get_password_hash(password)
12
+ assert verify_password(password, hashed_password)
13
+ assert not verify_password("wrongpassword", hashed_password)
14
+
15
+ def test_password_verification_exception():
16
+ password = "testpassword"
17
+ hashed_password = get_password_hash(password)
18
+ try:
19
+ verify_password("wrongpassword", hashed_password)
20
+ except HTTPException as e:
21
+ assert e.status_code == 401
22
+ assert e.detail == "Incorrect email or password"
requirements.txt ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ fastapi
2
+ uvicorn[standard]
3
+ python-dotenv
4
+ sqlalchemy
5
+ psycopg2-binary
6
+ pytest
7
+ pytest-cov
8
+ faker
9
+ requests-mock