Merge pull request #10 from ljensen505/main-controller-tests

added main-controller tests
This commit is contained in:
Lucas Jensen
2024-05-03 21:19:20 -07:00
committed by GitHub
11 changed files with 240 additions and 27 deletions

View File

@@ -1,3 +1,9 @@
from .controller import MainController from app.controllers.events import EventController
from app.controllers.group import GroupController
from app.controllers.musicians import MusicianController
from app.controllers.users import UserController
controller = MainController() user_controller = UserController()
event_controller = EventController()
group_controller = GroupController()
musicians_controller = MusicianController()

View File

@@ -3,6 +3,12 @@ from fastapi.security import HTTPAuthorizationCredentials
from icecream import ic from icecream import ic
from app.admin import oauth_token from app.admin import oauth_token
from app.controllers import (
event_controller,
group_controller,
musicians_controller,
user_controller,
)
from app.controllers.events import EventController from app.controllers.events import EventController
from app.controllers.group import GroupController from app.controllers.group import GroupController
from app.controllers.musicians import MusicianController from app.controllers.musicians import MusicianController
@@ -22,19 +28,29 @@ class MainController:
All methods are asynchronous to facilitate asynchronous calls from the Router layer. All methods are asynchronous to facilitate asynchronous calls from the Router layer.
token-based authentication is handled here as needed per the nature of the data being accessed. token-based authentication is handled here as needed per the nature of the data being accessed.
Testing: pass mocked sub-controllers to the constructor.
""" """
def __init__(self) -> None: def __init__(
self.event_controller = EventController() self,
self.musician_controller = MusicianController() event_controller=event_controller,
self.user_controller = UserController() musicians_controller=musicians_controller,
self.group_controller = GroupController() user_controller=user_controller,
group_controller=group_controller,
oauth_token=oauth_token,
) -> None:
self.event_controller = event_controller
self.musician_controller = musicians_controller
self.user_controller = user_controller
self.group_controller = group_controller
self.oauth_token = oauth_token
async def get_musicians(self) -> list[Musician]: async def get_musicians(self) -> list[Musician]:
return self.musician_controller.get_musicians() return self.musician_controller.get_musicians()
async def get_musician(self, id: int) -> Musician: async def get_musician(self, musician_id: int) -> Musician:
return self.musician_controller.get_musician(id) return self.musician_controller.get_musician(musician_id)
async def update_musician( async def update_musician(
self, self,
@@ -49,7 +65,7 @@ class MainController:
status_code=status.HTTP_400_BAD_REQUEST, status_code=status.HTTP_400_BAD_REQUEST,
detail="ID in URL does not match ID in request body", detail="ID in URL does not match ID in request body",
) )
_, sub = oauth_token.email_and_sub(token) _, sub = self.oauth_token.email_and_sub(token)
self.user_controller.get_user_by_sub(sub) self.user_controller.get_user_by_sub(sub)
return self.musician_controller.update_musician( return self.musician_controller.update_musician(
musician_id=musician.id, musician_id=musician.id,
@@ -60,42 +76,54 @@ class MainController:
async def get_events(self) -> list[EventSeries]: async def get_events(self) -> list[EventSeries]:
return self.event_controller.get_all_series() return self.event_controller.get_all_series()
async def get_event(self, id: int) -> EventSeries: async def get_event(self, series_id: int) -> EventSeries:
return self.event_controller.get_one_series_by_id(id) return self.event_controller.get_one_series_by_id(series_id)
async def create_event( async def create_event(
self, series: NewEventSeries, token: HTTPAuthorizationCredentials self, series: NewEventSeries, token: HTTPAuthorizationCredentials
) -> EventSeries: ) -> EventSeries:
_, sub = oauth_token.email_and_sub(token) _, sub = self.oauth_token.email_and_sub(token)
self.user_controller.get_user_by_sub(sub) self.user_controller.get_user_by_sub(sub)
return self.event_controller.create_series(series) return self.event_controller.create_series(series)
async def add_series_poster( async def add_series_poster(
self, series_id: int, poster: UploadFile, token: HTTPAuthorizationCredentials self, series_id: int, poster: UploadFile, token: HTTPAuthorizationCredentials
) -> EventSeries: ) -> EventSeries:
_, sub = oauth_token.email_and_sub(token) _, sub = self.oauth_token.email_and_sub(token)
self.user_controller.get_user_by_sub(sub) self.user_controller.get_user_by_sub(sub)
return self.event_controller.add_series_poster(series_id, poster) return self.event_controller.add_series_poster(series_id, poster)
async def delete_series(self, id: int, token: HTTPAuthorizationCredentials) -> None: async def delete_series(
_, sub = oauth_token.email_and_sub(token) self, series_id: int, token: HTTPAuthorizationCredentials
) -> None:
_, sub = self.oauth_token.email_and_sub(token)
self.user_controller.get_user_by_sub(sub) self.user_controller.get_user_by_sub(sub)
self.event_controller.delete_series(id) self.event_controller.delete_series(series_id)
async def update_series( async def update_series(
self, route_id: int, series: EventSeries, token: HTTPAuthorizationCredentials self, route_id: int, series: EventSeries, token: HTTPAuthorizationCredentials
) -> EventSeries: ) -> EventSeries:
_, sub = oauth_token.email_and_sub(token) _, sub = self.oauth_token.email_and_sub(token)
self.user_controller.get_user_by_sub(sub) self.user_controller.get_user_by_sub(sub)
return self.event_controller.update_series(route_id, series) return self.event_controller.update_series(route_id, series)
async def get_users(self) -> list[User]: async def get_users(self) -> list[User]:
return self.user_controller.get_users() return self.user_controller.get_users()
async def get_user(self, id: int) -> User: async def get_user(self, user_id: int) -> User:
return self.user_controller.get_user_by_id(id) return self.user_controller.get_user_by_id(user_id)
async def create_user(self, token: HTTPAuthorizationCredentials) -> User: async def create_user(self, token: HTTPAuthorizationCredentials) -> User:
"""This method does NOT post a user to the database.
Instead, it retrieves the user's information from the OAuth token,
updates the user's sub in the db if needed, and returns the user object.
Args:
token (HTTPAuthorizationCredentials): The OAuth token
Returns:
User: The User object
"""
return self.user_controller.create_user(token) return self.user_controller.create_user(token)
async def get_group(self) -> Group: async def get_group(self) -> Group:
@@ -104,6 +132,6 @@ class MainController:
async def update_group_bio( async def update_group_bio(
self, bio: str, token: HTTPAuthorizationCredentials self, bio: str, token: HTTPAuthorizationCredentials
) -> Group: ) -> Group:
_, sub = oauth_token.email_and_sub(token) _, sub = self.oauth_token.email_and_sub(token)
self.user_controller.get_user_by_sub(sub) self.user_controller.get_user_by_sub(sub)
return self.group_controller.update_group_bio(bio) return self.group_controller.update_group_bio(bio)

View File

@@ -116,6 +116,14 @@ class UserController(BaseController):
) )
def create_user(self, token: HTTPAuthorizationCredentials) -> User: def create_user(self, token: HTTPAuthorizationCredentials) -> User:
"""Updates a user's sub in the database and creates a new User object.
Args:
token (HTTPAuthorizationCredentials): The token containing the user's email and sub
Returns:
User: A User object which is suitable for a response body
"""
email, sub = oauth_token.email_and_sub(token) email, sub = oauth_token.email_and_sub(token)
user: User = self.get_user_by_email(email) user: User = self.get_user_by_email(email)
if user.sub is None: if user.sub is None:

