diff --git a/__init__.py b/__init__.py index e69de29bb..d3f5a12fa 100644 --- a/__init__.py +++ b/__init__.py @@ -0,0 +1 @@ + diff --git a/bun.py b/bun.py index 5504bc1f4..2d72ac989 100644 --- a/bun.py +++ b/bun.py @@ -1,15 +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 +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/burger.py b/burger.py index 2b3b6a88b..c47ebe76a 100644 --- a/burger.py +++ b/burger.py @@ -1,48 +1,48 @@ -from typing import List - -from praktikum.bun import Bun -from praktikum.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) +from typing import List + +from praktikum.bun import Bun +from praktikum.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/database.py b/database.py index 4c75baf71..675150c09 100644 --- a/database.py +++ b/database.py @@ -1,33 +1,33 @@ -from typing import List - -from praktikum.bun import Bun -from praktikum.ingredient import Ingredient -from praktikum.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 +from typing import List + +from praktikum.bun import Bun +from praktikum.ingredient import Ingredient +from praktikum.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/ingredient.py b/ingredient.py index 0e50db8a2..15b4dff57 100644 --- a/ingredient.py +++ b/ingredient.py @@ -1,20 +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 +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/ingredient_types.py b/ingredient_types.py index 34940ad5d..00242d044 100644 --- a/ingredient_types.py +++ b/ingredient_types.py @@ -1,7 +1,7 @@ -""" -Перечисление с типами ингредиентов. -SAUCE – соус -FILLING – начинка -""" -INGREDIENT_TYPE_SAUCE = 'SAUCE' -INGREDIENT_TYPE_FILLING = 'FILLING' +""" +Перечисление с типами ингредиентов. +SAUCE – соус +FILLING – начинка +""" +INGREDIENT_TYPE_SAUCE = 'SAUCE' +INGREDIENT_TYPE_FILLING = 'FILLING' diff --git a/praktikum.py b/praktikum.py index ec522fa6d..ef22bc592 100644 --- a/praktikum.py +++ b/praktikum.py @@ -1,41 +1,41 @@ -from typing import List - -from praktikum.bun import Bun -from praktikum.burger import Burger -from praktikum.database import Database -from praktikum.ingredient import Ingredient - - -def main(): - # Инициализируем базу данных - database: Database = Database() - - # Создадим новый бургер - burger: Burger = Burger() - - # Считаем список доступных булок из базы данных - buns: List[Bun] = database.available_buns() - - # Считаем список доступных ингредиентов из базы данных - ingredients: List[Ingredient] = database.available_ingredients() - - # Соберём бургер - burger.set_buns(buns[0]) - - burger.add_ingredient(ingredients[1]) - burger.add_ingredient(ingredients[4]) - burger.add_ingredient(ingredients[3]) - burger.add_ingredient(ingredients[5]) - - # Переместим слой с ингредиентом - burger.move_ingredient(2, 1) - - # Удалим ингредиент - burger.remove_ingredient(3) - - # Распечатаем рецепт бургера - print(burger.get_receipt()) - - -if __name__ == "__main__": - main() +from typing import List + +from praktikum.bun import Bun +from praktikum.burger import Burger +from praktikum.database import Database +from praktikum.ingredient import Ingredient + + +def main(): + # Инициализируем базу данных + database: Database = Database() + + # Создадим новый бургер + burger: Burger = Burger() + + # Считаем список доступных булок из базы данных + buns: List[Bun] = database.available_buns() + + # Считаем список доступных ингредиентов из базы данных + ingredients: List[Ingredient] = database.available_ingredients() + + # Соберём бургер + burger.set_buns(buns[0]) + + burger.add_ingredient(ingredients[1]) + burger.add_ingredient(ingredients[4]) + burger.add_ingredient(ingredients[3]) + burger.add_ingredient(ingredients[5]) + + # Переместим слой с ингредиентом + burger.move_ingredient(2, 1) + + # Удалим ингредиент + burger.remove_ingredient(3) + + # Распечатаем рецепт бургера + print(burger.get_receipt()) + + +if __name__ == "__main__": + main() diff --git a/tests/test_burger.py b/tests/test_burger.py new file mode 100644 index 000000000..edece89b3 --- /dev/null +++ b/tests/test_burger.py @@ -0,0 +1,181 @@ +# tests/test_burger.py + +import pytest +from unittest.mock import Mock +from praktikum.burger import Burger +from praktikum.bun import Bun +from praktikum.ingredient import Ingredient + + +@pytest.fixture +def burger(): + """Фикстура, создающая новый экземпляр Burger перед каждым тестом.""" + return Burger() + + +# ---- Тесты для set_buns ---- + +def test_set_buns(burger): + """Проверяет, что метод set_buns сохраняет переданную булочку.""" + mock_bun = Mock(spec=Bun) + mock_bun.get_name.return_value = "white bun" + mock_bun.get_price.return_value = 100.0 + + burger.set_buns(mock_bun) + + assert burger.bun == mock_bun + assert burger.bun.get_name() == "white bun" + assert burger.bun.get_price() == 100.0 + + +# ---- Тесты для add_ingredient ---- + +def test_add_ingredient(burger): + """Проверяет добавление ингредиента в список.""" + mock_ingredient = Mock(spec=Ingredient) + mock_ingredient.get_name.return_value = "cutlet" + mock_ingredient.get_price.return_value = 150.0 + + burger.add_ingredient(mock_ingredient) + + assert len(burger.ingredients) == 1 + assert burger.ingredients[0] == mock_ingredient + assert burger.ingredients[0].get_name() == "cutlet" + + +# ---- Тесты для remove_ingredient ---- + +def test_remove_ingredient(burger): + """Проверяет удаление ингредиента по индексу.""" + ing1 = Mock(spec=Ingredient) + ing2 = Mock(spec=Ingredient) + burger.add_ingredient(ing1) + burger.add_ingredient(ing2) + + burger.remove_ingredient(0) + + assert len(burger.ingredients) == 1 + assert burger.ingredients[0] == ing2 + + +def test_remove_ingredient_out_of_range(burger): + """Проверяет, что при удалении по несуществующему индексу возникает IndexError.""" + with pytest.raises(IndexError): + burger.remove_ingredient(0) # список пуст + + +# ---- Тесты для move_ingredient ---- + +def test_move_ingredient(burger): + """Проверяет перемещение ингредиента с одного индекса на другой.""" + ing1 = Mock(spec=Ingredient) + ing2 = Mock(spec=Ingredient) + ing3 = Mock(spec=Ingredient) + burger.add_ingredient(ing1) + burger.add_ingredient(ing2) + burger.add_ingredient(ing3) + + burger.move_ingredient(0, 2) + + # Ожидаемый порядок: ing2, ing3, ing1 + assert burger.ingredients[0] == ing2 + assert burger.ingredients[1] == ing3 + assert burger.ingredients[2] == ing1 + + +def test_move_ingredient_out_of_range(burger): + """Проверяет, что при перемещении с невалидным индексом возникает IndexError.""" + with pytest.raises(IndexError): + burger.move_ingredient(5, 0) + + +# ---- Тесты для get_price (с параметризацией) ---- + +@pytest.mark.parametrize( + "bun_price, ingredient_prices, expected_price", + [ + (100.0, [50.0, 30.0], 280.0), # 2*100 + 50 + 30 = 280 + (200.0, [], 400.0), # только булочка (2*200) + (150.0, [10.0, 20.0, 30.0], 360.0), # 2*150 + 10+20+30 = 360 + ] +) +def test_get_price(burger, bun_price, ingredient_prices, expected_price): + """Параметризованный тест: проверяет расчёт цены бургера с разными наборами ингредиентов.""" + mock_bun = Mock(spec=Bun) + mock_bun.get_price.return_value = bun_price + burger.set_buns(mock_bun) + + for price in ingredient_prices: + mock_ing = Mock(spec=Ingredient) + mock_ing.get_price.return_value = price + burger.add_ingredient(mock_ing) + + assert burger.get_price() == expected_price + + +# ---- Тесты для get_receipt (с параметризацией) ---- + +@pytest.mark.parametrize( + "bun_name, bun_price, ingredients_info, expected_lines", + [ + # Случай без ингредиентов + ( + "black bun", + 100.0, + [], + [ + "(==== black bun ====)", + "(==== black bun ====)", + "Price: 200.0" + ] + ), + # С одним соусом + ( + "white bun", + 200.0, + [("SAUCE", "hot sauce", 100.0)], + [ + "(==== white bun ====)", + "= sauce hot sauce =", + "(==== white bun ====)", + "Price: 500.0" # 2*200 + 100 = 500 + ] + ), + # С двумя ингредиентами (соус и начинка) + ( + "red bun", + 300.0, + [ + ("SAUCE", "chili sauce", 50.0), + ("FILLING", "cutlet", 150.0) + ], + [ + "(==== red bun ====)", + "= sauce chili sauce =", + "= filling cutlet =", + "(==== red bun ====)", + "Price: 800.0" # 2*300 + 50 + 150 = 800 + ] + ), + ] +) +def test_get_receipt(burger, bun_name, bun_price, ingredients_info, expected_lines): + """Параметризованный тест: проверяет формирование чека для разных составов.""" + mock_bun = Mock(spec=Bun) + mock_bun.get_name.return_value = bun_name + mock_bun.get_price.return_value = bun_price + burger.set_buns(mock_bun) + + for ing_type, ing_name, ing_price in ingredients_info: + mock_ing = Mock(spec=Ingredient) + mock_ing.get_type.return_value = ing_type + mock_ing.get_name.return_value = ing_name + mock_ing.get_price.return_value = ing_price + burger.add_ingredient(mock_ing) + + receipt = burger.get_receipt() + receipt_lines = receipt.split('\n') + + assert len(receipt_lines) == len(expected_lines), "Количество строк не совпадает" + for expected, actual in zip(expected_lines, receipt_lines): + assert expected == actual, f"Ожидалось: '{expected}', получено: '{actual}'" \ No newline at end of file