Merge pull request #7 from ljensen505/add-more-tests

added more unit tests
This commit is contained in:
Lucas Jensen
2024-05-03 13:03:26 -07:00
committed by GitHub
3 changed files with 132 additions and 23 deletions

View File

@@ -85,22 +85,22 @@ class MusicianController(BaseController):
""" """
musician = await self.get_musician(musician_id) musician = await self.get_musician(musician_id)
if new_bio != musician.bio: if new_bio != musician.bio:
return await self._update_musician_bio(musician.id, new_bio) return await self._update_musician_bio(musician, new_bio)
if file is not None: if file is not None:
return await self._upload_headshot(musician.id, file) return await self._upload_headshot(musician, file)
raise HTTPException( raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST, status_code=status.HTTP_400_BAD_REQUEST,
detail="Update operation not implemented. Neither the bio or headshot was updated.", detail="Update operation not implemented. Neither the bio or headshot was updated.",
) )
async def update_musician_headshot( async def _update_musician_headshot(
self, musician_id: int, headshot_id: str self, musician: Musician, headshot_id: str
) -> Musician: ) -> Musician:
"""Updates a musician's headshot in the database. """Updates a musician's headshot in the database.
Args: Args:
id (int): The numeric ID of the musician to update musician (Musician): The musician object to update
headshot_id (str): The public ID of the new headshot (as determined by Cloudinary) headshot_id (str): The public ID of the new headshot (as determined by Cloudinary)
Raises: Raises:
@@ -109,21 +109,20 @@ class MusicianController(BaseController):
Returns: Returns:
Musician: The updated Musician object Musician: The updated Musician object
""" """
await self.get_musician(musician_id)
try: try:
await self.db.update_headshot(musician_id, headshot_id) await self.db.update_headshot(musician, headshot_id)
except Exception as e: except Exception as e:
raise HTTPException( raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Error updating musician headshot: {e}", detail=f"Error updating musician headshot: {e}",
) )
return await self.get_musician(musician_id) return await self.get_musician(musician.id)
async def _update_musician_bio(self, musician_id: int, bio: str) -> Musician: async def _update_musician_bio(self, musician: Musician, bio: str) -> Musician:
"""Updates a musician's bio in the database. """Updates a musician's bio in the database.
Args: Args:
id (int): The numeric ID of the musician to update musician (Musician): The musician object to update
bio (str): The new biography for the musician bio (str): The new biography for the musician
Raises: Raises:
@@ -132,21 +131,20 @@ class MusicianController(BaseController):
Returns: Returns:
Musician: The updated Musician object Musician: The updated Musician object
""" """
await self.get_musician(musician_id) # Check if musician exists
try: try:
await self.db.update_bio(musician_id, bio) await self.db.update_bio(musician, bio)
except Exception as e: except Exception as e:
raise HTTPException( raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Error updating musician bio: {e}", detail=f"Error updating musician bio: {e}",
) )
return await self.get_musician(musician_id) return await self.get_musician(musician.id)
async def _upload_headshot(self, id: int, file: UploadFile) -> Musician: async def _upload_headshot(self, musician: Musician, file: UploadFile) -> Musician:
"""Uploads a new headshot for a musician and updates the database with the new public ID. """Uploads a new headshot for a musician and updates the database with the new public ID.
Args: Args:
id (int): The numeric ID of the musician to update musician (Musician): The musician object to update
file (UploadFile): The new headshot file file (UploadFile): The new headshot file
Raises: Raises:
@@ -163,6 +161,6 @@ class MusicianController(BaseController):
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Failed to upload image", detail="Failed to upload image",
) )
await self.update_musician_headshot(id, public_id) await self._update_musician_headshot(musician, public_id)
return await self.get_musician(id) return await self.get_musician(musician.id)

View File

