Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How can I override my JWT auth dependency in the endponits in FastAPI testing? #635

Open
SebaRossi94 opened this issue Mar 7, 2024 · 1 comment

Comments

@SebaRossi94
Copy link

I managed to override my DB dependency for my testing in FastAPI but when I try to apply the same technique for overriding my JWT auth dependency I still get a 422 response with the following detail:

{'detail': [{'type': 'missing', 'loc': ['query', 'fake_jwt'], 'msg': 'Field required', 'input': None, 'url': 'https://errors.pydantic.dev/2.6/v/missing'}]}

I followed all documentation and suggested posts on this topic of overriding dependencies for testing and still haven't found my issue. Does anyone know what's the problem here?
Here are the involved codes:

conftest.py

def init_test_db(_app):
    engine = create_engine(
        settings.sql_alchemy_database_url,
        connect_args={"check_same_thread": False},
    )

    def override_get_session():
        with Session(engine, autoflush=True) as session:
            with session.begin():
                yield session

    def override_get_session_no_transaction():
        with Session(engine) as session:
            yield session

    _app.dependency_overrides[get_session] = override_get_session
    _app.dependency_overrides[get_session_no_transaction] = (
        override_get_session_no_transaction
    )
    SQLBaseModel.metadata.create_all(bind=engine)
    with Session(engine) as session:
        for user in fake_users.values():
            session.add(user)
        session.commit()
    return engine

@pytest.fixture
def app_with_db():
    from app.main import app

    test_engine = init_test_db(app)
    yield app
    app.dependency_overrides = {}
    drop_test_db(test_engine)

@pytest.fixture()
def app_with_db_and_jwt(app_with_db):
    def override_jwt_dependency(fake_jwt):
        print(fake_jwt)
        return TokenData(id=1, email="[email protected]")
    app_with_db.dependency_overrides[validate_access_token] = override_jwt_dependency
    yield app_with_db

test.py

def test_me_success(app_with_db_and_jwt):
    darth_user = fake_users["darth"]
    client = TestClient(app_with_db_and_jwt)
    headers = {"Authorization": "Bearer fakejwt.super.fake"}
    me_response = client.get("/users/me", headers=headers)
    print(me_response.json())
    assert me_response.status_code == 200

schemas.py

from pydantic import BaseModel


class TokenSchema(BaseModel):
    access_token: str
    token_type: str


class TokenData(BaseModel):
    id: int
    email: str

router.py

@users_router.get("/me", response_model=ResponseUserSchema)
def me(user_data: jwt_dependency, db: get_session_dependency):
    user = db.exec(
        select(User).where(User.email == user_data.email, User.id == user_data.id)
    ).first()
    return user

dependencies.py

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="auth/token")

token_dependency = Annotated[str, Depends(oauth2_scheme)]
def validate_access_token(token: token_dependency):
    try:
        token_payload = jwt.decode(
            token, key=settings.jwt_secret_key, algorithms=settings.jwt_algorithm
        )
        email = token_payload.get("sub")
        user_id = token_payload.get("id")
        if email is None or user_id is None:
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="Could not validate user",
            )
        else:
            return TokenData(id=user_id, email=email)
    except JWTError as e:
        logger.logger.exception(e)
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED, detail="Could not validate user"
        )


jwt_dependency = Annotated[TokenData, Depends(validate_access_token)]
@saltie2193
Copy link
Contributor

In case you did not find a solution yet.

The problem might be, that the override override_jwt_dependency has one argument fake_jwt. This should be the same as adding it this argument in the me function, resulting in an expected query parameter fake_jwt. (.../api/me?fake_jwt=<some value>).

Using a function without any arguments as override however should work:

# conftest.py
...
@pytest.fixture()
def app_with_db_and_jwt(app_with_db):
    def override_jwt_dependency():
        return TokenData(id=1, email="[email protected]")
    app_with_db.dependency_overrides[validate_access_token] = override_jwt_dependency
    yield app_with_db
    app_with_db.dependency_overrides.pop(validate_access_token) # remove override

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants