diff --git a/web_app/api/referal.py b/web_app/api/referal.py new file mode 100644 index 00000000..4d090063 --- /dev/null +++ b/web_app/api/referal.py @@ -0,0 +1,88 @@ +""" +FastAPI app for generating referral links based on wallet IDs. + +Endpoint: +- GET /api/create_referal_link: Creates a referral link with a random code for a user. + +Dependencies: +- SQLAlchemy: For user lookup in the database. +- FastAPI: For handling API requests. +- random and string: For generating referral codes. + +Errors: +- 404: If the user with the provided wallet ID does not exist. +""" + +import random +import string + +from fastapi import APIRouter, Depends, FastAPI, HTTPException, Query +from sqlalchemy.orm import Session + +from web_app.db.database import Base, get_database +from web_app.db.crud import UserDBConnector +from pydantic import BaseModel + +app = FastAPI() +router = APIRouter( + prefix="/api", + tags=["referral"], + responses={404: {"description": "Not found"}}, +) + + +class ReferralResponse(BaseModel): + """ + Response model + """ + + wallet_id: str + referral_code: str + + +def generate_random_string(length=16): + """ + Generate a random string of letters and digits with the given length. + + Args: + length (int): Length of the string (default is 16). + + Returns: + str: Randomly generated string. + """ + + return "".join(random.choices(string.ascii_letters + string.digits, k=length)) + + +@router.get("/create_referal_link") +async def create_referal_link( + wallet_id: str = Query(..., description="Wallet ID of the user"), + db: Session = Depends(get_database), +): + """ + Create a referral link for a user. + + Args: + wallet_id (str): The wallet ID of the user + + Returns: + dict: Wallet ID and the generated referral code + + Raises: + HTTPException: If the user is not found in the database + """ + + if not wallet_id: + raise HTTPException(status_code=400, detail="Wallet ID cannot be empty") + + user = UserDBConnector.get_user_by_wallet_id(db, wallet_id) + if not user: + raise HTTPException( + status_code=404, detail="User with the provided wallet_id does not exist" + ) + + referral_code = generate_random_string() + return ReferralResponse(wallet_id=wallet_id, referral_code=referral_code) + + +app.include_router(router) diff --git a/web_app/tests/test_create_referal_link.py b/web_app/tests/test_create_referal_link.py new file mode 100644 index 00000000..b64b3f65 --- /dev/null +++ b/web_app/tests/test_create_referal_link.py @@ -0,0 +1,90 @@ +""" +Unit tests for the FastAPI referral link creation endpoint. + +Tests include: +- Successful creation of a referral link with a valid wallet ID. +- Missing wallet ID in the request. +- User not found in the database. + +Uses pytest, unittest.mock for mocking, and FastAPI's TestClient for testing the API. +""" + +import pytest +from fastapi.testclient import TestClient + +from web_app.db.crud import UserDBConnector +from web_app.api.referal import app + + +@pytest.fixture +def client(): + """ + Returns a TestClient for the FastAPI app. + """ + return TestClient(app) + + +@pytest.fixture +def mock_get_user_by_wallet_id(mocker): + """ + Mocks the UserDBConnector's method for fetching a user by wallet_id. + """ + return mocker.patch.object(UserDBConnector, "get_user_by_wallet_id") + + +def test_create_referral_link_for_existing_user(client, mock_get_user_by_wallet_id): + """Positive Test Case: Test referral link creation for an existing user""" + mock_get_user_by_wallet_id.return_value = {"wallet_id": "valid_wallet_id"} + response = client.get("/api/create_referal_link?wallet_id=valid_wallet_id") + + assert response.status_code == 200 + assert "wallet_id" in response.json() + assert "referral_code" in response.json() + assert len(response.json()["referral_code"]) == 16 + + +def test_create_referral_link_for_non_existent_user(client, mock_get_user_by_wallet_id): + """Negative Test Case: Test referral link creation for a non-existent user""" + mock_get_user_by_wallet_id.return_value = None + response = client.get("/api/create_referal_link?wallet_id=non_existent_wallet_id") + + assert response.status_code == 404 + assert response.json() == { + "detail": "User with the provided wallet_id does not exist" + } + + +def test_create_referral_link_with_empty_wallet_id(client): + """Negative Test Case: Test referral link creation with an empty wallet ID""" + response = client.get("/api/create_referal_link?wallet_id=") + + assert response.status_code == 400 + assert response.json() == {"detail": "Wallet ID cannot be empty"} + + +def test_create_referral_link_with_malformed_wallet_id( + client, mock_get_user_by_wallet_id +): + """Test referral link creation with malformed wallet ID""" + mock_get_user_by_wallet_id.return_value = None + response = client.get("/api/create_referal_link?wallet_id=@@!invalidwallet") + + assert response.status_code == 404 + assert response.json() == { + "detail": "User with the provided wallet_id does not exist" + } + + +def test_create_referral_link_for_multiple_users(client, mock_get_user_by_wallet_id): + """Test referral link creation with a random referral code for multiple users""" + mock_get_user_by_wallet_id.return_value = {"wallet_id": "valid_wallet_id"} + response1 = client.get("/api/create_referal_link?wallet_id=valid_wallet_id") + referral_code1 = response1.json()["referral_code"] + response2 = client.get("/api/create_referal_link?wallet_id=valid_wallet_id") + referral_code2 = response2.json()["referral_code"] + + assert response1.status_code == 200 + assert response2.status_code == 200 + assert referral_code1 != referral_code2 + +