Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion api/v1/models/activity_logs.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,4 @@ class ActivityLog(BaseTableModel):
action = Column(String, nullable=False)
timestamp = Column(DateTime(timezone=True), server_default=func.now())

user = relationship("User", back_populates="activity_logs")
user = relationship("User", back_populates="activity_logs")
2 changes: 2 additions & 0 deletions api/v1/models/testimonial.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,5 @@ class Testimonial(BaseTableModel):
ratings = Column(Float, nullable=True)

author = relationship("User", back_populates="testimonials")


32 changes: 32 additions & 0 deletions api/v1/routes/testimonial.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
"""
Module contains CRUD routes for testimonial
"""

from fastapi.encoders import jsonable_encoder
from fastapi import HTTPException
from api.db.database import get_db
from sqlalchemy.orm import Session
from api.v1.models.user import User
Expand All @@ -11,6 +13,7 @@
from api.v1.services.testimonial import testimonial_service
from api.v1.services.user import user_service
from api.v1.schemas.testimonial import CreateTestimonial
from api.v1.schemas.testimonial import UpdateTestimonial
from api.core.responses import SUCCESS
from typing import Annotated
from api.utils.pagination import paginated_response
Expand Down Expand Up @@ -96,6 +99,35 @@ def create_testimonial(
)
return response


@testimonial.put("/{testimonial_id}", response_model=success_response)
def update_testimonial(
testimonial_id: str,
testimonial_data: UpdateTestimonial,
db: Annotated[Session, Depends(get_db)],
current_user: User = Depends(user_service.get_current_user)
):
"""Endpoint to update a testimonial"""

existing_testimonial = testimonial_service.fetch(db, testimonial_id)
if not existing_testimonial:
raise HTTPException(status_code=404, detail="Testimonial not found")

if existing_testimonial.author_id != current_user.id:
raise HTTPException(status_code=403, detail="Not authorized to update this testimonial")

updated_testimonial = testimonial_service.update(db, testimonial_id, testimonial_data)

if not updated_testimonial:
raise HTTPException(status_code=500, detail="Failed to update testimonial")

response = success_response(
status_code=200,
message="Testimonial updated successfully",
data={"id": updated_testimonial.id}
)
return response

@testimonial.get("/user/{user_id}", status_code=status.HTTP_200_OK)
def get_user_testimonials(
user_id: str,
Expand Down
10 changes: 9 additions & 1 deletion api/v1/schemas/testimonial.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
from pydantic import BaseModel
from typing import Optional

class CreateTestimonial(BaseModel):
content: str
ratings: float = 0
ratings: float = 0


class UpdateTestimonial(BaseModel):
content: Optional[str] = None
ratings: Optional[float] = None
client_name: Optional[str] = None
client_designation: Optional[str] = None
16 changes: 14 additions & 2 deletions api/v1/services/testimonial.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
from api.v1.models.testimonial import Testimonial
from api.v1.models.user import User
from api.v1.schemas.testimonial import CreateTestimonial
from api.v1.schemas.testimonial import UpdateTestimonial



class TestimonialService(Service):
Expand Down Expand Up @@ -34,9 +36,19 @@ def fetch(self, db: Session, id: str):

return check_model_existence(db, Testimonial, id)

def update(self, db: Session, id: str, schema):
def update(self, db: Session, id: str, schema: UpdateTestimonial):
"""Updates a testimonial"""
pass
testimonial = self.fetch(db, id)
if not testimonial:
return None

for key, value in schema.dict(exclude_unset=True).items():
setattr(testimonial, key, value)

db.commit()
db.refresh(testimonial)
return testimonial


def delete(self, db: Session, id: str):
"""Deletes a specific testimonial"""
Expand Down
103 changes: 103 additions & 0 deletions tests/v1/testimonial/test_testimonial_updates.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import pytest
from fastapi.testclient import TestClient
from unittest.mock import MagicMock, patch
from uuid_extensions import uuid7
from datetime import datetime, timezone
from faker import Faker
from main import app
from api.db.database import get_db
from api.v1.models.user import User
from api.v1.models.testimonial import Testimonial
from api.v1.services.user import user_service
from fastapi import status

fake = Faker()
client = TestClient(app)

# Fixtures
@pytest.fixture
def db_session_mock():
db_session = MagicMock()
return db_session

@pytest.fixture
def client(db_session_mock):
app.dependency_overrides[get_db] = lambda: db_session_mock
client = TestClient(app)
yield client
app.dependency_overrides = {}


def mock_get_current_user():
return User(
id=str(uuid7()),
email=fake.email(),
password=user_service.hash_password("Testpassword@123"),
first_name="Test",
last_name="User",
is_active=True,
is_superadmin=False,
created_at=datetime.now(timezone.utc),
updated_at=datetime.now(timezone.utc)
)

def mock_testimonial(user_id):
return Testimonial(
id=str(uuid7()),
content="Original content",
author_id=user_id,
client_name="Client 1",
client_designation="Client Designation",
comments="Testimonial comments",
ratings=4.5
)


def test_update_testimonial_success(client, db_session_mock):
'''Test successful update of a testimonial'''

mock_user = mock_get_current_user()
app.dependency_overrides[user_service.get_current_user] = lambda: mock_user

mock_testimonial_obj = mock_testimonial(mock_user.id)
db_session_mock.get.return_value = mock_testimonial_obj

update_data = {"content": "Updated content"}
response = client.put(
f'/api/v1/testimonials/{mock_testimonial_obj.id}',
json=update_data,
headers={'Authorization': 'Bearer token'}
)

assert response.status_code == 200
assert response.json()["message"] == "Testimonial updated successfully"


def test_update_testimonial_not_found(client, db_session_mock):
'''Test updating a non-existing testimonial'''

app.dependency_overrides[user_service.get_current_user] = lambda: mock_get_current_user()

db_session_mock.get.return_value = None

update_data = {"content": "Updated content"}
testimonial_id = str(uuid7())
response = client.put(
f'/api/v1/testimonials/{testimonial_id}',
json=update_data,
headers={'Authorization': 'Bearer token'}
)

assert response.status_code == 404
assert response.json()["message"] in ["Testimonial not found", "Testimonial does not exist"]

def test_update_testimonial_unauthorized(client):
'''Test updating a testimonial without authentication'''

testimonial_id = str(uuid7())
update_data = {"content": "Updated content"}

response = client.put(f'/api/v1/testimonials/{testimonial_id}', json=update_data)

assert response.status_code == 401
assert response.json()["message"] == "Not authenticated"