diff --git a/assets/database.json b/assets/database.json index 990694e..de5d4ce 100644 --- a/assets/database.json +++ b/assets/database.json @@ -14,12 +14,18 @@ "name": "Gabe Lewis", "dept": "C-Level", "role": "CEO" + }, + "pam@dundermifflin.com": { + "name": "Pam Beasly", + "dept": "General", + "role": "Recepcionist" } }, "balance": { "jim@dundermifflin.com": 653, "schrute@dundermifflin.com": 253, - "glewis@dundermifflin.com": 200 + "glewis@dundermifflin.com": 200, + "pam@dundermifflin.com": 500 }, "movement": { "jim@dundermifflin.com": [ @@ -72,6 +78,16 @@ "date": "2024-07-01T10:29:00.532653", "actor": "solermvictor", "value": 100 + }, + { + "date": "2024-07-19T14:41:20.573189", + "actor": "solermvictor", + "value": 5 + }, + { + "date": "2024-07-19T14:42:12.498918", + "actor": "solermvictor", + "value": -5 } ], "schrute@dundermifflin.com": [ @@ -124,6 +140,16 @@ "date": "2024-07-01T10:29:00.532668", "actor": "solermvictor", "value": 100 + }, + { + "date": "2024-07-19T14:41:20.573205", + "actor": "solermvictor", + "value": 5 + }, + { + "date": "2024-07-19T14:42:12.498933", + "actor": "solermvictor", + "value": -5 } ], "glewis@dundermifflin.com": [ @@ -136,6 +162,33 @@ "date": "2024-07-01T10:28:22.353956", "actor": "solermvictor", "value": 100 + }, + { + "date": "2024-07-19T14:41:20.573209", + "actor": "solermvictor", + "value": 5 + }, + { + "date": "2024-07-19T14:42:12.498938", + "actor": "solermvictor", + "value": -5 + } + ], + "pam@dundermifflin.com": [ + { + "date": "2024-07-15T18:20:06.454769", + "actor": "system", + "value": 500 + }, + { + "date": "2024-07-19T14:41:20.573212", + "actor": "solermvictor", + "value": 5 + }, + { + "date": "2024-07-19T14:42:12.498940", + "actor": "solermvictor", + "value": -5 } ] }, @@ -148,6 +201,9 @@ }, "glewis@dundermifflin.com": { "password": "SR2jr3iG" + }, + "pam@dundermifflin.com": { + "password": "BdO3tMuX" } } } \ No newline at end of file diff --git a/assets/people.csv b/assets/people.csv index 1efc0f2..abf2d0b 100644 --- a/assets/people.csv +++ b/assets/people.csv @@ -1,3 +1,4 @@ Jim Halpert, Sales, Salesman, jim@dundermifflin.com Dwight Schrute, Sales, Manager, schrute@dundermifflin.com Gabe Lewis, C-Level, CEO, glewis@dundermifflin.com +Pam Beasly, General, Recepcionist, pam@dundermifflin.com diff --git a/dundie/cli.py b/dundie/cli.py index 3774b60..a0b7ca2 100644 --- a/dundie/cli.py +++ b/dundie/cli.py @@ -1,4 +1,4 @@ -"""Main commands for dundie in command line interface.""" +"""Commandline Interface of Dundie.""" import json from importlib import metadata @@ -12,7 +12,7 @@ click.rich_click.USE_RICH_MARKUP = True click.rich_click.USE_MARKDOWN = True click.rich_click.SHOW_ARGUMENTS = True -click.rich_click.GRUOP_ARGUMENTS_OPTIONS = True +click.rich_click.GROUP_ARGUMENTS_OPTIONS = True click.rich_click.SHOW_METAVARS_COLUMN = False click.rich_click.APPEND_METAVARS_HELP = True @@ -22,10 +22,10 @@ def main(): """Dunder Mifflin Rewards System. - This CLI application controls Dunder Mifflin Rewards. + This cli application controls Dunder Mifflin rewards. - - admins can load information to the people database and assign points. - - users can view and transfer points. + - admins can load information tot he people database and assign points. + - users can view reports and transfer points. """ @@ -44,7 +44,7 @@ def load(filepath): table = Table(title="Dunder Mifflin Associates") headers = ["name", "dept", "role", "created", "e-mail"] for header in headers: - table.add_column(header, style="italic cyan1") + table.add_column(header, style="magenta") result = core.load(filepath) for person in result: @@ -59,7 +59,7 @@ def load(filepath): @click.option("--email", required=False) @click.option("--output", default=None) def show(output, **query): - """Show information about users od dept.""" + """Show information about user or dept.""" result = core.read(**query) if output: with open(output, "w") as output_file: @@ -70,7 +70,7 @@ def show(output, **query): table = Table(title="Dunder Mifflin Report") for key in result[0]: - table.add_column(key.title(), style="italic cyan1") + table.add_column(key.title().replace("_", " "), style="magenta") for person in result: table.add_row(*[str(value) for value in person.values()]) @@ -96,6 +96,6 @@ def add(ctx, value, **query): @click.option("--email", required=False) @click.pass_context def remove(ctx, value, **query): - """Remove points to the user or dept.""" + """Remove points from the user or dept.""" core.add(-value, **query) ctx.invoke(show, **query) diff --git a/dundie/core.py b/dundie/core.py index 50c6eef..ec4671e 100644 --- a/dundie/core.py +++ b/dundie/core.py @@ -2,14 +2,18 @@ import os from csv import reader +from typing import Any, Dict, List from dundie.database import add_movement, add_person, commit, connect +from dundie.models import Balance, Movement, Person from dundie.utils.log import get_logger log = get_logger() +Query = Dict[str, Any] +ResultDict = List[Dict[str, Any]] -def load(filepath): +def load(filepath: str) -> ResultDict: """Load data from filepath to the database. >>> len(load('assets/people.csv')) @@ -23,58 +27,56 @@ def load(filepath): db = connect() people = [] - headers = ["name", "dept", "role", "e-mail"] + headers = ["name", "dept", "role", "email"] for line in csv_data: person_data = dict(zip(headers, [item.strip() for item in line])) - pk = person_data.pop("e-mail") - person, created = add_person(db, pk, person_data) - - return_data = person.copy() + instance = Person(pk=person_data.pop("email"), **person_data) + person, created = add_person(db, instance) + return_data = person.dict(exclude={"pk"}) return_data["created"] = created - return_data["email"] = pk + return_data["email"] = person.pk people.append(return_data) commit(db) return people -def read(**query): +def read(**query: Query) -> ResultDict: """Read data from db and filters using query. read(email="joe@doe.com") """ + query = {k: v for k, v in query.items() if v is not None} db = connect() return_data = [] - for pk, data in db["people"].items(): - - dept = query.get("dept") - if dept and dept != data["dept"]: - continue - - # WALRUS / Assignment Expression - a partir do python 3.8 - if (email := query.get("email")) and email != pk: - continue + if "email" in query: + query["pk"] = query.pop("email") + for person in db[Person].filter(**query): return_data.append( { - "email": pk, - "balance": db["balance"][pk], - "last_movement": db["movement"][pk][-1]["date"], - **data, + "email": person.pk, + "balance": db[Balance].get_by_pk(person.pk).value, + "last_movement": db[Movement] + .filter(person__pk=person.pk)[-1] + .date, + **person.dict(exclude={"pk"}), } ) - return return_data -def add(value, **query): +def add(value: int, **query: Query): """Add value to each record on query.""" + query = {k: v for k, v in query.items() if v is not None} people = read(**query) + if not people: raise RuntimeError("Not Found") db = connect() user = os.getenv("USER") for person in people: - add_movement(db, person["email"], value, user) + instance = db[Person].get_by_pk(person["email"]) + add_movement(db, instance, value, user) commit(db) diff --git a/dundie/database.py b/dundie/database.py index dda2a7b..20fc52c 100644 --- a/dundie/database.py +++ b/dundie/database.py @@ -1,79 +1,236 @@ -"""Main functions to access, commit and make alterations into the database.""" +"""Functions to access and manipulate the database.""" +import importlib import json -from datetime import datetime +from collections import UserList, defaultdict +from typing import TYPE_CHECKING, Any, Dict, Optional -from dundie.settings import DATABASE_PATH, EMAIL_FROM -from dundie.utils.email import check_valid_email, send_email -from dundie.utils.user import generate_simple_password - -EMPTY_DB = {"people": {}, "balance": {}, "movement": {}, "users": {}} +if TYPE_CHECKING: + from pydantic import BaseModel - -def connect() -> dict: - """Connect to the database, returns dict data.""" +from dundie.settings import DATABASE_PATH, EMAIL_FROM +from dundie.utils.email import send_email + +EMPTY_DB: Dict[str, Dict[str, Any]] = { + "people": {}, + "balance": {}, + "movement": {}, + "users": {}, +} + + +DB = Dict["BaseModel", "ResultList"] + + +class NotFoundError(Exception): + """Not found error.""" + + ... + + +class ResultList(UserList): + def first(self) -> Any: + return self[0] + + def last(self) -> Any: + return self[-1] + + def get_by_pk(self, pk: str) -> Any: + if len(self) == 0: + raise NotFoundError(f"{pk} not found") + try: + if hasattr(self[0], "pk"): + return ResultList( + item for item in self if item.pk == pk + ).first() + return ResultList( + item for item in self if item.person.pk == pk + ).first() + except KeyError: + raise NotFoundError(f"{pk} not found") + + def filter(self, **query: Dict[str, Any]) -> "ResultList": + if not query: + return self + return_data = ResultList() + for item in self: + add_item = [] + for q, v in query.items(): + if "__" in q: + sub_model, sub_field = q.split("__") + related = getattr(item, sub_model) + if getattr(related, sub_field) == v: + add_item.append(True) + else: + if getattr(item, q) == v: + add_item.append(True) + else: + add_item.append(False) + if add_item and all(add_item): + return_data.append(item) + return return_data + + +class ORM: + """Mapeamento entre "table" no JSON e classes em models""" + + MAPPING: Dict[str, str] = { + "people": "dundie.models.Person", + "balance": "dundie.models.Balance", + "movement": "dundie.models.Movement", + "users": "dundie.models.User", + } + + @classmethod + def get_model_class(cls, table_name: str) -> Any: + module, obj = cls.MAPPING[table_name].rsplit(".", 1) + return getattr(importlib.import_module(module), obj) + + @classmethod + def get_table_name(cls, model: Any) -> str: + inverted_orm = {v.split(".")[-1]: k for k, v in cls.MAPPING.items()} + return inverted_orm[model.__name__] + + @classmethod + def serialize(cls, db) -> Dict[str, Any]: + """Turns Model instances back to json compatible dict.""" + raw_db: Dict[str, Any] = defaultdict(dict) + for model, instances in db.items(): + table_name = cls.get_table_name(model) + raw_db[table_name] # initialize default dict + for instance in instances: + raw_instance = json.loads(instance.json()) + if table_name == "people": + raw_db[table_name][raw_instance.pop("pk")] = raw_instance + elif table_name == "balance": + raw_db[table_name][instance.person.pk] = raw_instance[ + "value" + ] + elif table_name == "movement": + table = raw_db[table_name] + table.setdefault(instance.person.pk, []) + raw_instance.pop("person") + table[instance.person.pk].append(raw_instance) + else: + raw_instance.pop("person") + raw_db[table_name][instance.person.pk] = raw_instance + return raw_db + + @classmethod + def deserialize(cls, raw_data: Dict[str, Any]) -> Dict[Any, ResultList]: + """Turns JSON in to model isntances""" + results: Dict[Any, ResultList] = defaultdict(ResultList) + indexes = {} + for table_name, data in raw_data.items(): + Model = cls.get_model_class(table_name) + results[Model] # initialize default dict + if table_name == "people": + for pk, person_data in data.items(): + instance = Model(pk=pk, **person_data) + indexes[pk] = instance + results[Model].append(instance) + elif table_name == "balance": + for pk, balance_data in data.items(): + instance = Model(person=indexes[pk], value=balance_data) + results[Model].append(instance) + elif table_name == "users": + for pk, user_data in data.items(): + instance = Model(person=indexes[pk], **user_data) + results[Model].append(instance) + elif table_name == "movement": + for pk, movements in data.items(): + for movement in movements: + instance = Model(person=indexes[pk], **movement) + results[Model].append(instance) + return results + + +def connect() -> Dict[Any, ResultList]: + """Connects to the database, returns dict data""" try: with open(DATABASE_PATH, "r") as database_file: - return json.loads(database_file.read()) + raw_data = json.loads(database_file.read()) except (json.JSONDecodeError, FileNotFoundError): - return EMPTY_DB + raw_data = EMPTY_DB + # transform raw data from json to model objects / Deserialize + results = ORM.deserialize(raw_data) + return results -def commit(db): + +def commit(db: DB): """Save db back to the database file.""" - if db.keys() != EMPTY_DB.keys(): - raise RuntimeError("Database Schema is invalid.") + # transform model objects back to json database / Serialize + raw_db = ORM.serialize(db) + + if raw_db.keys() != EMPTY_DB.keys(): + raise RuntimeError(f"Database Schema is invalid. {raw_db.keys()}") + final_data = json.dumps(raw_db, indent=4) with open(DATABASE_PATH, "w") as database_file: - database_file.write(json.dumps(db, indent=4)) + database_file.write(final_data) -def add_person(db, pk, data): - """Save person data to database. +def add_person(db: DB, instance: Any): + """Saves person data to database. - - E-mail is unique (resolved by dictonary hash table) - If exists, update, else create - Set initial balance (managers = 100, others = 500) - - Generate a password if user is new, and send_email + - Generate a password if user is new and send_email """ - if not check_valid_email(pk): - raise ValueError(f"{pk} is not a valid e-mail.") - - table = db["people"] - person = table.get(pk, {}) - created = not bool(person) - person.update(data) - table[pk] = person + Person = ORM.get_model_class("people") + table = db[Person] + existing = table.filter(pk=instance.pk) + created = len(existing) == 0 if created: - set_initial_balance(db, pk, person) - password = set_initial_password(db, pk) - send_email(EMAIL_FROM, pk, "Your dundie password", password) - # TODO: Encrypt and send only link,not password - return person, created - - -def set_initial_password(db, pk): - """Generate and save password.""" - db["users"].setdefault(pk, {}) - db["users"][pk]["password"] = generate_simple_password(8) - return db["users"][pk]["password"] + table.append(instance) + set_initial_balance(db, instance) + password = set_initial_password(db, instance) + send_email(EMAIL_FROM, instance.pk, "Your dundie password", password) + else: + existing_data = existing.first().dict() + new_data = instance.dict() + existing_data.update(new_data) + table.remove(existing) + table.append(Person(**existing_data)) + return instance, created + + +def set_initial_password(db: DB, instance: Any) -> str: + """Generated and saves password""" + User = ORM.get_model_class("users") + user = User(person=instance) # password generated by model + db[User].append(user) + return user.password + + +def set_initial_balance(db: DB, person: Any): + """Add movement and set initial balance""" + value = 100 if person.role == "Manager" else 500 + add_movement(db, person, value) + + +def add_movement( + db: DB, person: Any, value: int, actor: Optional[str] = "system" +): + """Adds movement to user account. + Example:: -def set_initial_balance(db, pk, person): - """Add movement and set initial balance.""" - value = 100 if person["role"] == "Manager" else 500 - add_movement(db, pk, value) + add_movement(db, Person(...), 100, "me") + """ + Movement = ORM.get_model_class("movement") + db[Movement].append(Movement(person=person, value=value, actor=actor)) + movements = [item for item in db[Movement] if item.person.pk == person.pk] -def add_movement(db, pk, value, actor="system"): - """Add movement to user account. + Balance = ORM.get_model_class("balance") - Example:: + # reset balance table for the user + db[Balance] = ResultList( + [item for item in db[Balance] if item.person.pk != person.pk] + ) - add_movement(db, "me@me.com", 100, "me") - """ - movements = db["movement"].setdefault(pk, []) - movements.append( - {"date": datetime.now().isoformat(), "actor": actor, "value": value} + db[Balance].append( + Balance(person=person, value=sum([item.value for item in movements])) ) - db["balance"][pk] = sum([item["value"] for item in movements]) diff --git a/dundie/models.py b/dundie/models.py new file mode 100644 index 0000000..349ca55 --- /dev/null +++ b/dundie/models.py @@ -0,0 +1,76 @@ +from datetime import datetime +from decimal import Decimal + +from pydantic import BaseModel, Field, field_validator + +from dundie.utils.email import check_valid_email +from dundie.utils.user import generate_simple_password + + +class InvalidEmailError(Exception): + """Invalid email error.""" + + ... + + +class Person(BaseModel): + pk: str + name: str + dept: str + role: str + + @field_validator("pk") + def validate_email(cls, v: str) -> str: + if not check_valid_email(v): + raise InvalidEmailError(f"Invalid email for {v!r}") + return v + + def __str__(self) -> str: + return f"{self.name} - {self.role}" + + +class Balance(BaseModel): + person: Person + value: Decimal + + class ConfigDict: + json_encoders = {Person: lambda p: p.pk} + + +class Movement(BaseModel): + person: Person + actor: str + value: Decimal + date: datetime = Field(default_factory=lambda: datetime.now().isoformat()) + + class ConfigDict: + json_encoders = {Person: lambda p: p.pk} + + +class User(BaseModel): + person: Person + password: str = Field(default_factory=generate_simple_password) + + class ConfigDict: + json_encoders = {Person: lambda p: p.pk} + + +if __name__ == "__main__": + p = Person(pk="bruno@g.com", name="Bruno", dept="Sales", role="NA") + print(p) + print(p.json()) + + b = Balance(person=p, value=100) + print(b.json(models_as_dict=False)) + + m = Movement(person=p, date=datetime.now(), actor="sys", value=10) + print(m.json(models_as_dict=False)) + + u = User(person=p) + print(u.json(models_as_dict=False)) + + email = "invalid@" + try: + Person(pk=email) + except InvalidEmailError as e: + assert str(e) == f"Invalid email for {email!r}" diff --git a/requirements.dev.txt b/requirements.dev.txt index 36190b2..e5f2157 100644 --- a/requirements.dev.txt +++ b/requirements.dev.txt @@ -7,7 +7,6 @@ pytest-watch # Code Quality flake8 pyproject-flake8 -flake8-docstrings black isort diff --git a/requirements.txt b/requirements.txt index 45857f0..2e9dd91 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ click rich rich-click +pydantic diff --git a/tests/test_add.py b/tests/test_add.py index 231da47..01c2573 100644 --- a/tests/test_add.py +++ b/tests/test_add.py @@ -2,23 +2,24 @@ import pytest -from dundie.core import add +from dundie.core import add, load, read from dundie.database import add_person, commit, connect +from dundie.models import Balance, Person +from tests.constants import PEOPLE_FILE @pytest.mark.unit def test_add_movement(): - """Add two person into the db, and tests movements into the balance.""" db = connect() pk = "joe@doe.com" data = {"role": "Salesman", "dept": "Sales", "name": "Joe Doe"} - person, created = add_person(db, pk, data) + _, created = add_person(db, Person(pk=pk, **data)) assert created is True pk = "jim@doe.com" data = {"role": "Manager", "dept": "Management", "name": "Jim Doe"} - person, created = add_person(db, pk, data) + _, created = add_person(db, Person(pk=pk, **data)) assert created is True commit(db) @@ -27,5 +28,29 @@ def test_add_movement(): add(90, dept="Management") db = connect() - assert db["balance"]["joe@doe.com"] == 470 - assert db["balance"]["jim@doe.com"] == 190 + assert db[Balance].get_by_pk("joe@doe.com").value == 470 + assert db[Balance].get_by_pk("jim@doe.com").value == 190 + + +@pytest.mark.unit +def test_add_balance_for_dept(): + load(PEOPLE_FILE) + original = read(dept="Sales") + + add(100, dept="Sales") + + modified = read(dept="Sales") + for index, person in enumerate(modified): + assert person["balance"] == original[index]["balance"] + 100 + + +@pytest.mark.unit +def test_add_balance_for_person(): + load(PEOPLE_FILE) + original = read(email="jim@dundermifflin.com") + + add(-30, email="jim@dundermifflin.com") + + modified = read(email="jim@dundermifflin.com") + for index, person in enumerate(modified): + assert person["balance"] == original[index]["balance"] - 30 diff --git a/tests/test_database.py b/tests/test_database.py index 08d9eb0..f05b2db 100644 --- a/tests/test_database.py +++ b/tests/test_database.py @@ -1,71 +1,82 @@ -"""Tests the database.""" - import pytest -from dundie.database import EMPTY_DB, add_movement, add_person, commit, connect +from dundie.database import ( + EMPTY_DB, + ORM, + add_movement, + add_person, + commit, + connect, +) +from dundie.models import Balance, InvalidEmailError, Movement, Person + + +@pytest.mark.unit +def test_ensure_database_is_test(): + from dundie.database import DATABASE_PATH + + assert "test.json" in DATABASE_PATH @pytest.mark.unit def test_database_schema(): - """Test the schema of database.""" db = connect() - assert db.keys() == EMPTY_DB.keys() + db_keys = {ORM.get_table_name(model) for model in db} + assert db_keys == EMPTY_DB.keys() @pytest.mark.unit def test_commit_to_database(): - """Test the function commit to the database.""" db = connect() + pk = "joe@doe.com" data = {"name": "Joe Doe", "role": "Salesman", "dept": "Sales"} - db["people"]["joe@doe.com"] = data + db[Person].append(Person(pk=pk, **data)) commit(db) db = connect() - assert db["people"]["joe@doe.com"] == data + assert db[Person].get_by_pk("joe@doe.com").dict() == {"pk": pk, **data} @pytest.mark.unit def test_add_person_for_the_first_time(): - """Test the add person to the db for the first time.""" pk = "joe@doe.com" data = {"role": "Salesman", "dept": "Sales", "name": "Joe Doe"} db = connect() - person, created = add_person(db, pk, data) + _, created = add_person(db, Person(pk=pk, **data)) assert created is True commit(db) db = connect() - assert db["people"][pk] == data - assert db["balance"][pk] == 500 - assert len(db["movement"][pk]) > 0 - assert db["movement"][pk][0]["value"] == 500 + # assert db[Person].get_by_pk(pk) == {"pk": pk, **data} + assert db[Balance].get_by_pk(pk).value == 500 + assert len(db[Movement].filter(person__pk=pk)) > 0 + assert db[Movement].filter(person__pk=pk).first().value == 500 @pytest.mark.unit def test_negative_add_person_invalid_email(): - """Test if invalid email is going to raise a Value Error.""" - with pytest.raises(ValueError): - add_person({}, ".@bla", {}) + with pytest.raises(InvalidEmailError): + add_person({}, Person(pk=".@bla")) @pytest.mark.unit def test_add_or_remove_points_for_person(): - """Test add two persons and add and remove points of them.""" pk = "joe@doe.com" data = {"role": "Salesman", "dept": "Sales", "name": "Joe Doe"} db = connect() - person, created = add_person(db, pk, data) + person = Person(pk=pk, **data) + _, created = add_person(db, person) assert created is True commit(db) db = connect() - before = db["balance"][pk] + before = db[Balance].get_by_pk(pk).value - add_movement(db, pk, -100, "manager") + add_movement(db, person, -100, "manager") commit(db) db = connect() - after = db["balance"][pk] + after = db[Balance].get_by_pk(pk).value assert after == before - 100 assert after == 400 diff --git a/tests/test_load.py b/tests/test_load.py index b794f4a..82d7314 100644 --- a/tests/test_load.py +++ b/tests/test_load.py @@ -1,21 +1,28 @@ -"""Test the function load of dundie.""" - import pytest from dundie.core import load +from dundie.database import EMPTY_DB, ORM, connect from .constants import PEOPLE_FILE @pytest.mark.unit @pytest.mark.high -def test_load_positive_has_3_people(request): - """Test load function has 3 people.""" +def test_load_positive_has_2_people(request): + """Test function load function.""" assert len(load(PEOPLE_FILE)) == 3 @pytest.mark.unit @pytest.mark.high def test_load_positive_first_name_starts_with_j(request): - """Test load function starts with letter J.""" + """Test function load function.""" assert load(PEOPLE_FILE)[0]["name"] == "Jim Halpert" + + +@pytest.mark.unit +def test_db_schema(): + load(PEOPLE_FILE) + db = connect() + db_keys = {ORM.get_table_name(model) for model in db} + assert db_keys == EMPTY_DB.keys() diff --git a/tests/test_read.py b/tests/test_read.py index 5fc3c5a..704d9e2 100644 --- a/tests/test_read.py +++ b/tests/test_read.py @@ -1,24 +1,24 @@ -"""Test the function read.""" - import pytest -from dundie.core import read +from dundie.core import load, read from dundie.database import add_person, commit, connect +from dundie.models import Person + +from .constants import PEOPLE_FILE @pytest.mark.unit def test_read_with_query(): - """Test the function read passing query of dept and email.""" db = connect() pk = "joe@doe.com" data = {"role": "Salesman", "dept": "Sales", "name": "Joe Doe"} - person, created = add_person(db, pk, data) + _, created = add_person(db, Person(pk=pk, **data)) assert created is True pk = "jim@doe.com" data = {"role": "Manager", "dept": "Management", "name": "Jim Doe"} - person, created = add_person(db, pk, data) + _, created = add_person(db, Person(pk=pk, **data)) assert created is True commit(db) @@ -33,3 +33,24 @@ def test_read_with_query(): response = read(email="joe@doe.com") assert len(response) == 1 assert response[0]["name"] == "Joe Doe" + + +@pytest.mark.unit +def test_read_all_data(): + load(PEOPLE_FILE) + result = read() + assert len(result) == 3 + + +@pytest.mark.unit +def test_read_only_one_dept(): + load(PEOPLE_FILE) + result = read(dept="Sales") + assert len(result) == 2 + + +@pytest.mark.unit +def test_read_only_one_person(): + load(PEOPLE_FILE) + result = read(email="jim@dundermifflin.com") + assert len(result) == 1 diff --git a/tests/test_utils.py b/tests/test_utils.py index 23dcb9a..0ca15bd 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -1,5 +1,3 @@ -"""Test the dundie's utils function.""" - import pytest from dundie.utils.email import check_valid_email @@ -11,26 +9,24 @@ "address", ["bruno@rocha.com", "joe@doe.com", "a@b.pt"] ) def test_positive_check_valid_email(address): - """Ensure e-mail is valid.""" + """Ensure email is valid.""" assert check_valid_email(address) is True @pytest.mark.unit @pytest.mark.parametrize("address", ["bruno@.com", "@doe.com", "a@b"]) def test_negative_check_valid_email(address): - """Ensure e-mail is valid.""" + """Ensure email is invalid.""" assert check_valid_email(address) is False @pytest.mark.unit def test_generate_simple_password(): - """Test generation of random simple passwords. - + """Test generation of random simple passwords TODO: Generate hashed complex passwords, encrypit it """ passwords = [] - for i in range(100): + for _ in range(100): passwords.append(generate_simple_password(8)) - print(passwords) - assert (len(set(passwords))) == 100 + assert len(set(passwords)) == 100