diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 000000000..290f41711 --- /dev/null +++ b/.coveragerc @@ -0,0 +1,17 @@ +[run] +source = praktikum +omit = + praktikum/praktikum.py + */__pycache__/* + */.pytest_cache/* + tests/* + +[report] +exclude_lines = + pragma: no cover + def __repr__ + if self.debug: + if __name__ == .__main__.: + raise AssertionError + raise NotImplementedError + pass \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..eea9904bd --- /dev/null +++ b/.gitignore @@ -0,0 +1,141 @@ +__pycache__/ +*.py[cod] +*$py.class + +*.so + +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +*.manifest +*.spec + +pip-log.txt +pip-delete-this-directory.txt + +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ +reports/ + +htmlcov/ +coverage_html_report/ +.coverage.* +*.cover +*.py,cover + +allure-results/ +allure-report/ + +logs/ +*.log + +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ +pythonenv* + +.vscode/ +.idea/ +*.swp +*.swo +*~ +.DS_Store +.project +.classpath +.pydevproject +.settings/ + +.idea/ +*.iml +.vscode/ +*.code-workspace +.ipynb_checkpoints/ +*.ipynb + +*.pyc +*.pyo +*.pyd + +venv/ +env/ +.virtualenv/ +virtualenv/ + +*.db +*.sqlite +*.sqlite3 + +praktikum/__pycache__/ +tests/__pycache__/ + +tmp/ +temp/ +*.tmp +*.temp + +Thumbs.db +ehthumbs.db +Desktop.ini +$RECYCLE.BIN/ + +downloads/ +*.part + +*.pid +docker-compose.override.yml +Dockerfile.dev + +*.orig +*.patch +*.merge +*.rej + +*.secret +config/local.py +secrets.toml +coverage_reports/ +test_reports/ + +pytest.local.ini +.coverage.local + +*.bak +*.backup + +.python-version +.poetry.lock +poetry.lock.bak + +test_output/ +test-results/ \ No newline at end of file diff --git a/__init__.py b/praktikum/__init__.py similarity index 100% rename from __init__.py rename to praktikum/__init__.py diff --git a/bun.py b/praktikum/bun.py similarity index 100% rename from bun.py rename to praktikum/bun.py diff --git a/burger.py b/praktikum/burger.py similarity index 100% rename from burger.py rename to praktikum/burger.py diff --git a/database.py b/praktikum/database.py similarity index 100% rename from database.py rename to praktikum/database.py diff --git a/ingredient.py b/praktikum/ingredient.py similarity index 100% rename from ingredient.py rename to praktikum/ingredient.py diff --git a/ingredient_types.py b/praktikum/ingredient_types.py similarity index 100% rename from ingredient_types.py rename to praktikum/ingredient_types.py diff --git a/praktikum.py b/praktikum/praktikum.py similarity index 100% rename from praktikum.py rename to praktikum/praktikum.py diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 000000000..b91889d91 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +pytest==9.0.2 +pytest-cov==6.0.0 \ No newline at end of file diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/test_bun.py b/tests/test_bun.py new file mode 100644 index 000000000..bc3a8eb19 --- /dev/null +++ b/tests/test_bun.py @@ -0,0 +1,47 @@ +import pytest +import sys +import os + +sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) + +from praktikum.bun import Bun + + +class TestBun: + + @pytest.mark.parametrize("name, price", [ + ("black bun", 100), + ("white bun", 200), + ("red bun", 300), + ("", 0), + ("test bun", 150.5), + ]) + + def test_init(self, name, price): + + bun = Bun(name, price) + assert bun.name == name + assert bun.price == price + + @pytest.mark.parametrize("name, price", [ + ("black bun", 100), + ("white bun", 200), + ("red bun", 300), + ]) + + def test_get_name(self, name, price): + + bun = Bun(name, price) + assert bun.get_name() == name + + @pytest.mark.parametrize("name, price", [ + ("black bun", 100), + ("white bun", 200), + ("red bun", 300), + ("test bun", 150.5), + ]) + + def test_get_price(self, name, price): + + bun = Bun(name, price) + assert bun.get_price() == price \ No newline at end of file diff --git a/tests/test_burger.py b/tests/test_burger.py new file mode 100644 index 000000000..84f97aaad --- /dev/null +++ b/tests/test_burger.py @@ -0,0 +1,210 @@ +import pytest +from unittest.mock import Mock, patch +import sys +import os + + +sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) + +from praktikum.burger import Burger +from praktikum.bun import Bun +from praktikum.ingredient import Ingredient +from praktikum.ingredient_types import INGREDIENT_TYPE_SAUCE, INGREDIENT_TYPE_FILLING + + +class TestBurger: + + + def setup_method(self): + + self.burger = Burger() + self.mock_bun = Mock(spec=Bun) + self.mock_ingredient1 = Mock(spec=Ingredient) + self.mock_ingredient2 = Mock(spec=Ingredient) + + def test_init(self): + + assert self.burger.bun is None + assert self.burger.ingredients == [] + + def test_set_buns(self): + + self.burger.set_buns(self.mock_bun) + assert self.burger.bun == self.mock_bun + + def test_add_ingredient(self): + + self.burger.add_ingredient(self.mock_ingredient1) + assert len(self.burger.ingredients) == 1 + assert self.burger.ingredients[0] == self.mock_ingredient1 + + self.burger.add_ingredient(self.mock_ingredient2) + assert len(self.burger.ingredients) == 2 + assert self.burger.ingredients[1] == self.mock_ingredient2 + + def test_remove_ingredient(self): + + self.burger.add_ingredient(self.mock_ingredient1) + self.burger.add_ingredient(self.mock_ingredient2) + assert len(self.burger.ingredients) == 2 + + self.burger.remove_ingredient(0) + assert len(self.burger.ingredients) == 1 + assert self.burger.ingredients[0] == self.mock_ingredient2 + + def test_remove_ingredient_invalid_index(self): + + self.burger.add_ingredient(self.mock_ingredient1) + + with pytest.raises(IndexError): + self.burger.remove_ingredient(5) + + @pytest.mark.parametrize("index, new_index", [ + (0, 1), + (1, 0), + (0, 0), + ]) + def test_move_ingredient(self, index, new_index): + + + mock_ing1 = Mock(spec=Ingredient) + mock_ing2 = Mock(spec=Ingredient) + mock_ing1.get_name.return_value = "ingredient1" + mock_ing2.get_name.return_value = "ingredient2" + + self.burger.ingredients = [mock_ing1, mock_ing2] + + self.burger.move_ingredient(index, new_index) + + + assert len(self.burger.ingredients) == 2 + + def test_move_ingredient_invalid_index(self): + + self.burger.add_ingredient(self.mock_ingredient1) + + with pytest.raises(IndexError): + self.burger.move_ingredient(2, 0) + + @pytest.mark.parametrize("bun_price, ingredient_prices, expected_price", [ + (100, [50, 75], 325), + (200, [100, 150, 200], 850), + (50, [], 100), + ]) + def test_get_price(self, bun_price, ingredient_prices, expected_price): + + mock_bun = Mock(spec=Bun) + mock_bun.get_price.return_value = bun_price + self.burger.set_buns(mock_bun) + + for price in ingredient_prices: + mock_ingredient = Mock(spec=Ingredient) + mock_ingredient.get_price.return_value = price + self.burger.add_ingredient(mock_ingredient) + + assert self.burger.get_price() == expected_price + + def test_get_price_no_bun(self): + + with pytest.raises(AttributeError): + self.burger.get_price() + + def test_get_receipt_without_ingredients(self): + + mock_bun = Mock(spec=Bun) + mock_bun.get_name.return_value = "red bun" + mock_bun.get_price.return_value = 100 + self.burger.set_buns(mock_bun) + + receipt = self.burger.get_receipt() + + expected_receipt = "(==== red bun ====)\n(==== red bun ====)\n\nPrice: 200" + assert receipt == expected_receipt + + def test_get_receipt_with_one_ingredient(self): + + mock_bun = Mock(spec=Bun) + mock_bun.get_name.return_value = "white bun" + mock_bun.get_price.return_value = 100 + self.burger.set_buns(mock_bun) + + mock_ingredient = Mock(spec=Ingredient) + mock_ingredient.get_type.return_value = INGREDIENT_TYPE_SAUCE + mock_ingredient.get_name.return_value = "sour cream" + mock_ingredient.get_price.return_value = 100 + self.burger.add_ingredient(mock_ingredient) + + receipt = self.burger.get_receipt() + + expected_receipt = "(==== white bun ====)\n= sauce sour cream =\n(==== white bun ====)\n\nPrice: 300" + assert receipt == expected_receipt + + def test_get_receipt_with_two_ingredients(self): + + mock_bun = Mock(spec=Bun) + mock_bun.get_name.return_value = "black bun" + mock_bun.get_price.return_value = 100 + self.burger.set_buns(mock_bun) + + mock_sauce = Mock(spec=Ingredient) + mock_sauce.get_type.return_value = INGREDIENT_TYPE_SAUCE + mock_sauce.get_name.return_value = "hot sauce" + mock_sauce.get_price.return_value = 100 + self.burger.add_ingredient(mock_sauce) + + mock_filling = Mock(spec=Ingredient) + mock_filling.get_type.return_value = INGREDIENT_TYPE_FILLING + mock_filling.get_name.return_value = "cutlet" + mock_filling.get_price.return_value = 100 + self.burger.add_ingredient(mock_filling) + + receipt = self.burger.get_receipt() + + expected_receipt = "(==== black bun ====)\n= sauce hot sauce =\n= filling cutlet =\n(==== black bun ====)\n\nPrice: 400" + assert receipt == expected_receipt + + def test_get_receipt_complex(self): + + mock_bun = Mock(spec=Bun) + mock_bun.get_name.return_value = "test bun" + mock_bun.get_price.return_value = 150 + + mock_sauce = Mock(spec=Ingredient) + mock_sauce.get_type.return_value = INGREDIENT_TYPE_SAUCE + mock_sauce.get_name.return_value = "test sauce" + mock_sauce.get_price.return_value = 75 + + mock_filling = Mock(spec=Ingredient) + mock_filling.get_type.return_value = INGREDIENT_TYPE_FILLING + mock_filling.get_name.return_value = "test filling" + mock_filling.get_price.return_value = 125 + + self.burger.set_buns(mock_bun) + self.burger.add_ingredient(mock_sauce) + self.burger.add_ingredient(mock_filling) + + receipt = self.burger.get_receipt() + + expected_receipt = "(==== test bun ====)\n= sauce test sauce =\n= filling test filling =\n(==== test bun ====)\n\nPrice: 500" + assert receipt == expected_receipt + + def test_multiple_ingredients_manipulation(self): + + ingredients = [] + for i in range(5): + mock_ing = Mock(spec=Ingredient) + mock_ing.get_price.return_value = i * 10 + mock_ing.get_name.return_value = f"ingredient{i}" + ingredients.append(mock_ing) + + for ing in ingredients: + self.burger.add_ingredient(ing) + + assert len(self.burger.ingredients) == 5 + + self.burger.remove_ingredient(2) + assert len(self.burger.ingredients) == 4 + + self.burger.move_ingredient(3, 0) + + assert len(self.burger.ingredients) == 4 \ No newline at end of file diff --git a/tests/test_burger_integration.py b/tests/test_burger_integration.py new file mode 100644 index 000000000..cb83fe69a --- /dev/null +++ b/tests/test_burger_integration.py @@ -0,0 +1,39 @@ +import pytest +from praktikum.burger import Burger +from praktikum.bun import Bun +from praktikum.ingredient import Ingredient +from praktikum.ingredient_types import INGREDIENT_TYPE_SAUCE, INGREDIENT_TYPE_FILLING + + +class TestBurgerIntegration: + + def setup_method(self): + self.burger = Burger() + self.bun = Bun("test bun", 100) + self.sauce = Ingredient(INGREDIENT_TYPE_SAUCE, "test sauce", 50) + self.filling = Ingredient(INGREDIENT_TYPE_FILLING, "test filling", 75) + + def test_real_ingredients_price(self): + + self.burger.set_buns(self.bun) + self.burger.add_ingredient(self.sauce) + self.burger.add_ingredient(self.filling) + + assert self.burger.get_price() == 325 + + def test_real_ingredients_receipt(self): + + self.burger.set_buns(self.bun) + self.burger.add_ingredient(self.sauce) + self.burger.add_ingredient(self.filling) + + receipt = self.burger.get_receipt() + expected_receipt = ( + "(==== test bun ====)\n" + "= sauce test sauce =\n" + "= filling test filling =\n" + "(==== test bun ====)\n\n" + "Price: 325" + ) + + assert receipt == expected_receipt \ No newline at end of file diff --git a/tests/test_database.py b/tests/test_database.py new file mode 100644 index 000000000..2c23485a3 --- /dev/null +++ b/tests/test_database.py @@ -0,0 +1,87 @@ +import pytest +from unittest.mock import Mock, patch +import sys +import os + +sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) + +from praktikum.database import Database +from praktikum.bun import Bun +from praktikum.ingredient import Ingredient +from praktikum.ingredient_types import INGREDIENT_TYPE_SAUCE, INGREDIENT_TYPE_FILLING + + +class TestDatabase: + + + def setup_method(self): + self.database = Database() + + def test_init(self): + assert len(self.database.buns) == 3 + assert len(self.database.ingredients) == 6 + + def test_available_buns(self): + buns = self.database.available_buns() + + assert len(buns) == 3 + assert isinstance(buns[0], Bun) + assert buns[0].get_name() == "black bun" + assert buns[0].get_price() == 100 + + assert buns[1].get_name() == "white bun" + assert buns[1].get_price() == 200 + + assert buns[2].get_name() == "red bun" + assert buns[2].get_price() == 300 + + def test_available_ingredients(self): + ingredients = self.database.available_ingredients() + + assert len(ingredients) == 6 + assert isinstance(ingredients[0], Ingredient) + + assert ingredients[0].get_type() == INGREDIENT_TYPE_SAUCE + assert ingredients[0].get_name() == "hot sauce" + assert ingredients[0].get_price() == 100 + + assert ingredients[1].get_type() == INGREDIENT_TYPE_SAUCE + assert ingredients[1].get_name() == "sour cream" + assert ingredients[1].get_price() == 200 + + assert ingredients[2].get_type() == INGREDIENT_TYPE_SAUCE + assert ingredients[2].get_name() == "chili sauce" + assert ingredients[2].get_price() == 300 + + assert ingredients[3].get_type() == INGREDIENT_TYPE_FILLING + assert ingredients[3].get_name() == "cutlet" + assert ingredients[3].get_price() == 100 + + assert ingredients[4].get_type() == INGREDIENT_TYPE_FILLING + assert ingredients[4].get_name() == "dinosaur" + assert ingredients[4].get_price() == 200 + + assert ingredients[5].get_type() == INGREDIENT_TYPE_FILLING + assert ingredients[5].get_name() == "sausage" + assert ingredients[5].get_price() == 300 + + def test_buns_list_is_mutable(self): + + buns = self.database.available_buns() + original_count = len(buns) + + new_bun = Bun("test bun", 400) + buns.append(new_bun) + + assert len(self.database.buns) == original_count + 1 + assert self.database.buns[-1].get_name() == "test bun" + + def test_ingredients_list_is_mutable(self): + ingredients = self.database.available_ingredients() + original_count = len(ingredients) + + new_ingredient = Ingredient(INGREDIENT_TYPE_SAUCE, "test sauce", 400) + ingredients.append(new_ingredient) + + assert len(self.database.ingredients) == original_count + 1 + assert self.database.ingredients[-1].get_name() == "test sauce" \ No newline at end of file diff --git a/tests/test_ingredient.py b/tests/test_ingredient.py new file mode 100644 index 000000000..218424089 --- /dev/null +++ b/tests/test_ingredient.py @@ -0,0 +1,50 @@ +import pytest +import sys +import os + +sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) + +from praktikum.ingredient import Ingredient +from praktikum.ingredient_types import INGREDIENT_TYPE_SAUCE, INGREDIENT_TYPE_FILLING + + +class TestIngredient: + + + @pytest.mark.parametrize("ingredient_type, name, price", [ + (INGREDIENT_TYPE_SAUCE, "hot sauce", 100), + (INGREDIENT_TYPE_SAUCE, "sour cream", 200), + (INGREDIENT_TYPE_FILLING, "cutlet", 100), + (INGREDIENT_TYPE_FILLING, "dinosaur", 200), + ("INVALID_TYPE", "test", 300), + ]) + def test_init(self, ingredient_type, name, price): + + ingredient = Ingredient(ingredient_type, name, price) + assert ingredient.type == ingredient_type + assert ingredient.name == name + assert ingredient.price == price + + @pytest.mark.parametrize("ingredient_type, name, price", [ + (INGREDIENT_TYPE_SAUCE, "hot sauce", 100), + (INGREDIENT_TYPE_FILLING, "cutlet", 200), + ]) + def test_get_price(self, ingredient_type, name, price): + ingredient = Ingredient(ingredient_type, name, price) + assert ingredient.get_price() == price + + @pytest.mark.parametrize("ingredient_type, name, price", [ + (INGREDIENT_TYPE_SAUCE, "hot sauce", 100), + (INGREDIENT_TYPE_FILLING, "cutlet", 200), + ]) + def test_get_name(self, ingredient_type, name, price): + ingredient = Ingredient(ingredient_type, name, price) + assert ingredient.get_name() == name + + @pytest.mark.parametrize("ingredient_type, name, price", [ + (INGREDIENT_TYPE_SAUCE, "hot sauce", 100), + (INGREDIENT_TYPE_FILLING, "cutlet", 200), + ]) + def test_get_type(self, ingredient_type, name, price): + ingredient = Ingredient(ingredient_type, name, price) + assert ingredient.get_type() == ingredient_type \ No newline at end of file diff --git a/tests/test_ingredient_types.py b/tests/test_ingredient_types.py new file mode 100644 index 000000000..5f99643f4 --- /dev/null +++ b/tests/test_ingredient_types.py @@ -0,0 +1,23 @@ +import pytest +import sys +import os + +sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) + +from praktikum import ingredient_types + + +class TestIngredientTypes: + + + def test_constants_exist(self): + assert hasattr(ingredient_types, 'INGREDIENT_TYPE_SAUCE') + assert hasattr(ingredient_types, 'INGREDIENT_TYPE_FILLING') + + def test_constants_values(self): + assert ingredient_types.INGREDIENT_TYPE_SAUCE == 'SAUCE' + assert ingredient_types.INGREDIENT_TYPE_FILLING == 'FILLING' + + def test_constants_are_strings(self): + assert isinstance(ingredient_types.INGREDIENT_TYPE_SAUCE, str) + assert isinstance(ingredient_types.INGREDIENT_TYPE_FILLING, str) \ No newline at end of file