From 0a5319a5ddece110f7f66b7be34942e5ed728c2f Mon Sep 17 00:00:00 2001 From: Serrones Date: Sat, 18 Jul 2020 23:55:14 -0300 Subject: [PATCH] Create organization views and tests --- sucuri/crud/crud_organization.py | 45 +++++++ sucuri/main.py | 6 +- sucuri/schemas/schemas.py | 8 +- .../test_views/test_organization_view.py | 124 ++++++++++++++++++ sucuri/tests/test_views/test_profile_view.py | 15 +-- sucuri/views/__init__.py | 0 sucuri/views/organization_view.py | 62 +++++++++ 7 files changed, 246 insertions(+), 14 deletions(-) create mode 100644 sucuri/crud/crud_organization.py create mode 100644 sucuri/tests/test_views/test_organization_view.py create mode 100644 sucuri/views/__init__.py create mode 100644 sucuri/views/organization_view.py diff --git a/sucuri/crud/crud_organization.py b/sucuri/crud/crud_organization.py new file mode 100644 index 0000000..2802910 --- /dev/null +++ b/sucuri/crud/crud_organization.py @@ -0,0 +1,45 @@ +from sqlalchemy.orm import Session + +# IIB +from sucuri.database.dependency import get_db +from sucuri.models.models import Organization +from sucuri.schemas.schemas import OrganizationSchema + + +def post_organization(organization: OrganizationSchema, db: Session): + db_organization = Organization(**organization.dict()) + db.add(db_organization) + db.commit() + db.refresh(db_organization) + return db_organization + + +def get_organization(organization_id: int, db: Session): + return db.query(Organization).filter(Organization.id == organization_id).first() + + +def get_organizations(db: Session, skip: int = 0, limit: int = 100): + return db.query(Organization).offset(skip).limit(limit).all() + + +def delete_organization(organization_id: int, db: Session): + db.query(Organization).filter(Organization.id == organization_id).delete() + db.commit() + return + + +def update_organization( + old_organization: Organization, organization: OrganizationSchema, db: Session +): + db_organization = Organization(**organization.dict()) + old_organization.name = db_organization.name + old_organization.about = db_organization.about + old_organization.social_media = db_organization.social_media + old_organization.owner = ( + db_organization.owner if db_organization.owner else old_organization.owner + ) + old_organization.members = ( + db_organization.members if db_organization.members else old_organization.members + ) + db.commit() + return diff --git a/sucuri/main.py b/sucuri/main.py index 6c68237..1346a29 100644 --- a/sucuri/main.py +++ b/sucuri/main.py @@ -2,8 +2,12 @@ def get_app(): - from .views import profile_view + from .views import ( + profile_view, + organization_view, + ) app = FastAPI() app.include_router(profile_view.router) + app.include_router(organization_view.router) return app diff --git a/sucuri/schemas/schemas.py b/sucuri/schemas/schemas.py index 50b7d21..9a67f89 100644 --- a/sucuri/schemas/schemas.py +++ b/sucuri/schemas/schemas.py @@ -20,14 +20,12 @@ class Config: class OrganizationSchema(BaseModel): - id: int name: str about: str url: str - social_media: str - owner: Optional[int] = None - projects: Optional[List[int]] = None - members: Optional[List[int]] = None + social_media: List[Optional[SocialMediaSchema]] = [] + owner: int + members: List[Optional[int]] = [] class Config: orm_mode = True diff --git a/sucuri/tests/test_views/test_organization_view.py b/sucuri/tests/test_views/test_organization_view.py new file mode 100644 index 0000000..bece8c3 --- /dev/null +++ b/sucuri/tests/test_views/test_organization_view.py @@ -0,0 +1,124 @@ +# Standard Library +from unittest import mock + +# pytest +import pytest + +from fastapi import HTTPException +from fastapi.testclient import TestClient + +# IIB +from sucuri.main import get_app + + +app = get_app() + +client = TestClient(app) + + +class TestOrganizationView: + @pytest.fixture + def organization(self): + return { + "name": "Live de Python", + "about": "lives sobre temas pythônicos", + "url": "https://www.youtube.com/eduardomendes", + "social_media": [{"type": "twitter", "value": "@edumendes"}], + "owner": 1, + "members": [], + } + + @pytest.fixture + def organizations(self): + return [ + { + "name": "Live de Python", + "about": "lives sobre temas pythônicos", + "url": "https://www.youtube.com/eduardomendes", + "social_media": [{"type": "twitter", "value": "@edumendes"}], + "owner": 1, + "members": [], + }, + { + "name": "Foo", + "about": "aulas de foo", + "url": "https://www.foo.com/", + "social_media": [{"type": "twitter", "value": "@foo"}], + "owner": 1, + "members": [], + }, + { + "name": "Bar", + "about": "lives sobre bar", + "url": "https://www.bar.com/", + "social_media": [{"type": "twitter", "value": "@bar"}], + "owner": 1, + "members": [], + }, + ] + + def test_create_organization_should_return_200(self, organization): + response = client.post("/organizations/", json=organization) + assert response.status_code == 200 + assert response.json() == organization + + def test_get_organization_should_return_200(self, organization): + with mock.patch( + "sucuri.views.organization_view.get_organization" + ) as mock_get_organization: + mock_get_organization.return_value = organization + response = client.get("/organizations/1") + assert response.status_code == 200 + assert response.json() == organization + + def test_get_organization_should_return_organization_not_found(self): + with mock.patch( + "sucuri.views.organization_view.get_organization" + ) as mock_get_organization: + mock_get_organization.return_value = None + response = client.get("/organizations/1") + assert response.status_code == 404 + assert response.json() == {"detail": "Organization not found"} + + def test_get_organizations_should_return_existent_organizations( + self, organizations + ): + with mock.patch( + "sucuri.views.organization_view.get_organizations" + ) as mock_get_organizations: + mock_get_organizations.return_value = organizations + response = client.get("/organizations/") + assert response.status_code == 200 + assert len(response.json()) == 3 + + def test_get_organizations_should_return_empty_list(self): + with mock.patch( + "sucuri.views.organization_view.get_organizations" + ) as mock_get_organizations: + mock_get_organizations.return_value = [] + response = client.get("/organizations/") + assert response.status_code == 200 + assert len(response.json()) == 0 + + def test_delete_organization_should_return_200(self): + with mock.patch( + "sucuri.views.organization_view.delete_organization" + ) as mock_delete_organization: + mock_delete_organization.return_value = None + response = client.delete("/organizations/1") + assert response.status_code == 200 + + def test_update_organization_should_return_200(self, organization): + response = client.put("/organizations/1", json=organization) + assert response.status_code == 200 + + def test_update_organization_should_return_organization_not_found( + self, organization + ): + with mock.patch( + "sucuri.views.organization_view.get_organization" + ) as mock_get_organization: + mock_get_organization.return_value = None + response = client.put("/organizations/1", json=organization) + assert response.status_code == 404 + assert response.json() == {"detail": "Organization not found"} diff --git a/sucuri/tests/test_views/test_profile_view.py b/sucuri/tests/test_views/test_profile_view.py index 0930e48..147a61f 100644 --- a/sucuri/tests/test_views/test_profile_view.py +++ b/sucuri/tests/test_views/test_profile_view.py @@ -29,13 +29,10 @@ def profile(self): "name": "Zoey", "social_media": [{"type": "instagram", "value": "@zoey"}], } - + @pytest.fixture def profile_only_name(self): - return { - "name": "Zoey", - "social_media": [] - } + return {"name": "Zoey", "social_media": []} @pytest.fixture def broken_profile(self): @@ -47,15 +44,17 @@ def profiles(self): {"name": "John", "social_media": [{"type": "twitter", "value": "@john"}]}, {"name": "Paul", "social_media": [{"type": "instagram", "value": "@paul"}]}, {"name": "Zoey", "social_media": [{"type": "instagram", "value": "@zoey"}]}, - {"name": "Anthony", "social_media": []} + {"name": "Anthony", "social_media": []}, ] def test_create_profile_should_return_200(self, profile): response = client.post("/profiles/", json=profile,) assert response.status_code == 200 assert response.json() == profile - - def test_create_profile_should_return_200_without_social_media(self, profile_only_name): + + def test_create_profile_should_return_200_without_social_media( + self, profile_only_name + ): response = client.post("/profiles/", json=profile_only_name) assert response.status_code == 200 assert response.json() == profile_only_name diff --git a/sucuri/views/__init__.py b/sucuri/views/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/sucuri/views/organization_view.py b/sucuri/views/organization_view.py new file mode 100644 index 0000000..4f49fe4 --- /dev/null +++ b/sucuri/views/organization_view.py @@ -0,0 +1,62 @@ +# Standard Library +from typing import List + +from fastapi import APIRouter +from fastapi import Depends +from fastapi import HTTPException +from sqlalchemy.orm import Session + +# IIB +from sucuri.crud.crud_organization import delete_organization +from sucuri.crud.crud_organization import get_organization +from sucuri.crud.crud_organization import get_organizations +from sucuri.crud.crud_organization import post_organization +from sucuri.crud.crud_organization import update_organization +from sucuri.database.dependency import get_db +from sucuri.models.models import Organization +from sucuri.schemas.schemas import OrganizationSchema + + +router = APIRouter() + + +@router.post("/organizations/", response_model=OrganizationSchema) +async def create_organization( + organization: OrganizationSchema, db: Session = Depends(get_db) +): + return post_organization(organization=organization, db=db) + + +@router.get("/organizations/{organization_id}", response_model=OrganizationSchema) +async def get_organization_by_id(organization_id: int, db: Session = Depends(get_db)): + organization = get_organization(organization_id, db) + if organization is None: + raise HTTPException(status_code=404, detail="Organization not found") + return organization + + +@router.get("/organizations/", response_model=List[OrganizationSchema]) +async def get_all_organizations( + skip: int = 0, limit: int = 100, db: Session = Depends(get_db) +): + organizations = get_organizations(db=db, skip=skip, limit=limit) + return organizations + + +@router.delete("/organizations/{organization_id}", response_model=OrganizationSchema) +async def delete_organization_by_id( + organization_id: int, db: Session = Depends(get_db) +): + return delete_organization(organization_id, db) + + +@router.put("/organizations/{organization_id}", response_model=OrganizationSchema) +async def update_organization_by_id( + organization_id: int, + organization: OrganizationSchema, + db: Session = Depends(get_db), +): + old_organization = get_organization(organization_id, db) + if old_organization is None: + raise HTTPException(status_code=404, detail="Organization not found") + return update_organization(old_organization, organization, db)