24 : Unit test for JWT token header

You might have already noticed that we have several failing unit tests. It's because now our fastapi app expects a valid jwt token to be present in the header of some requests. But, our tests have not adapted to the change. Let's modify the tests to make post and delete requests with a valid jwt header. 
Remember, our unit test configurations live at tests > conftest.py. Time to add a new function to get a valid jwt token for requests.

...
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker,Session    #new

sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
# this is to include backend dir in sys.path so that we can import from db,main.py

from db.base import Base
from db.session import get_db
from apis.base import api_router
from core.config import settings           #new 
from tests.utils.users import authentication_token_from_email   #new


...


@pytest.fixture(scope="module")
def client(
    app: FastAPI, db_session: SessionTesting
) -> Generator[TestClient, Any, None]:
    """
    Create a new FastAPI TestClient that uses the `db_session` fixture to override
    the `get_db` dependency that is injected into routes.
    """

    def _get_test_db():
        try:
            yield db_session
        finally:
            pass

    .....
    .....

@pytest.fixture(scope="module")           #new function
def normal_user_token_headers(client: TestClient, db_session: Session):
    return  authentication_token_from_email(
        client=client, email=settings.TEST_USER_EMAIL, db=db_session
    )

Notice that we require a TEST_USER_EMAIL from config file. Let's modify our core > config.py file

class Settings:
    ACCESS_TOKEN_EXPIRE_MINUTES = 30  # in mins

    TEST_USER_EMAIL = "test@example.com"  #new


settings = Settings()

Basically, we have made the normal_user_token_header function a module-level fixture. It will be called once for our use-case and will give us a jwt token. Time to implement the logic to create a get a jwt token during tests. Make a directory/folder inside tests folder and name it utils. We are going to store our test utilities in this folder. Create a new file tests > utils > users.py

from db.repository.users import create_new_user
from db.repository.users import get_user_by_email
from fastapi.testclient import TestClient
from schemas.users import UserCreate
from sqlalchemy.orm import Session


def user_authentication_headers(client: TestClient, email: str, password: str):
    data = {"username": email, "password": password}
    r = client.post("/login/token", data=data)
    response = r.json()
    auth_token = response["access_token"]
    headers = {"Authorization": f"Bearer {auth_token}"}
    return headers


def authentication_token_from_email(client: TestClient, email: str, db: Session):
    """
    Return a valid token for the user with given email.
    If the user doesn't exist it is created first.
    """
    password = "random-passW0rd"
    user = get_user_by_email(email=email, db=db)
    if not user:
        user_in_create = UserCreate(username=email, email=email, password=password)
        user = create_new_user(user=user_in_create, db=db)
    return user_authentication_headers(client=client, email=email, password=password)

We have created a new function named get_user_by_emil to find out if we already have an existing user with the same email. I just realized we can improve our user creation process by first verifying if a user exists with the same email. Back to the track, we don't have this get_user_by_email function, so, lets create one in db > repository > users.py

def create_new_user(user: UserCreate, db: Session):
    ...
    return user

def get_user_by_email(email:str,db:Session):             #new
    user = db.query(User).filter(User.email == email).first()
    return user

Now, we good to modify our unit tests to include a header if required. Since, we require a header to post job and to delete job. I am going to modify them in unit tests in tests > test_routes > test_jobs.py

def test_create_job(client,normal_user_token_headers):   #added normal_user_token_headers
    data = {
        "title": "SDE super",
        "company": "doogle",
        "company_url": "www.doogle.com",
        "location": "USA,NY",
        "description": "python",
        "date_posted": "2022-03-20",
    }
    response = client.post("/jobs/create-job/",data=json.dumps(data),headers=normal_user_token_headers)  #added header in the post request
    assert response.status_code == 200
    assert response.json()["company"] == "doogle"
    assert response.json()["description"] == "python"

# We need to modify each and every unit test in which we are making a post/delete request. Since we are not restricting get requests. We do not need headers for get requests.

Once we make the changes, all the tests should pass again. Note, we can also use print statements/logs in tests and they will be printed on stdout if we have failing tests.
Git commit : https://github.com/nofoobar/JobBoard-Fastapi/commit/81f5f1629ea7b9cd42a6c04052b4374c9fc24ef2

Prev: 23 : Authorization/Permissions …