View File

@@ -3,7 +3,7 @@ from asyncio import gather
from fastapi import FastAPI from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware from fastapi.middleware.cors import CORSMiddleware
from app.controllers import MainController from app.controllers.controller import MainController
from app.models.tgd import TheGrapefruitsDuo from app.models.tgd import TheGrapefruitsDuo
from app.routers.contact import router as contact_router from app.routers.contact import router as contact_router
from app.routers.events import router as event_router from app.routers.events import router as event_router

View File

@@ -0,0 +1,3 @@
from app.controllers.controller import MainController
controller = MainController()

View File

@@ -3,8 +3,8 @@ from fastapi.security import HTTPAuthorizationCredentials
from icecream import ic from icecream import ic
from app.admin import oauth2_http from app.admin import oauth2_http
from app.controllers import controller
from app.models.event import EventSeries, NewEventSeries from app.models.event import EventSeries, NewEventSeries
from app.routers import controller
router = APIRouter( router = APIRouter(
prefix="/events", prefix="/events",

View File

@@ -3,8 +3,8 @@ from fastapi.security.http import HTTPAuthorizationCredentials
from icecream import ic from icecream import ic
from app.admin import oauth2_http from app.admin import oauth2_http
from app.controllers import controller
from app.models.group import Group from app.models.group import Group
from app.routers import controller
router = APIRouter( router = APIRouter(
prefix="/group", prefix="/group",

View File

@@ -3,8 +3,8 @@ from fastapi.security import HTTPAuthorizationCredentials
from icecream import ic from icecream import ic
from app.admin import oauth2_http from app.admin import oauth2_http
from app.controllers import controller
from app.models.musician import Musician from app.models.musician import Musician
from app.routers import controller
router = APIRouter( router = APIRouter(
prefix="/musicians", prefix="/musicians",

View File

@@ -2,8 +2,8 @@ from fastapi import APIRouter, Depends, status
from fastapi.security import HTTPAuthorizationCredentials from fastapi.security import HTTPAuthorizationCredentials
from app.admin import oauth2_http from app.admin import oauth2_http
from app.controllers import controller
from app.models.user import User from app.models.user import User
from app.routers import controller
router = APIRouter( router = APIRouter(
prefix="/users", prefix="/users",

View File

@@ -1,6 +1,6 @@
[tool.poetry] [tool.poetry]
name = "thegrapefruitsduo" name = "thegrapefruitsduo"
version = "0.4.0" version = "0.4.1"
package-mode = false package-mode = false
description = "FastAPI backend for thegrapefruitsduo.com" description = "FastAPI backend for thegrapefruitsduo.com"
authors = ["Lucas Jensen <lucas.p.jensen10@gmail.com>"] authors = ["Lucas Jensen <lucas.p.jensen10@gmail.com>"]

View File

@@ -0,0 +1,168 @@
from unittest.mock import MagicMock
import pytest
from fastapi import HTTPException, status
from icecream import ic
from app.controllers.controller import MainController
from app.models.event import Event, EventSeries, NewEventSeries
from app.models.group import Group
from app.models.musician import Musician
from app.models.user import User
mock_user_controller = MagicMock()
mock_musician_controller = MagicMock()
mock_group_controller = MagicMock()
mock_event_controller = MagicMock()
mock_oauth_token = MagicMock()
mock_oauth_token.email_and_sub = MagicMock(return_value=("email", "sub"))
mock_token = MagicMock()
controller = MainController(
user_controller=mock_user_controller,
musicians_controller=mock_musician_controller,
group_controller=mock_group_controller,
event_controller=mock_event_controller,
oauth_token=mock_oauth_token, # type: ignore
)
def test_type():
"""Tests the type of the controller object."""
assert isinstance(controller, MainController)
@pytest.mark.asyncio
async def test_get_musicians():
"""Tests the get_musicians method."""
await controller.get_musicians()
MagicMock.assert_called_once_with(mock_musician_controller.get_musicians)
@pytest.mark.asyncio
async def test_get_musician():
"""Tests the get_musician method."""
musician_id = 1
await controller.get_musician(musician_id)
MagicMock.assert_called_once_with(
mock_musician_controller.get_musician, musician_id
)
@pytest.mark.asyncio
async def test_update_musician():
"""Tests the update_musician method.
Underlying controller methods are tested elsewhere. This test is to ensure the method is called correctly.
"""
musician = Musician(
id=1, name="John Doe", bio="A musician", headshot_id="headshot123"
)
await controller.update_musician(
musician=musician, url_param_id=1, token=mock_token
)
MagicMock.assert_called_with(mock_oauth_token.email_and_sub, mock_token)
MagicMock.assert_called_once(mock_user_controller.get_user_by_sub)
MagicMock.assert_called_once(mock_musician_controller.update_musician)
@pytest.mark.asyncio
async def test_get_events():
"""Tests the get_events method."""
await controller.get_events()
MagicMock.assert_called_once(mock_event_controller.get_all_series)
@pytest.mark.asyncio
async def test_get_event():
"""Tests the get_event method."""
series_id = 1
await controller.get_event(series_id)
MagicMock.assert_called_once_with(
mock_event_controller.get_one_series_by_id, series_id
)
@pytest.mark.asyncio
async def test_create_event():
"""Tests the create_event method."""
series = NewEventSeries(name="Test Event", description="A test event", events=[])
await controller.create_event(series, mock_token)
MagicMock.assert_called_with(mock_oauth_token.email_and_sub, mock_token)
MagicMock.assert_called(mock_user_controller.get_user_by_sub)
MagicMock.assert_called(mock_event_controller.create_series)
@pytest.mark.asyncio
async def test_add_series_poster():
"""Tests the add_series_poster method."""
series_id = 1
poster = MagicMock()
await controller.add_series_poster(series_id, poster, mock_token)
MagicMock.assert_called_with(mock_oauth_token.email_and_sub, mock_token)
MagicMock.assert_called(mock_user_controller.get_user_by_sub)
MagicMock.assert_called(mock_event_controller.add_series_poster)
@pytest.mark.asyncio
async def test_delete_series():
"""Tests the delete_series method."""
series_id = 1
await controller.delete_series(series_id, mock_token)
MagicMock.assert_called_with(mock_oauth_token.email_and_sub, mock_token)
MagicMock.assert_called(mock_user_controller.get_user_by_sub)
MagicMock.assert_called(mock_event_controller.delete_series)
@pytest.mark.asyncio
async def test_update_series():
"""Tests the update_series method."""
series = EventSeries(
series_id=1, name="Test Event", description="A test event", events=[]
)
await controller.update_series(1, series, mock_token)
MagicMock.assert_called_with(mock_oauth_token.email_and_sub, mock_token)
MagicMock.assert_called(mock_user_controller.get_user_by_sub)
MagicMock.assert_called(mock_event_controller.update_series)
@pytest.mark.asyncio
async def test_get_users():
"""Tests the get_users method."""
await controller.get_users()
MagicMock.assert_called_once(mock_user_controller.get_users)
@pytest.mark.asyncio
async def test_get_user():
"""Tests the get_user method."""
user_id = 1
await controller.get_user(user_id)
MagicMock.assert_called_once_with(mock_user_controller.get_user_by_id, user_id)
@pytest.mark.asyncio
async def test_create_user():
"""Tests the create_user method."""
await controller.create_user(mock_token)
MagicMock.assert_called_with(mock_oauth_token.email_and_sub, mock_token)
MagicMock.assert_called(mock_user_controller.create_user)
@pytest.mark.asyncio
async def test_get_group():
"""Tests the get_group method."""
await controller.get_group()
MagicMock.assert_called_once(mock_group_controller.get_group)
@pytest.mark.asyncio
async def test_update_group_bio():
"""Tests the update_group_bio method."""
bio = "A new bio"
await controller.update_group_bio(bio, mock_token)
MagicMock.assert_called_with(mock_oauth_token.email_and_sub, mock_token)
MagicMock.assert_called(mock_user_controller.get_user_by_sub)
MagicMock.assert_called(mock_group_controller.update_group_bio)