From f4defc3891330a1d6d46cbda66c69ef7797d5d66 Mon Sep 17 00:00:00 2001 From: tony Date: Thu, 19 Feb 2026 23:44:17 +0100 Subject: [PATCH 1/2] =?UTF-8?q?=D0=92=D1=8B=D0=BF=D0=BE=D0=BB=D0=BD=D0=B5?= =?UTF-8?q?=D0=BD=D0=BD=D0=BE=D0=B5=20=D0=BF=D0=B5=D1=80=D0=B2=D0=BE=D0=B5?= =?UTF-8?q?=20=D0=B7=D0=B0=D0=B4=D0=B0=D0=BD=D0=B8=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- praktikum/bun.py | 15 +++++++ praktikum/burger.py | 48 ++++++++++++++++++++ praktikum/database.py | 33 ++++++++++++++ praktikum/ingredient.py | 20 +++++++++ praktikum/ingredient_types.py | 7 +++ requirements.txt | 2 + tests/conftest.py | 19 ++++++++ tests/test_bun.py | 17 ++++++++ tests/test_burger.py | 82 +++++++++++++++++++++++++++++++++++ tests/test_database.py | 58 +++++++++++++++++++++++++ tests/test_ingredient.py | 19 ++++++++ 12 files changed, 321 insertions(+), 1 deletion(-) create mode 100644 praktikum/bun.py create mode 100644 praktikum/burger.py create mode 100644 praktikum/database.py create mode 100644 praktikum/ingredient.py create mode 100644 praktikum/ingredient_types.py create mode 100644 requirements.txt create mode 100644 tests/conftest.py create mode 100644 tests/test_bun.py create mode 100644 tests/test_burger.py create mode 100644 tests/test_database.py create mode 100644 tests/test_ingredient.py diff --git a/README.md b/README.md index 272081708..808865fe4 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ ### Структура проекта - `praktikum` - пакет, содержащий код программы -- `tests` - пакет, содержащий тесты, разделенные по классам. Например, `bun_test.py`, `burger_test.py` и т.д. +- `tests` - пакет, содержащий тесты, разделенные по классам. Например, `test_bun.py`, `test_burger.py` и т.д. ### Запуск автотестов diff --git a/praktikum/bun.py b/praktikum/bun.py new file mode 100644 index 000000000..5504bc1f4 --- /dev/null +++ b/praktikum/bun.py @@ -0,0 +1,15 @@ +class Bun: + """ + Модель булочки для бургера. + Булочке можно дать название и назначить цену. + """ + + def __init__(self, name: str, price: float): + self.name = name + self.price = price + + def get_name(self) -> str: + return self.name + + def get_price(self) -> float: + return self.price diff --git a/praktikum/burger.py b/praktikum/burger.py new file mode 100644 index 000000000..a33cc7c4f --- /dev/null +++ b/praktikum/burger.py @@ -0,0 +1,48 @@ +from typing import List + +from .bun import Bun +from .ingredient import Ingredient + + +class Burger: + """ + Модель бургера. + Бургер состоит из булочек и ингредиентов (начинка или соус). + Ингредиенты можно перемещать и удалять. + Можно распечать чек с информацией о бургере. + """ + + def __init__(self): + self.bun = None + self.ingredients: List[Ingredient] = [] + + def set_buns(self, bun: Bun): + self.bun = bun + + def add_ingredient(self, ingredient: Ingredient): + self.ingredients.append(ingredient) + + def remove_ingredient(self, index: int): + del self.ingredients[index] + + def move_ingredient(self, index: int, new_index: int): + self.ingredients.insert(new_index, self.ingredients.pop(index)) + + def get_price(self) -> float: + price = self.bun.get_price() * 2 + + for ingredient in self.ingredients: + price += ingredient.get_price() + + return price + + def get_receipt(self) -> str: + receipt: List[str] = [f'(==== {self.bun.get_name()} ====)'] + + for ingredient in self.ingredients: + receipt.append(f'= {str(ingredient.get_type()).lower()} {ingredient.get_name()} =') + + receipt.append(f'(==== {self.bun.get_name()} ====)\n') + receipt.append(f'Price: {self.get_price()}') + + return '\n'.join(receipt) diff --git a/praktikum/database.py b/praktikum/database.py new file mode 100644 index 000000000..b99c35512 --- /dev/null +++ b/praktikum/database.py @@ -0,0 +1,33 @@ +from typing import List + +from .bun import Bun +from .ingredient import Ingredient +from .ingredient_types import INGREDIENT_TYPE_SAUCE, INGREDIENT_TYPE_FILLING + + +class Database: + """ + Класс с методами по работе с базой данных. + """ + + def __init__(self): + self.buns: List[Bun] = [] + self.ingredients: List[Ingredient] = [] + + self.buns.append(Bun("black bun", 100)) + self.buns.append(Bun("white bun", 200)) + self.buns.append(Bun("red bun", 300)) + + self.ingredients.append(Ingredient(INGREDIENT_TYPE_SAUCE, "hot sauce", 100)) + self.ingredients.append(Ingredient(INGREDIENT_TYPE_SAUCE, "sour cream", 200)) + self.ingredients.append(Ingredient(INGREDIENT_TYPE_SAUCE, "chili sauce", 300)) + + self.ingredients.append(Ingredient(INGREDIENT_TYPE_FILLING, "cutlet", 100)) + self.ingredients.append(Ingredient(INGREDIENT_TYPE_FILLING, "dinosaur", 200)) + self.ingredients.append(Ingredient(INGREDIENT_TYPE_FILLING, "sausage", 300)) + + def available_buns(self) -> List[Bun]: + return self.buns + + def available_ingredients(self) -> List[Ingredient]: + return self.ingredients diff --git a/praktikum/ingredient.py b/praktikum/ingredient.py new file mode 100644 index 000000000..0e50db8a2 --- /dev/null +++ b/praktikum/ingredient.py @@ -0,0 +1,20 @@ +class Ingredient: + """ + Модель ингредиента. + Ингредиент: начинка или соус. + У ингредиента есть тип (начинка или соус), название и цена. + """ + + def __init__(self, ingredient_type: str, name: str, price: float): + self.type = ingredient_type + self.name = name + self.price = price + + def get_price(self) -> float: + return self.price + + def get_name(self) -> str: + return self.name + + def get_type(self) -> str: + return self.type diff --git a/praktikum/ingredient_types.py b/praktikum/ingredient_types.py new file mode 100644 index 000000000..34940ad5d --- /dev/null +++ b/praktikum/ingredient_types.py @@ -0,0 +1,7 @@ +""" +Перечисление с типами ингредиентов. +SAUCE – соус +FILLING – начинка +""" +INGREDIENT_TYPE_SAUCE = 'SAUCE' +INGREDIENT_TYPE_FILLING = 'FILLING' diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 000000000..1fe7b5eb9 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +pytest==8.3.3 +pytest-cov==5.0.0 diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 000000000..7b4f72b42 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,19 @@ +import pytest +from unittest.mock import Mock + + +@pytest.fixture +def bun_mock(): + bun = Mock() + bun.get_name.return_value = 'test bun' + bun.get_price.return_value = 50.0 + return bun + + +@pytest.fixture +def ingredient_mock(): + ingr = Mock() + ingr.get_type.return_value = 'SAUCE' + ingr.get_name.return_value = 'hot sauce' + ingr.get_price.return_value = 10.0 + return ingr diff --git a/tests/test_bun.py b/tests/test_bun.py new file mode 100644 index 000000000..5c11d8c13 --- /dev/null +++ b/tests/test_bun.py @@ -0,0 +1,17 @@ +import pytest + +from praktikum.bun import Bun + + +@pytest.mark.parametrize( + 'name, price', + [ + ('black bun', 100.0), + ('white bun', 200.5), + ], +) +def test_bun_getters_return_init_values(name, price): + bun = Bun(name, price) + + assert bun.get_name() == name + assert bun.get_price() == price diff --git a/tests/test_burger.py b/tests/test_burger.py new file mode 100644 index 000000000..457654360 --- /dev/null +++ b/tests/test_burger.py @@ -0,0 +1,82 @@ +from unittest.mock import Mock + +import pytest + +from praktikum.burger import Burger + + +def test_set_buns_sets_bun_reference(bun_mock): + burger = Burger() + + burger.set_buns(bun_mock) + + assert burger.bun is bun_mock + + +def test_add_ingredient_appends_to_list(ingredient_mock): + burger = Burger() + + burger.add_ingredient(ingredient_mock) + + assert burger.ingredients == [ingredient_mock] + + +def test_remove_ingredient_deletes_by_index(): + burger = Burger() + ing1, ing2 = Mock(), Mock() + burger.ingredients = [ing1, ing2] + + burger.remove_ingredient(0) + + assert burger.ingredients == [ing2] + + +def test_move_ingredient_reorders_list(): + burger = Burger() + ing1, ing2, ing3 = Mock(), Mock(), Mock() + burger.ingredients = [ing1, ing2, ing3] + + burger.move_ingredient(0, 2) + + assert burger.ingredients == [ing2, ing3, ing1] + + +def test_get_price_sums_bun_double_and_ingredients(bun_mock): + burger = Burger() + burger.set_buns(bun_mock) + + ing1, ing2 = Mock(), Mock() + ing1.get_price.return_value = 10.0 + ing2.get_price.return_value = 15.5 + burger.ingredients = [ing1, ing2] + + assert burger.get_price() == 50.0 * 2 + 10.0 + 15.5 + + +def test_get_receipt_formats_lines_and_uses_lowercase_types(bun_mock): + burger = Burger() + burger.set_buns(bun_mock) + + sauce = Mock() + sauce.get_type.return_value = 'SAUCE' + sauce.get_name.return_value = 'hot sauce' + sauce.get_price.return_value = 10.0 + + filling = Mock() + filling.get_type.return_value = 'FILLING' + filling.get_name.return_value = 'cutlet' + filling.get_price.return_value = 20.0 + + burger.ingredients = [sauce, filling] + + receipt = burger.get_receipt() + + expected = ( + '(==== test bun ====)\n' + '= sauce hot sauce =\n' + '= filling cutlet =\n' + '(==== test bun ====)\n\n' + 'Price: 130.0' + ) + + assert receipt == expected diff --git a/tests/test_database.py b/tests/test_database.py new file mode 100644 index 000000000..74c66e073 --- /dev/null +++ b/tests/test_database.py @@ -0,0 +1,58 @@ +import pytest + +from praktikum.database import Database +from praktikum.ingredient_types import INGREDIENT_TYPE_SAUCE, INGREDIENT_TYPE_FILLING + + +def test_available_buns_returns_three_buns(): + db = Database() + + buns = db.available_buns() + + assert len(buns) == 3 + + +@pytest.mark.parametrize( + 'index, expected_name, expected_price', + [ + (0, 'black bun', 100), + (1, 'white bun', 200), + (2, 'red bun', 300), + ], +) +def test_buns_have_expected_data(index, expected_name, expected_price): + db = Database() + + bun = db.available_buns()[index] + + assert bun.get_name() == expected_name + assert bun.get_price() == expected_price + + +def test_available_ingredients_returns_six_ingredients(): + db = Database() + + ingredients = db.available_ingredients() + + assert len(ingredients) == 6 + + +@pytest.mark.parametrize( + 'index, expected_type, expected_name, expected_price', + [ + (0, INGREDIENT_TYPE_SAUCE, 'hot sauce', 100), + (1, INGREDIENT_TYPE_SAUCE, 'sour cream', 200), + (2, INGREDIENT_TYPE_SAUCE, 'chili sauce', 300), + (3, INGREDIENT_TYPE_FILLING, 'cutlet', 100), + (4, INGREDIENT_TYPE_FILLING, 'dinosaur', 200), + (5, INGREDIENT_TYPE_FILLING, 'sausage', 300), + ], +) +def test_ingredients_have_expected_data(index, expected_type, expected_name, expected_price): + db = Database() + + ingredient = db.available_ingredients()[index] + + assert ingredient.get_type() == expected_type + assert ingredient.get_name() == expected_name + assert ingredient.get_price() == expected_price diff --git a/tests/test_ingredient.py b/tests/test_ingredient.py new file mode 100644 index 000000000..f5f6b5d3b --- /dev/null +++ b/tests/test_ingredient.py @@ -0,0 +1,19 @@ +import pytest + +from praktikum.ingredient import Ingredient +from praktikum.ingredient_types import INGREDIENT_TYPE_SAUCE, INGREDIENT_TYPE_FILLING + + +@pytest.mark.parametrize( + 'ingredient_type, name, price', + [ + (INGREDIENT_TYPE_SAUCE, 'hot sauce', 100.0), + (INGREDIENT_TYPE_FILLING, 'cutlet', 300.5), + ], +) +def test_ingredient_getters_return_init_values(ingredient_type, name, price): + ingredient = Ingredient(ingredient_type, name, price) + + assert ingredient.get_type() == ingredient_type + assert ingredient.get_name() == name + assert ingredient.get_price() == price From 000516dc468cbcb4504cd14d20cc9fe8a43e48ae Mon Sep 17 00:00:00 2001 From: tony Date: Mon, 23 Feb 2026 23:40:18 +0100 Subject: [PATCH 2/2] =?UTF-8?q?=D0=9F=D1=80=D0=B0=D0=B2=D0=BA=D0=B8=20?= =?UTF-8?q?=D0=BF=D0=BE=D1=81=D0=BB=D0=B5=20=D1=80=D0=B5=D0=B2=D1=8C=D1=8E?= =?UTF-8?q?=201=20=D0=B8=D1=82=D1=82=D0=B5=D1=80=D0=B0=D1=86=D0=B8=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/conftest.py | 9 ++++++ tests/test_bun.py | 27 ++++++++++++++---- tests/test_database.py | 60 ++++++++++++---------------------------- tests/test_ingredient.py | 39 ++++++++++++++++++++++---- 4 files changed, 83 insertions(+), 52 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 7b4f72b42..afdb0eaaf 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,7 +1,16 @@ +import os +import sys + import pytest from unittest.mock import Mock +# Ensure the project root (where the `praktikum/` package lives) is importable. +PROJECT_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) +if PROJECT_ROOT not in sys.path: + sys.path.insert(0, PROJECT_ROOT) + + @pytest.fixture def bun_mock(): bun = Mock() diff --git a/tests/test_bun.py b/tests/test_bun.py index 5c11d8c13..7c9b7ff03 100644 --- a/tests/test_bun.py +++ b/tests/test_bun.py @@ -4,14 +4,31 @@ @pytest.mark.parametrize( - 'name, price', + 'name', [ - ('black bun', 100.0), - ('white bun', 200.5), + 'black bun', + 'булочка', + 'bun-123', + 'bun!@#$', ], ) -def test_bun_getters_return_init_values(name, price): - bun = Bun(name, price) +def test_get_name_returns_init_value(name): + bun = Bun(name, 100.0) assert bun.get_name() == name + + +@pytest.mark.parametrize( + 'price', + [ + 0, + 1, + 200.5, + 1_000_000.0, + -10.5, + ], +) +def test_get_price_returns_init_value(price): + bun = Bun('black bun', price) + assert bun.get_price() == price diff --git a/tests/test_database.py b/tests/test_database.py index 74c66e073..e60f13707 100644 --- a/tests/test_database.py +++ b/tests/test_database.py @@ -1,58 +1,34 @@ -import pytest - 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 -def test_available_buns_returns_three_buns(): +def test_available_buns_returns_list_of_buns(): db = Database() buns = db.available_buns() - assert len(buns) == 3 - - -@pytest.mark.parametrize( - 'index, expected_name, expected_price', - [ - (0, 'black bun', 100), - (1, 'white bun', 200), - (2, 'red bun', 300), - ], -) -def test_buns_have_expected_data(index, expected_name, expected_price): - db = Database() + assert isinstance(buns, list) + assert buns, 'Ожидался непустой список булочек' + assert all(isinstance(bun, Bun) for bun in buns) - bun = db.available_buns()[index] + # Проверяем, что возвращаемые объекты можно корректно использовать через публичные методы + for bun in buns: + assert isinstance(bun.get_name(), str) + assert isinstance(bun.get_price(), (int, float)) - assert bun.get_name() == expected_name - assert bun.get_price() == expected_price - -def test_available_ingredients_returns_six_ingredients(): +def test_available_ingredients_returns_list_of_ingredients(): db = Database() ingredients = db.available_ingredients() - assert len(ingredients) == 6 - - -@pytest.mark.parametrize( - 'index, expected_type, expected_name, expected_price', - [ - (0, INGREDIENT_TYPE_SAUCE, 'hot sauce', 100), - (1, INGREDIENT_TYPE_SAUCE, 'sour cream', 200), - (2, INGREDIENT_TYPE_SAUCE, 'chili sauce', 300), - (3, INGREDIENT_TYPE_FILLING, 'cutlet', 100), - (4, INGREDIENT_TYPE_FILLING, 'dinosaur', 200), - (5, INGREDIENT_TYPE_FILLING, 'sausage', 300), - ], -) -def test_ingredients_have_expected_data(index, expected_type, expected_name, expected_price): - db = Database() - - ingredient = db.available_ingredients()[index] + assert isinstance(ingredients, list) + assert ingredients, 'Ожидался непустой список ингредиентов' + assert all(isinstance(ingredient, Ingredient) for ingredient in ingredients) - assert ingredient.get_type() == expected_type - assert ingredient.get_name() == expected_name - assert ingredient.get_price() == expected_price + for ingredient in ingredients: + assert ingredient.get_type() in (INGREDIENT_TYPE_SAUCE, INGREDIENT_TYPE_FILLING) + assert isinstance(ingredient.get_name(), str) + assert isinstance(ingredient.get_price(), (int, float)) diff --git a/tests/test_ingredient.py b/tests/test_ingredient.py index f5f6b5d3b..3d309c346 100644 --- a/tests/test_ingredient.py +++ b/tests/test_ingredient.py @@ -5,15 +5,44 @@ @pytest.mark.parametrize( - 'ingredient_type, name, price', + 'ingredient_type', [ - (INGREDIENT_TYPE_SAUCE, 'hot sauce', 100.0), - (INGREDIENT_TYPE_FILLING, 'cutlet', 300.5), + INGREDIENT_TYPE_SAUCE, + INGREDIENT_TYPE_FILLING, ], ) -def test_ingredient_getters_return_init_values(ingredient_type, name, price): - ingredient = Ingredient(ingredient_type, name, price) +def test_get_type_returns_init_value(ingredient_type): + ingredient = Ingredient(ingredient_type, 'test ingredient', 100.0) assert ingredient.get_type() == ingredient_type + + +@pytest.mark.parametrize( + 'name', + [ + 'hot sauce', + 'котлета', + 'ingredient-123', + 'ingr!@#$', + ], +) +def test_get_name_returns_init_value(name): + ingredient = Ingredient(INGREDIENT_TYPE_SAUCE, name, 100.0) + assert ingredient.get_name() == name + + +@pytest.mark.parametrize( + 'price', + [ + 0, + 1, + 300.5, + 1_000_000.0, + -10.5, + ], +) +def test_get_price_returns_init_value(price): + ingredient = Ingredient(INGREDIENT_TYPE_SAUCE, 'test ingredient', price) + assert ingredient.get_price() == price