From 9da6748fa73e8bc5b764f649dd1a276cab672ca4 Mon Sep 17 00:00:00 2001 From: Eno Bassey Date: Sat, 16 Jan 2021 12:21:17 +0100 Subject: [PATCH] add project, skills and update user test case for duplicate user --- app/blueprints/base_blueprint.py | 2 + app/blueprints/user_employment_blueprint.py | 19 +- app/blueprints/user_project_blueprint.py | 71 ++++ app/controllers/user_employment_controller.py | 15 + app/controllers/user_project_controller.py | 205 +++++++++++ app/models/__init__.py | 5 + app/models/user_employment_skill.py | 1 - app/models/user_project.py | 13 +- app/models/user_project_skill.py | 11 + app/repositories/__init__.py | 2 + app/repositories/user_project_repo.py | 29 ++ app/repositories/user_project_skill_repo.py | 15 + app/utils/security.py | 8 + factories/__init__.py | 1 + factories/user_project_factory.py | 64 ++++ .../test_user_employment_endpoints.py | 1 + .../endpoints/test_user_project_endpoints.py | 235 +++++++++++++ .../unit/controllers/test_user_controller.py | 49 +++ .../test_user_employment_controller.py | 2 - .../test_user_project_controller.py | 326 ++++++++++++++++++ .../repositories/test_user_project_repo.py | 30 ++ .../test_user_project_skill_repo.py | 40 +++ 22 files changed, 1131 insertions(+), 13 deletions(-) create mode 100644 app/blueprints/user_project_blueprint.py create mode 100644 app/controllers/user_project_controller.py create mode 100644 app/models/user_project_skill.py create mode 100644 app/repositories/user_project_repo.py create mode 100644 app/repositories/user_project_skill_repo.py create mode 100644 factories/user_project_factory.py create mode 100644 tests/integration/endpoints/test_user_project_endpoints.py create mode 100644 tests/unit/controllers/test_user_project_controller.py create mode 100644 tests/unit/repositories/test_user_project_repo.py create mode 100644 tests/unit/repositories/test_user_project_skill_repo.py diff --git a/app/blueprints/base_blueprint.py b/app/blueprints/base_blueprint.py index 9395bf4..9999a10 100755 --- a/app/blueprints/base_blueprint.py +++ b/app/blueprints/base_blueprint.py @@ -20,6 +20,7 @@ def register(self): from app.blueprints.activity_blueprint import activity_blueprint from app.blueprints.skills_category_blueprint import skills_category_blueprint from app.blueprints.user_employment_blueprint import user_employment_blueprint + from app.blueprints.user_project_blueprint import user_project_blueprint self.app.register_blueprint(home_blueprint) self.app.register_blueprint(activity_blueprint) @@ -27,3 +28,4 @@ def register(self): self.app.register_blueprint(user_blueprint) self.app.register_blueprint(skills_category_blueprint) self.app.register_blueprint(user_employment_blueprint) + self.app.register_blueprint(user_project_blueprint) diff --git a/app/blueprints/user_employment_blueprint.py b/app/blueprints/user_employment_blueprint.py index 95df5df..24b4802 100644 --- a/app/blueprints/user_employment_blueprint.py +++ b/app/blueprints/user_employment_blueprint.py @@ -49,14 +49,17 @@ def create_user_employment(): @user_employment_blueprint.route("/", methods=["PUT", "PATCH"]) -# @Security.validator([ -# "user_id|required:int", -# "institution_name|required:string", -# "job_title|required:string", -# "start_date|required:date", -# "end_date|required:date", -# "is_current|required", -# ]) +@Security.validator( + [ + "user_id|required:int", + "institution_name|required:string", + "job_title|required:string", + "start_date|required:date", + "end_date|required:date", + "is_current|required", + "skills|optional:list_int", + ] +) # @Auth.has_permission(["update_user_employment_history"]) # @swag_from("documentation/update_user_employment.yml") def update_user_employment(user_employment_id): diff --git a/app/blueprints/user_project_blueprint.py b/app/blueprints/user_project_blueprint.py new file mode 100644 index 0000000..9b6455c --- /dev/null +++ b/app/blueprints/user_project_blueprint.py @@ -0,0 +1,71 @@ +from app.blueprints.base_blueprint import ( + Blueprint, + BaseBlueprint, + request, + Security, + Auth, +) +from app.controllers.user_project_controller import UserProjectController + +url_prefix = "{}/user_project".format(BaseBlueprint.base_url_prefix) +user_project_blueprint = Blueprint("user_project", __name__, url_prefix=url_prefix) +user_project_controller = UserProjectController(request) + + +@user_project_blueprint.route("/user/", methods=["GET"]) +@Auth.has_permission(["view_user_project"]) +# @swag_from('documentation/get_all_user_project.yml') +def list_user_projects(user_id): + return user_project_controller.list_user_projects(user_id) + + +@user_project_blueprint.route("/user-single/", methods=["GET"]) +@Auth.has_permission(["view_user_project"]) +# @swag_from('documentation/get_user_project_by_id.yml') +def get_user_project(user_project_id): + return user_project_controller.get_user_project(user_project_id) + + +@user_project_blueprint.route("/", methods=["POST"]) +@Security.validator( + [ + "user_id|required:int", + "project_name|required:string", + "project_url|optional:string", + "project_description|required:string", + "start_date|required:date", + "end_date|required:date", + "is_current|required", + "skills|optional:list_int", + ] +) +@Auth.has_permission(["create_user_project"]) +# @swag_from('documentation/create_user_project.yml') +def create_user_project(): + return user_project_controller.create_user_project() + + +@user_project_blueprint.route("/", methods=["PUT", "PATCH"]) +@Security.validator( + [ + "user_id|required:int", + "project_name|required:string", + "project_url|optional:string", + "project_description|required:string", + "start_date|required:date", + "end_date|required:date", + "is_current|required", + "skills|optional:list_int", + ] +) +# @Auth.has_permission(["update_user_project"]) +# @swag_from("documentation/update_user_project.yml") +def update_user_project(user_project_id): + return user_project_controller.update_user_project(user_project_id) + + +@user_project_blueprint.route("/", methods=["DELETE"]) +@Auth.has_permission(["delete_user_project"]) +# @swag_from("documentation/delete_user_project.yml") +def delete_user_project(user_project_id): + return user_project_controller.delete_user_project(user_project_id) diff --git a/app/controllers/user_employment_controller.py b/app/controllers/user_employment_controller.py index f00cbcf..19847b7 100644 --- a/app/controllers/user_employment_controller.py +++ b/app/controllers/user_employment_controller.py @@ -89,6 +89,10 @@ def create_user_employment(self): return self.handle_response( "Start Date cannot be greater than End date ", status_code=400 ) + + if skills is not None: + self._validate_skills(skills) + user_employment = self.user_employment_repo.new_user_employment( user_id=user_id, institution_name=institution_name, @@ -109,6 +113,15 @@ def create_user_employment(self): except Exception as e: return self.handle_response("Error processing: " + str(e), status_code=400) + def _validate_skills(self, skills): + for skill in skills: + skill_data = self.skill_repo.find_first(id=skill) + if skill_data is None: + return self.handle_response( + "One of the skills is invalid", status_code=400 + ) + return + def _process_skills(self, user_employment_id, skills): skills_dict = [] @@ -150,6 +163,8 @@ def update_user_employment(self, update_id): return self.handle_response( "Invalid or incorrect user_employment_id provided", status_code=400 ) + if skills is not None: + self._validate_skills(skills) user_employment = self.user_employment_repo.get(user_employment_id) diff --git a/app/controllers/user_project_controller.py b/app/controllers/user_project_controller.py new file mode 100644 index 0000000..9d149b0 --- /dev/null +++ b/app/controllers/user_project_controller.py @@ -0,0 +1,205 @@ +import datetime + +from app.controllers.base_controller import BaseController +from app.repositories import SkillRepo, UserProjectSkillRepo, UserProjectRepo, UserRepo + + +class UserProjectController(BaseController): + def __init__(self, request): + BaseController.__init__(self, request) + self.user_repo = UserRepo() + self.user_project_repo = UserProjectRepo() + self.user_project_skill_repo = UserProjectSkillRepo() + self.skill_repo = SkillRepo() + + def _get_project_skills(self, project_id): + skills_list = [] + skills = self.user_project_skill_repo.get_unpaginated( + user_project_id=project_id + ) + for skill in skills: + skill_data = self.skill_repo.find_first(id=skill) + skill_dict = skill_data.serialize() + skill_dict["name"] = skill_data.name + skills_list.append(skill_dict) + return skills_list + + def list_user_projects(self, user_id): + user_projects = self.user_project_repo.get_unpaginated(user_id=user_id) + + user_project_list = [] + for user_project in user_projects: + user_project_dict = user_project.serialize() + user_project_dict["skills"] = self._get_project_skills(user_project.id) + user_project_list.append(user_project_dict) + return self.handle_response( + "OK", + payload={ + "user_projects": user_project_list, + }, + ) + + def get_user_project(self, user_project_id): + user_project = self.user_project_repo.get(user_project_id) + + if user_project: + user_project_dict = user_project.serialize() + user_project_dict["skills"] = self._get_project_skills(user_project.id) + return self.handle_response( + "OK", payload={"user_project": user_project_dict} + ) + return self.handle_response( + "Invalid User Project or Missing user_project_id", status_code=400 + ) + + def create_user_project(self): + ( + user_id, + project_name, + project_url, + project_description, + start_date, + end_date, + is_current, + skills, + ) = self.request_params( + "user_id", + "project_name", + "project_url", + "project_description", + "start_date", + "end_date", + "is_current", + "skills", + ) + try: + + if not isinstance(start_date, datetime.date): + start_date = datetime.datetime.strptime(start_date, "%Y-%m-%d") + end_date = datetime.datetime.strptime(end_date, "%Y-%m-%d") + + if start_date > end_date: + return self.handle_response( + "Start Date cannot be greater than End date ", status_code=400 + ) + if skills is not None: + self._validate_skills(skills) + + user_project = self.user_project_repo.new_user_project( + user_id=user_id, + project_name=project_name, + project_url=project_url, + project_description=project_description, + start_date=start_date, + end_date=end_date, + is_current=is_current, + ) + skills_dict = self._process_skills(user_project.id, skills) + + user_project_serialized = user_project.serialize() + user_project_serialized["skills"] = skills_dict + return self.handle_response( + "OK", + payload={"user_project": user_project_serialized}, + status_code=201, + ) + except Exception as e: + return self.handle_response("Error processing: " + str(e), status_code=400) + + def _validate_skills(self, skills): + for skill in skills: + skill_data = self.skill_repo.find_first(id=skill) + if skill_data is None: + return self.handle_response( + "One of the skills is invalid", status_code=400 + ) + return + + def _process_skills(self, user_project_id, skills): + skills_dict = [] + + if skills is not None: + + for skill in skills: + user_project_skills = ( + self.user_project_skill_repo.new_user_project_skill( + user_project_id=user_project_id, skill_id=skill + ) + ) + skill_data = self.skill_repo.find_first(id=skill) + user_project_skill_dict = user_project_skills.serialize() + user_project_skill_dict["name"] = skill_data.name + skills_dict.append(user_project_skill_dict) + return skills_dict + + def update_user_project(self, update_id): + ( + user_id, + user_project_id, + project_name, + project_url, + project_description, + start_date, + end_date, + is_current, + skills, + ) = self.request_params( + "user_id", + "user_project_id", + "project_name", + "project_url", + "project_description", + "start_date", + "end_date", + "is_current", + "skills", + ) + if update_id != user_project_id: + return self.handle_response( + "Invalid or incorrect user_project_id provided", status_code=400 + ) + + if skills is not None: + self._validate_skills(skills) + + user_project = self.user_project_repo.get(user_project_id) + + if not isinstance(start_date, datetime.date): + start_date = datetime.datetime.strptime(start_date, "%Y-%m-%d") + end_date = datetime.datetime.strptime(end_date, "%Y-%m-%d") + + if user_project: + updates = { + "project_name": project_name, + "project_url": project_url, + "project_description": project_description, + "start_date": start_date, + "end_date": end_date, + "is_current": is_current, + } + + self.user_project_repo.update(user_project, **updates) + skills_dict = self._process_skills(user_project.id, skills) + user_project_serialized = user_project.serialize() + user_project_serialized["skills"] = skills_dict + return self.handle_response( + "OK", + payload={"user_project": user_project_serialized}, + ) + + return self.handle_response( + "Invalid or incorrect user_project_id provided", status_code=400 + ) + + def delete_user_project(self, user_project_id): + user_project = self.user_project_repo.get(user_project_id) + + if user_project: + updates = {"is_deleted": True} + self.user_project_repo.update(user_project, **updates) + return self.handle_response( + "user project deleted", payload={"status": "success"} + ) + return self.handle_response( + "Invalid or incorrect user_project_id provided", status_code=404 + ) diff --git a/app/models/__init__.py b/app/models/__init__.py index 4845b51..cb7fd7f 100755 --- a/app/models/__init__.py +++ b/app/models/__init__.py @@ -12,6 +12,7 @@ from .user_employment import UserEmployment from .user_employment_skill import UserEmploymentSkill from .user_project import UserProject +from .user_project_skill import UserProjectSkill from .user_skills import UserSkills from .location import Location @@ -25,6 +26,7 @@ "Skill", "UserSkills", "UserProject", + "UserProjectSkill", "UserEmploymentSkill", "UserEmployment", "UserEducation", @@ -38,6 +40,7 @@ UserEmployment, UserEmploymentSkill, UserProject, + UserProjectSkill, UserSkills, Role, Permission, @@ -52,6 +55,7 @@ UserEmployment, UserEmploymentSkill, UserProject, + UserProjectSkill, UserSkills, Role, Permission, @@ -66,6 +70,7 @@ UserEmployment, UserEmploymentSkill, UserProject, + UserProjectSkill, UserSkills, Role, Permission, diff --git a/app/models/user_employment_skill.py b/app/models/user_employment_skill.py index b084f26..5acb188 100644 --- a/app/models/user_employment_skill.py +++ b/app/models/user_employment_skill.py @@ -1,5 +1,4 @@ from .base_model import BaseModel, db -from app.utils.enums import Gender class UserEmploymentSkill(BaseModel): diff --git a/app/models/user_project.py b/app/models/user_project.py index aaed4e0..f854916 100644 --- a/app/models/user_project.py +++ b/app/models/user_project.py @@ -6,5 +6,14 @@ class UserProject(BaseModel): __tablename__ = "user_projects" - gender = db.Column(db.Enum(Gender)) - date_of_birth = db.Column(db.Date(), nullable=False) + project_name = db.Column(db.String, nullable=False) + project_url = db.Column(db.String, nullable=False) + project_description = db.Column(db.Text, nullable=False) + start_date = db.Column(db.Date(), nullable=False) + end_date = db.Column(db.Date(), nullable=False) + user_id = db.Column(db.Integer(), db.ForeignKey("users.id")) + user = db.relationship("User", lazy=False) + is_current = db.Column(db.Boolean, default=False, nullable=True) + skills = db.relationship( + "UserProjectSkill", backref="user_project_skills", lazy=True + ) diff --git a/app/models/user_project_skill.py b/app/models/user_project_skill.py new file mode 100644 index 0000000..33cfb64 --- /dev/null +++ b/app/models/user_project_skill.py @@ -0,0 +1,11 @@ +from .base_model import BaseModel, db + + +class UserProjectSkill(BaseModel): + + __tablename__ = "user_project_skills" + + user_project_id = db.Column(db.Integer(), db.ForeignKey("user_projects.id")) + user_project = db.relationship("UserProject", lazy=False) + skill_id = db.Column(db.Integer(), db.ForeignKey("skills.id")) + skill = db.relationship("Skill", lazy=False) diff --git a/app/repositories/__init__.py b/app/repositories/__init__.py index 734158d..65b91fe 100755 --- a/app/repositories/__init__.py +++ b/app/repositories/__init__.py @@ -8,3 +8,5 @@ from .skill_repo import SkillRepo from .user_employment_repo import UserEmploymentRepo from .user_employment_skill_repo import UserEmploymentSkillRepo +from .user_project_repo import UserProjectRepo +from .user_project_skill_repo import UserProjectSkillRepo diff --git a/app/repositories/user_project_repo.py b/app/repositories/user_project_repo.py new file mode 100644 index 0000000..19eef24 --- /dev/null +++ b/app/repositories/user_project_repo.py @@ -0,0 +1,29 @@ +from app.repositories.base_repo import BaseRepo +from app.models.user_project import UserProject + + +class UserProjectRepo(BaseRepo): + def __init__(self): + BaseRepo.__init__(self, UserProject) + + def new_user_project( + self, + user_id, + project_name, + project_url, + project_description, + start_date, + end_date, + is_current, + ): + user_project = UserProject( + project_name=project_name, + project_url=project_url, + project_description=project_description, + start_date=start_date, + end_date=end_date, + user_id=user_id, + is_current=is_current, + ) + user_project.save() + return user_project diff --git a/app/repositories/user_project_skill_repo.py b/app/repositories/user_project_skill_repo.py new file mode 100644 index 0000000..40b3b26 --- /dev/null +++ b/app/repositories/user_project_skill_repo.py @@ -0,0 +1,15 @@ +from app.repositories.base_repo import BaseRepo +from app.models.user_project_skill import UserProjectSkill + + +class UserProjectSkillRepo(BaseRepo): + def __init__(self): + BaseRepo.__init__(self, UserProjectSkill) + + def new_user_project_skill(self, user_project_id, skill_id): + user_project_skill = UserProjectSkill( + user_project_id=user_project_id, + skill_id=skill_id, + ) + user_project_skill.save() + return user_project_skill diff --git a/app/utils/security.py b/app/utils/security.py index c4917f5..7d7e123 100755 --- a/app/utils/security.py +++ b/app/utils/security.py @@ -268,6 +268,9 @@ def decorated(*args, **kwargs): column_name = rule_array[3] rep = "app.repositories.{}_repo".format(repo_name) + import pdb + + pdb.set_trace() mod = importlib.import_module(rep) repo_class = getattr( mod, "{}Repo".format(to_pascal_case(repo_name)) @@ -534,6 +537,11 @@ def decorated(*args, **kwargs): column_name = rule_array[3] rep = "app.repositories.{}_repo".format(repo_name) + print("rep =>", rep) + import pdb + + pdb.set_trace() + mod = importlib.import_module(rep) repo_class = getattr( mod, "{}Repo".format(to_pascal_case(repo_name)) diff --git a/factories/__init__.py b/factories/__init__.py index 057332b..a52963b 100755 --- a/factories/__init__.py +++ b/factories/__init__.py @@ -10,3 +10,4 @@ SkillFactoryFake, ) from .user_employment_factory import UserEmploymentFactory, UserEmploymentFactoryFake +from .user_project_factory import UserProjectFactory, UserProjectFactoryFake diff --git a/factories/user_project_factory.py b/factories/user_project_factory.py new file mode 100644 index 0000000..1702722 --- /dev/null +++ b/factories/user_project_factory.py @@ -0,0 +1,64 @@ +import factory +from faker import Faker +from faker.providers import internet, company, job, date_time, lorem + +from app.models import UserProject, UserProjectSkill +from app.utils import db +from factories.skill_category_factory import SkillFactory +from factories.user_factory import UserFactory + +fake = Faker() +fake.add_provider(internet) +fake.add_provider(company) +fake.add_provider(job) +fake.add_provider(date_time) +fake.add_provider(lorem) + + +class UserProjectFactory(factory.alchemy.SQLAlchemyModelFactory): + class Meta: + model = UserProject + sqlalchemy_session = db.session + + user = factory.SubFactory(UserFactory) + user_id = factory.SelfAttribute("user.id") + project_name = fake.company() + project_url = fake.uri() + project_description = fake.paragraph(nb_sentences=5) + start_date = fake.date_between() + end_date = fake.date_between() + + +class UserProjectFactoryFake(factory.Factory): + class Meta: + model = UserProject + + user = factory.SubFactory(UserFactory) + user_id = factory.SelfAttribute("user.id") + project_name = fake.company() + project_url = fake.uri() + project_description = fake.paragraph(nb_sentences=5) + start_date = fake.date_between() + end_date = fake.date_between() + is_current = False + + +class UserProjectSkillFactory(factory.alchemy.SQLAlchemyModelFactory): + class Meta: + model = UserProjectSkill + sqlalchemy_session = db.session + + user_project = factory.SubFactory(UserProjectFactory) + user_project_id = factory.SelfAttribute("user_project.id") + skill = factory.SubFactory(SkillFactory) + skill_id = factory.SelfAttribute("skill.id") + + +class UserProjectSkillFactoryFake(factory.Factory): + class Meta: + model = UserProjectSkill + + user_project = factory.SubFactory(UserProjectFactory) + user_project_id = factory.SelfAttribute("user_project.id") + skill = factory.SubFactory(SkillFactory) + skill_id = factory.SelfAttribute("skill.id") diff --git a/tests/integration/endpoints/test_user_employment_endpoints.py b/tests/integration/endpoints/test_user_employment_endpoints.py index 7272953..84a65b6 100644 --- a/tests/integration/endpoints/test_user_employment_endpoints.py +++ b/tests/integration/endpoints/test_user_employment_endpoints.py @@ -42,6 +42,7 @@ def test_create_user_employment_endpoint(self): headers=self.headers(), ) response_json = self.decode_from_json_string(response.data.decode("utf-8")) + print(response_json) payload = response_json["payload"] self.assertEqual(response.status_code, 201) diff --git a/tests/integration/endpoints/test_user_project_endpoints.py b/tests/integration/endpoints/test_user_project_endpoints.py new file mode 100644 index 0000000..b3aa5f3 --- /dev/null +++ b/tests/integration/endpoints/test_user_project_endpoints.py @@ -0,0 +1,235 @@ +from datetime import datetime, date + +from factories.skill_category_factory import CategoryWithSkillsFactory +from tests.base_test_case import BaseTestCase +from factories import ( + UserProjectFactory, + UserProjectFactoryFake, + PermissionFactory, + UserRoleFactory, + RoleFactory, + UserFactory, +) + + +class TestUserProjectEndpoints(BaseTestCase): + def setUp(self): + self.BaseSetUp() + # self.skill_category = CategoryWithSkillsFactory.create(skills=4) + # self.skill_category.save() + # self.skill_one = self.skill_category.skills[0] + # self.skill_two = self.skill_category.skills[1] + # self.skill_three = self.skill_category.skills[2] + # self.skill_four = self.skill_category.skills[3] + + def tearDown(self): + self.BaseTearDown() + + def test_create_user_project_endpoint(self): + role1 = RoleFactory.create(name="admin") + user_id = BaseTestCase.user_id() + PermissionFactory.create(keyword="create_user_project", role=role1) + UserRoleFactory.create(user_id=user_id, role=role1) + user_proj_data = UserProjectFactoryFake.build( + start_date=date(year=2018, month=1, day=31), + end_date=date(year=2028, month=1, day=31), + ) + data = { + "user_id": user_id, + "project_name": user_proj_data.project_name, + "project_url": user_proj_data.project_url, + "project_description": user_proj_data.project_description, + "start_date": str(user_proj_data.start_date), + "end_date": str(user_proj_data.end_date), + "is_current": user_proj_data.is_current, + } + + response = self.client().post( + self.make_url("/user_project/"), + data=self.encode_to_json_string(data), + headers=self.headers(), + ) + print(response) + response_json = self.decode_from_json_string(response.data.decode("utf-8")) + print(response_json) + payload = response_json["payload"] + + self.assertEqual(response.status_code, 201) + self.assertJSONKeyPresent(response_json, "payload") + self.assertEqual( + payload["user_project"]["project_name"], + user_proj_data.project_name, + ) + self.assertEqual( + payload["user_project"]["project_url"], user_proj_data.project_url + ) + + def test_list_user_project_endpoint(self): + role1 = RoleFactory.create(name="admin") + BaseTestCase.user_id() + + PermissionFactory.create(keyword="view_user_project", role=role1) + user = UserFactory.create() + user.save() + UserRoleFactory.create(user=user, role=role1) + UserProjectFactory.create_batch(3, user=user) + + response = self.client().get( + self.make_url(f"/user_project/user/{user.id}"), + headers=self.headers(), + ) + response_json = self.decode_from_json_string(response.data.decode("utf-8")) + payload = response_json["payload"] + self.assert200(response) + self.assertEqual(len(payload["user_projects"]), 3) + self.assertJSONKeysPresent( + payload["user_projects"][0], "project_name", "project_url" + ) + + def test_get_specific_user_project_endpoint(self): + role = RoleFactory.create() + user_id = BaseTestCase.user_id() + + PermissionFactory.create(keyword="view_user_project", role=role) + UserRoleFactory.create(user_id=user_id, role=role) + + user_project = UserProjectFactory.create() + user_project.save() + response = self.client().get( + self.make_url("/user_project/user-single/{}".format(user_project.id)), + headers=self.headers(), + ) + response_json = self.decode_from_json_string(response.data.decode("utf-8")) + + payload = response_json["payload"] + + self.assert200(response) + self.assertJSONKeyPresent(payload, "user_project") + self.assertJSONKeysPresent( + payload["user_project"], "project_name", "project_url" + ) + self.assertEqual(payload["user_project"]["id"], user_project.id) + self.assertEqual( + payload["user_project"]["project_name"], + user_project.project_name, + ) + self.assertEqual( + payload["user_project"]["project_url"], user_project.project_url + ) + + def test_update_user_project_endpoint(self): + RoleFactory.create() + role1 = RoleFactory.create(name="admin") + user_id = BaseTestCase.user_id() + + PermissionFactory.create(keyword="update_user_project", role=role1) + UserRoleFactory.create(user_id=user_id, role=role1) + user_proj_data = UserProjectFactory.create() + user_proj_data.save() + data = { + "user_id": user_id, + "user_project_id": user_proj_data.id, + "project_name": user_proj_data.project_name, + "project_url": user_proj_data.project_url, + "project_description": user_proj_data.project_description, + "start_date": str(user_proj_data.start_date), + "end_date": str(user_proj_data.end_date), + "is_current": user_proj_data.is_current, + } + response = self.client().put( + self.make_url("/user_project/{}".format(user_proj_data.id)), + data=self.encode_to_json_string(data), + headers=self.headers(), + ) + response_json = self.decode_from_json_string(response.data.decode("utf-8")) + payload = response_json["payload"] + + self.assert200(response) + self.assertEqual(payload["user_project"]["project_name"], data["project_name"]) + + def test_invalid_update(self): + role1 = RoleFactory.create(name="admin") + user_id = BaseTestCase.user_id() + + PermissionFactory.create(keyword="update_user_project", role=role1) + UserRoleFactory.create(user_id=user_id, role=role1) + + user_proj_data = UserProjectFactory.create() + user_proj_data.save() + + data = { + "user_id": user_id, + "user_project_id": user_proj_data.id, + "project_name": user_proj_data.project_name, + "project_url": user_proj_data.project_url, + "start_date": str(user_proj_data.start_date), + "end_date": str(user_proj_data.end_date), + "is_current": user_proj_data.is_current, + } + response = self.client().put( + self.make_url("/user_project/1000"), + data=self.encode_to_json_string(data), + headers=self.headers(), + ) + self.assert400(response) + + def test_delete_user_project_endpoint_with_right_permission(self): + role = RoleFactory.create() + user_id = BaseTestCase.user_id() + + PermissionFactory.create(keyword="delete_user_project", role=role) + UserRoleFactory.create(user_id=user_id, role=role) + user_proj_data = UserProjectFactory.create() + user_proj_data.save() + + response = self.client().delete( + self.make_url(f"/user_project/{user_proj_data.id}"), + headers=self.headers(), + ) + response_json = self.decode_from_json_string(response.data.decode("utf-8")) + payload = response_json["payload"] + + self.assert200(response) + self.assertEqual(payload["status"], "success") + self.assertEqual(response_json["msg"], "user project deleted") + + def test_delete_user_project_endpoint_without_right_permission(self): + role = RoleFactory.create() + user_id = BaseTestCase.user_id() + + UserRoleFactory.create(user_id=user_id, role=role) + user_proj_data = UserProjectFactory.create() + user_proj_data.save() + + response = self.client().delete( + self.make_url(f"/user_project/{user_proj_data.id}"), + headers=self.headers(), + ) + response_json = self.decode_from_json_string(response.data.decode("utf-8")) + + self.assert401(response) + self.assertEqual(response_json["msg"], "Access Error - No Permission Granted") + + def test_delete_user_project_endpoint_with_wrong_user_project_id(self): + role1 = RoleFactory.create(name="admin") + + user_id = BaseTestCase.user_id() + PermissionFactory.create( + keyword="delete_user_project", + name="delete_user_project", + role=role1, + ) + + UserRoleFactory.create(user_id=user_id, role=role1) + user_proj_data = UserProjectFactory.create() + user_proj_data.save() + + response = self.client().delete( + self.make_url("/user_project/1576"), headers=self.headers() + ) + response_json = self.decode_from_json_string(response.data.decode("utf-8")) + + self.assert404(response) + self.assertEqual( + response_json["msg"], "Invalid or incorrect user_project_id provided" + ) diff --git a/tests/unit/controllers/test_user_controller.py b/tests/unit/controllers/test_user_controller.py index adf82a1..cb02f63 100755 --- a/tests/unit/controllers/test_user_controller.py +++ b/tests/unit/controllers/test_user_controller.py @@ -44,6 +44,7 @@ def setUp(self): last_name="test", gender="male", password="test", + email="user1@user.com", is_active=True, is_deleted=False, created_at=datetime.now(), @@ -55,6 +56,7 @@ def setUp(self): last_name="test", gender="male", password="test", + email="user2@user.com", is_active=True, is_deleted=False, created_at=datetime.now(), @@ -212,3 +214,50 @@ def test_list_user_when_user_found_succeeds( response = user_controller.list_user(id=1) self.assertEqual(response.status_code, 200) + + @patch.object(Auth, "get_location") + @patch.object(UserController, "request_params") + @patch.object(RoleRepo, "find_first") + @patch.object(UserRepo, "exists") + # @patch.object(UserRepo, "new_user") + @patch.object(UserRoleRepo, "new_user_role") + def test_create_user_fails_for_existing_user( + self, + mock_user_role_repo_new_user_role, + # mock_user_repo_new_user, + mock_user_repo_exists, + mock_role_repo_find_first, + mock_request_params, + mock_get_location, + ): + location = LocationFactory() + role = RoleFactory(name="test_role") + + with self.app.app_context(): + mock_get_location.return_value = location.id + mock_role_repo_find_first.return_value = self.mock_role + mock_user_repo_exists.return_value = self.mock_user2 + # mock_user_repo_new_user.return_value = None + mock_user_role_repo_new_user_role.return_value = self.mock_user_role + mock_request_params.return_value = [ + "Joseph", + "Serunjogi", + self.mock_user2.email, + role.id, + "male", + str(datetime.now()), + 1, + "password", + ] + user_controller = UserController(self.request_context) + + # Act + result = user_controller.create_user() + print(result) + print(result.get_json()) + # Assert + assert result.status_code == 400 + assert ( + result.get_json()["msg"] + == f"User with email '{self.mock_user2.email}' already exists" + ) diff --git a/tests/unit/controllers/test_user_employment_controller.py b/tests/unit/controllers/test_user_employment_controller.py index 1766d89..5001411 100644 --- a/tests/unit/controllers/test_user_employment_controller.py +++ b/tests/unit/controllers/test_user_employment_controller.py @@ -20,8 +20,6 @@ def setUp(self): self.BaseSetUp() self.skill_category = CategoryWithSkillsFactory.create(skills=4) self.skill_category.save() - print(self.skill_category) - print(self.skill_category.__dict__) self.skill_one = self.skill_category.skills[0] self.skill_two = self.skill_category.skills[1] self.skill_three = self.skill_category.skills[2] diff --git a/tests/unit/controllers/test_user_project_controller.py b/tests/unit/controllers/test_user_project_controller.py new file mode 100644 index 0000000..5879dff --- /dev/null +++ b/tests/unit/controllers/test_user_project_controller.py @@ -0,0 +1,326 @@ +""" +Unit tests for the User Project Controller. +""" +from datetime import datetime, date +from unittest.mock import patch + +from app.controllers.user_project_controller import UserProjectController +from app.models import User, UserProject, UserProjectSkill +from app.repositories.user_project_repo import UserProjectRepo +from factories.skill_category_factory import ( + CategoryWithSkillsFactory, + SkillFactory, + SkillFactoryFake, +) +from tests.base_test_case import BaseTestCase + + +class TestUserProjectController(BaseTestCase): + def setUp(self): + self.BaseSetUp() + self.skill_category = CategoryWithSkillsFactory.create(skills=4) + self.skill_category.save() + self.skill_one = self.skill_category.skills[0] + self.skill_two = self.skill_category.skills[1] + self.skill_three = self.skill_category.skills[2] + self.skill_four = self.skill_category.skills[3] + self.mock_user = User( + id=1, + first_name="test", + last_name="test", + gender="male", + password="test", + is_active=True, + is_deleted=False, + created_at=datetime.now(), + updated_at=datetime.now(), + ) + self.mock_user_project = UserProject( + id=1, + project_name="InstitutionName", + project_url="InstitutionName", + project_description="InstitutionName", + start_date=date(year=2018, month=1, day=31), + end_date=date(year=2020, month=1, day=31), + user_id=self.mock_user.id, + is_current=False, + created_at=datetime.now(), + updated_at=datetime.now(), + ) + self.mock_user_project_skill = UserProjectSkill( + user_project_id=self.mock_user_project.id, skill_id=self.skill_one.id + ) + + def tearDown(self): + self.BaseTearDown() + + @patch.object(UserProjectRepo, "get_unpaginated") + def test_list_user_projects_ok_response( + self, + mock_user_project_repo_get_unpaginated, + ): + """Test list_user_projects OK response.""" + # Arrange + with self.app.app_context(): + mock_user_project_repo_get_unpaginated.return_value.items = [ + self.mock_user_project, + ] + user_project_controller = UserProjectController(self.request_context) + + # Act + result = user_project_controller.list_user_projects(1) + + # Assert + assert result.status_code == 200 + assert result.get_json()["msg"] == "OK" + + @patch.object(UserProjectRepo, "get") + def test_get_user_project_when_invalid_or_missing(self, mock_user_project_repo_get): + """Test get_user_project invalid repo response.""" + # Arrange + with self.app.app_context(): + mock_user_project_repo_get.return_value = None + user_project_controller = UserProjectController(self.request_context) + + # Act + result = user_project_controller.get_user_project(1) + + # Assert + assert result.status_code == 400 + assert ( + result.get_json()["msg"] + == "Invalid User Project or Missing user_project_id" + ) + + @patch.object(UserProjectRepo, "get") + def test_get_user_project_ok_response(self, mock_user_project_repo_get): + """Test get_user_project OK response.""" + # Arrange + with self.app.app_context(): + mock_user_project_repo_get.return_value = self.mock_user_project + user_project_controller = UserProjectController(self.request_context) + + # Act + result = user_project_controller.get_user_project(1) + + # Assert + assert result.status_code == 200 + assert result.get_json()["msg"] == "OK" + # import pdb + # pdb.set_trace() + # assert result.get_json()["payload"]["user_project"]["skills"][0] == "OK" + # assert result.get_json()["msg"] == "OK" + + @patch.object(UserProjectController, "request_params") + def test_create_user_project_start_date_less_than_end_date_response( + self, + mock_user_project_controller_request_params, + ): + """ + Test create user project is invalid when start date is greater than end date + + :param mock_user_project_controller_request_params: + :return: + """ + with self.app.app_context(): + mock_user_project_controller_request_params.return_value = ( + 1, + "Institution name", + "http://www.test.com", + "Job title", + date(year=2028, month=1, day=31), + date(year=2020, month=1, day=31), + False, + [self.skill_one.id, self.skill_two.id], + ) + user_project_controller = UserProjectController(self.request_context) + + # Act + result = user_project_controller.create_user_project() + + # Assert + assert result.status_code == 400 + assert ( + result.get_json()["msg"] + == "Start Date cannot be greater than End date " + ) + + @patch.object(UserProjectController, "request_params") + @patch.object(UserProjectRepo, "find_first") + def test_create_user_project_ok_response( + self, + mock_user_project_repo_find_first, + mock_user_project_controller_request_params, + ): + """Test create_user_project OK response.""" + # Arrange + with self.app.app_context(): + mock_user_project_controller_request_params.return_value = ( + 1, + "Institution name", + "http://www.test.com", + "Job title", + date(year=2018, month=1, day=31), + date(year=2020, month=1, day=31), + False, + [self.skill_one.id, self.skill_two.id], + ) + mock_user_project_repo_find_first.return_value = None + user_project_controller = UserProjectController(self.request_context) + + # Act + result = user_project_controller.create_user_project() + + # Assert + assert result.status_code == 201 + assert result.get_json()["msg"] == "OK" + assert ( + result.get_json()["payload"]["user_project"]["skills"][0]["name"] + == self.skill_one.name + ) + + @patch.object(UserProjectController, "request_params") + @patch.object(UserProjectRepo, "get") + def test_update_user_project_when_user_project_doesnot_exist( + self, + mock_user_project_repo_get, + mock_user_project_controller_request_params, + ): + """Test update_user_project when role doesn't exist.""" + # Arrange + with self.app.app_context(): + mock_user_project_repo_get.return_value = None + mock_user_project_controller_request_params.return_value = ( + None, + None, + None, + None, + None, + None, + None, + None, + None, + ) + user_project_controller = UserProjectController(self.request_context) + + # Act + result = user_project_controller.update_user_project(1) + + # Assert + assert result.status_code == 400 + assert ( + result.get_json()["msg"] == "Invalid or incorrect " + "user_project_id provided" + ) + + @patch.object(UserProjectRepo, "find_first") + @patch.object(UserProjectController, "request_params") + @patch.object(UserProjectRepo, "get") + def test_update_user_project_ok_response( + self, + mock_user_project_repo_get, + mock_user_project_controller_request_params, + mock_user_project_repo_find_first, + ): + """Test update_user_project when role doesn't exist.""" + # Arrange + with self.app.app_context(): + mock_user_project_repo_get.return_value = self.mock_user_project + mock_user_project_repo_find_first.return_value = None + mock_user_project_controller_request_params.return_value = ( + 1, + 1, + "Institution name", + "http://www.test.com", + "Job title", + date(year=2018, month=1, day=31), + date(year=2020, month=1, day=31), + True, + [self.skill_one.id, self.skill_two.id], + ) + user_project_controller = UserProjectController(self.request_context) + + # Act + result = user_project_controller.update_user_project(1) + + # Assert + assert result.status_code == 200 + assert result.get_json()["msg"] == "OK" + assert ( + result.get_json()["payload"]["user_project"]["skills"][0]["name"] + == self.skill_one.name + ) + + @patch.object(UserProjectRepo, "get") + def test_delete_user_project_when_user_project_is_invalid( + self, mock_user_project_repo_get + ): + """Test delete_user_project when the role is invalid.""" + # Arrange + with self.app.app_context(): + mock_user_project_repo_get.return_value = None + user_project_controller = UserProjectController(self.request_context) + + # Act + result = user_project_controller.delete_user_project(1) + + # Assert + assert result.status_code == 404 + assert ( + result.get_json()["msg"] == "Invalid or incorrect " + "user_project_id provided" + ) + + @patch.object(UserProjectRepo, "get") + @patch.object(UserProjectRepo, "update") + def test_delete_user_project_ok_response( + self, mock_user_project_repo_update, mock_user_project_repo_get + ): + """Test delete_user_project when the role is invalid.""" + # Arrange + with self.app.app_context(): + mock_user_project_repo_get.return_value = self.mock_user_project + mock_user_project_repo_update.return_value = self.mock_user_project + user_project_controller = UserProjectController(self.request_context) + + # Act + result = user_project_controller.delete_user_project(1) + + # Assert + assert result.status_code == 200 + assert result.get_json()["msg"] == "user project deleted" + + @patch.object(UserProjectController, "request_params") + @patch.object(UserProjectRepo, "find_first") + def test_user_project_create_with_skills_valid( + self, + mock_user_project_repo_find_first, + mock_user_project_controller_request_params, + ): + """ + Test create_user_project with skills OK response. + """ + # Arrange + with self.app.app_context(): + mock_user_project_controller_request_params.return_value = ( + 1, + "Institution name", + "http://www.test.com", + "Job title", + date(year=2018, month=1, day=31), + date(year=2020, month=1, day=31), + False, + [self.skill_one.id, self.skill_two.id], + ) + mock_user_project_repo_find_first.return_value = None + user_project_controller = UserProjectController(self.request_context) + + # Act + result = user_project_controller.create_user_project() + + # Assert + assert result.status_code == 201 + assert result.get_json()["msg"] == "OK" + + def test_user_project_create_with_skills_invalid_skills(self): + pass diff --git a/tests/unit/repositories/test_user_project_repo.py b/tests/unit/repositories/test_user_project_repo.py new file mode 100644 index 0000000..e7a439f --- /dev/null +++ b/tests/unit/repositories/test_user_project_repo.py @@ -0,0 +1,30 @@ +from app.models import UserProject +from tests.base_test_case import BaseTestCase +from factories.user_project_factory import UserProjectFactoryFake +from factories.user_factory import UserFactory +from app.repositories.user_project_repo import UserProjectRepo + + +class TestUserProjectRepo(BaseTestCase): + def setUp(self): + self.BaseSetUp() + self.repo = UserProjectRepo() + + def tearDown(self): + self.BaseTearDown() + + def test_new_user_project_method_returns_new_user_project_object(self): + user = UserFactory.create() + user.save() + user_project = UserProjectFactoryFake.build(user=user, user_id=user.id) + new_user_project = self.repo.new_user_project( + user_id=user.id, + project_name=user_project.project_name, + project_url=user_project.project_url, + project_description=user_project.project_description, + start_date=user_project.start_date, + end_date=user_project.end_date, + is_current=False, + ) + self.assertIsInstance(new_user_project, UserProject) + self.assertEqual(str(new_user_project.user_id), str(user.id)) diff --git a/tests/unit/repositories/test_user_project_skill_repo.py b/tests/unit/repositories/test_user_project_skill_repo.py new file mode 100644 index 0000000..4639f0c --- /dev/null +++ b/tests/unit/repositories/test_user_project_skill_repo.py @@ -0,0 +1,40 @@ +from app.models import UserProjectSkill +from tests.base_test_case import BaseTestCase +from factories.user_project_factory import ( + UserProjectSkillFactoryFake, + UserProjectFactory, + SkillFactory, +) +from factories.user_factory import UserFactory +from app.repositories.user_project_skill_repo import UserProjectSkillRepo + + +class TestUserProjectSkillRepo(BaseTestCase): + def setUp(self): + self.BaseSetUp() + self.repo = UserProjectSkillRepo() + + def tearDown(self): + self.BaseTearDown() + + def test_new_user_project_skill_method_returns_new_user_project_skill_object( + self, + ): + user = UserFactory.create() + user.save() + + user_project = UserProjectFactory.create() + user_project.save() + + skill = SkillFactory() + skill.save() + + user_project_skill = UserProjectSkillFactoryFake.build( + user_project_id=user_project.id, skill_id=skill.id + ) + new_user_project_skill = self.repo.new_user_project_skill( + user_project_id=user_project_skill.user_project_id, + skill_id=user_project_skill.skill_id, + ) + self.assertIsInstance(new_user_project_skill, UserProjectSkill) + self.assertEqual(str(new_user_project_skill.skill_id), str(skill.id))