@@ -3,6 +3,7 @@ from icecream import ic
from app.constants import MUSICIAN_TABLE from app.constants import MUSICIAN_TABLE
from app.db.base_queries import BaseQueries from app.db.base_queries import BaseQueries
from app.db.conn import connect_db from app.db.conn import connect_db
from app.models.musician import Musician
class MusicianQueries(BaseQueries): class MusicianQueries(BaseQueries):
@@ -10,20 +11,33 @@ class MusicianQueries(BaseQueries):
super().__init__() super().__init__()
self.table = MUSICIAN_TABLE self.table = MUSICIAN_TABLE
async def update_bio(self, id: int, bio: str) -> None: async def update_bio(self, musician: Musician, bio: str) -> None:
"""Updates a musician's biography in the database.
Args:
musician (Musician): The musician object to update
bio (str): The new biography for the musician
"""
db = connect_db() db = connect_db()
cursor = db.cursor() cursor = db.cursor()
query = f"UPDATE {self.table} SET bio = %s WHERE id = %s" query = f"UPDATE {self.table} SET bio = %s WHERE id = %s"
cursor.execute(query, (bio, id)) cursor.execute(query, (bio, musician.id))
db.commit() db.commit()
cursor.close() cursor.close()
db.close() db.close()
async def update_headshot(self, id: int, headshot_id: str) -> None: async def update_headshot(self, musician: Musician, headshot_id: str) -> None:
"""Updates a musician's headshot ID in the database.
The image itself is stored with Cloudinary.
Args:
musician (Musician): The musician object to update
headshot_id (str): The public ID of the new headshot (as determined by Cloudinary)
"""
db = connect_db() db = connect_db()
cursor = db.cursor() cursor = db.cursor()
query = f"UPDATE {self.table} SET headshot_id = %s WHERE id = %s" query = f"UPDATE {self.table} SET headshot_id = %s WHERE id = %s"
cursor.execute(query, (headshot_id, id)) cursor.execute(query, (headshot_id, musician.id))
db.commit() db.commit()
cursor.close() cursor.close()
db.close() db.close()

View File

@@ -1,6 +1,7 @@
from unittest.mock import Mock from unittest.mock import MagicMock, Mock
import pytest import pytest
from fastapi import HTTPException, UploadFile, status
from icecream import ic from icecream import ic
from app.controllers.musicians import MusicianController from app.controllers.musicians import MusicianController
@@ -9,9 +10,105 @@ from app.models.musician import Musician
mock_queries = Mock() mock_queries = Mock()
ec = MusicianController(musician_queries=mock_queries) ec = MusicianController(musician_queries=mock_queries)
sample_data = [
{
"id": 1,
"name": "John Doe",
"bio": "A musician",
"headshot_id": "headshot123",
},
{
"id": 2,
"name": "Jane Doe",
"bio": "Another musician",
"headshot_id": "headshotABC",
},
]
bad_data = [
# no bio
{
"id": "three",
"name": "Jack Doe",
"headshot_id": "headshot456",
}
]
async def mock_select_all_series():
return sample_data
async def mock_select_all_series_sad():
return bad_data
async def mock_select_one_by_id(musician_id: int):
for musician in sample_data:
if musician.get("id") == musician_id:
return musician
return None
mock_queries.select_all_series = mock_select_all_series
mock_queries.select_one_by_id = mock_select_one_by_id
def test_type(): def test_type():
assert isinstance(ec, MusicianController) assert isinstance(ec, MusicianController)
# TODO: Write tests for MusicianController """
TODO: write tests for following methods:
- _update_musician_headshot
- _update_musician_bio
- _upload_headshot
"""
@pytest.mark.asyncio
async def test_happy_get_musicians():
musicians = await ec.get_musicians()
assert isinstance(musicians, list)
assert len(musicians) == 2
for musician in musicians:
assert isinstance(musician, Musician)
m1, m2 = musicians
assert m1.id == 1
assert m1.name == "John Doe"
assert m1.bio == "A musician"
assert m1.headshot_id == "headshot123"
assert m2.id == 2
assert m2.name == "Jane Doe"
assert m2.bio == "Another musician"
assert m2.headshot_id == "headshotABC"
@pytest.mark.asyncio
async def test_sad_get_musicians():
mock_queries.select_all_series = mock_select_all_series_sad
with pytest.raises(HTTPException) as e:
await ec.get_musicians()
mock_queries.select_all_series = mock_select_all_series
assert isinstance(e.value, HTTPException)
assert e.value.status_code == status.HTTP_500_INTERNAL_SERVER_ERROR
@pytest.mark.asyncio
async def test_happy_get_musician():
musician = await ec.get_musician(1)
assert isinstance(musician, Musician)
assert musician.id == 1
assert musician.name == "John Doe"
assert musician.bio == "A musician"
assert musician.headshot_id == "headshot123"
@pytest.mark.asyncio
async def test_musician_not_found():
with pytest.raises(HTTPException) as e:
await ec.get_musician(3)
assert isinstance(e.value, HTTPException)
assert e.value.status_code == status.HTTP_404_NOT_FOUND
assert e.value.detail == "Musician not found"