diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..87ec0dcb5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,19 @@ +# IDE +.idea/ + +# Python +__pycache__/ +*.py[cod] + +# Virtual environments +.venv/ +venv/ + +# Tests / coverage +.pytest_cache/ +.coverage +htmlcov/ + +# OS +.DS_Store +Thumbs.db diff --git a/1_task/.coverage b/1_task/.coverage new file mode 100644 index 000000000..045e92c0b Binary files /dev/null and b/1_task/.coverage differ diff --git a/1_task/praktikum/README.md b/1_task/praktikum/README.md new file mode 100644 index 000000000..272081708 --- /dev/null +++ b/1_task/praktikum/README.md @@ -0,0 +1,24 @@ +## Задание 1: Юнит-тесты + +### Автотесты для проверки программы, которая помогает заказать бургер в Stellar Burgers + +### Реализованные сценарии + +Созданы юнит-тесты, покрывающие классы `Bun`, `Burger`, `Ingredient`, `Database` + +Процент покрытия 100% (отчет: `htmlcov/index.html`) + +### Структура проекта + +- `praktikum` - пакет, содержащий код программы +- `tests` - пакет, содержащий тесты, разделенные по классам. Например, `bun_test.py`, `burger_test.py` и т.д. + +### Запуск автотестов + +**Установка зависимостей** + +> `$ pip install -r requirements.txt` + +**Запуск автотестов и создание HTML-отчета о покрытии** + +> `$ pytest --cov=praktikum --cov-report=html` diff --git a/1_task/praktikum/__init__.py b/1_task/praktikum/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/1_task/praktikum/bun.py b/1_task/praktikum/bun.py new file mode 100644 index 000000000..5504bc1f4 --- /dev/null +++ b/1_task/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/1_task/praktikum/burger.py b/1_task/praktikum/burger.py new file mode 100644 index 000000000..2b3b6a88b --- /dev/null +++ b/1_task/praktikum/burger.py @@ -0,0 +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) diff --git a/1_task/praktikum/database.py b/1_task/praktikum/database.py new file mode 100644 index 000000000..4c75baf71 --- /dev/null +++ b/1_task/praktikum/database.py @@ -0,0 +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 diff --git a/1_task/praktikum/ingredient.py b/1_task/praktikum/ingredient.py new file mode 100644 index 000000000..0e50db8a2 --- /dev/null +++ b/1_task/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/1_task/praktikum/ingredient_types.py b/1_task/praktikum/ingredient_types.py new file mode 100644 index 000000000..34940ad5d --- /dev/null +++ b/1_task/praktikum/ingredient_types.py @@ -0,0 +1,7 @@ +""" +Перечисление с типами ингредиентов. +SAUCE – соус +FILLING – начинка +""" +INGREDIENT_TYPE_SAUCE = 'SAUCE' +INGREDIENT_TYPE_FILLING = 'FILLING' diff --git a/1_task/praktikum/praktikum.py b/1_task/praktikum/praktikum.py new file mode 100644 index 000000000..ec522fa6d --- /dev/null +++ b/1_task/praktikum/praktikum.py @@ -0,0 +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() diff --git a/1_task/requirements.txt b/1_task/requirements.txt new file mode 100644 index 000000000..e69de29bb diff --git a/1_task/tests/__init__.py b/1_task/tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/1_task/tests/data/__init__.py b/1_task/tests/data/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/1_task/tests/data/test_data.py b/1_task/tests/data/test_data.py new file mode 100644 index 000000000..5bcba71b7 --- /dev/null +++ b/1_task/tests/data/test_data.py @@ -0,0 +1,28 @@ +# buns +BLACK_BUN_NAME = "black bun" +BLACK_BUN_PRICE = 100 + +WHITE_BUN_NAME = "white bun" +WHITE_BUN_PRICE = 200 + +RED_BUN_NAME = "red bun" +RED_BUN_PRICE = 300 + +# ingredients +HOT_SAUCE_NAME = "hot sauce" +HOT_SAUCE_PRICE = 50 + +CUTLET_NAME = "cutlet" +CUTLET_PRICE = 200 + +SAUSAGE_NAME = "sausage" +SAUSAGE_PRICE = 300 + +# receipt +EXPECTED_RECEIPT_SINGLE_SAUCE = ( + "(==== black bun ====)\n" + "= sauce hot sauce =\n" + "(==== black bun ====)\n" + "\n" + "Price: 250" +) diff --git a/1_task/tests/test_bun.py b/1_task/tests/test_bun.py new file mode 100644 index 000000000..a17d7036f --- /dev/null +++ b/1_task/tests/test_bun.py @@ -0,0 +1,28 @@ +from praktikum.bun import Bun + +from tests.data.test_data import ( + BLACK_BUN_NAME, + BLACK_BUN_PRICE, + WHITE_BUN_NAME, + WHITE_BUN_PRICE, + RED_BUN_NAME, + RED_BUN_PRICE +) + + + +class TestBun: + + def test_bun_init(self): + bun = Bun(BLACK_BUN_NAME, BLACK_BUN_PRICE) + + assert bun.name == BLACK_BUN_NAME + assert bun.price == BLACK_BUN_PRICE + + def test_get_name(self): + bun = Bun(WHITE_BUN_NAME, WHITE_BUN_PRICE) + assert bun.get_name() == WHITE_BUN_NAME + + def test_get_price(self): + bun = Bun(RED_BUN_NAME, RED_BUN_PRICE) + assert bun.get_price() == RED_BUN_PRICE diff --git a/1_task/tests/test_burger.py b/1_task/tests/test_burger.py new file mode 100644 index 000000000..c61984057 --- /dev/null +++ b/1_task/tests/test_burger.py @@ -0,0 +1,109 @@ +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 +) + +from tests.data.test_data import ( + BLACK_BUN_NAME, + BLACK_BUN_PRICE, + HOT_SAUCE_NAME, + HOT_SAUCE_PRICE, + CUTLET_NAME, + CUTLET_PRICE, + EXPECTED_RECEIPT_SINGLE_SAUCE +) + + +class TestBurger: + + def test_set_buns(self): + burger = Burger() + bun = Bun(BLACK_BUN_NAME, BLACK_BUN_PRICE) + + burger.set_buns(bun) + + assert burger.bun == bun + + def test_add_ingredient(self): + burger = Burger() + ingredient = Ingredient( + INGREDIENT_TYPE_SAUCE, + HOT_SAUCE_NAME, + HOT_SAUCE_PRICE + ) + + burger.add_ingredient(ingredient) + + assert ingredient in burger.ingredients + + def test_remove_ingredient(self): + burger = Burger() + ingredient = Ingredient( + INGREDIENT_TYPE_FILLING, + CUTLET_NAME, + CUTLET_PRICE + ) + + burger.add_ingredient(ingredient) + burger.remove_ingredient(0) + + assert burger.ingredients == [] + + def test_move_ingredient(self): + burger = Burger() + + ingredient1 = Ingredient( + INGREDIENT_TYPE_SAUCE, + HOT_SAUCE_NAME, + HOT_SAUCE_PRICE + ) + ingredient2 = Ingredient( + INGREDIENT_TYPE_FILLING, + CUTLET_NAME, + CUTLET_PRICE + ) + + burger.add_ingredient(ingredient1) + burger.add_ingredient(ingredient2) + burger.move_ingredient(0, 1) + + assert burger.ingredients == [ingredient2, ingredient1] + + def test_get_price(self): + burger = Burger() + bun = Bun(BLACK_BUN_NAME, BLACK_BUN_PRICE) + + burger.set_buns(bun) + burger.add_ingredient( + Ingredient( + INGREDIENT_TYPE_SAUCE, + HOT_SAUCE_NAME, + HOT_SAUCE_PRICE + ) + ) + burger.add_ingredient( + Ingredient( + INGREDIENT_TYPE_FILLING, + CUTLET_NAME, + CUTLET_PRICE + ) + ) + + assert burger.get_price() == 450 + + def test_get_receipt(self): + burger = Burger() + bun = Bun(BLACK_BUN_NAME, BLACK_BUN_PRICE) + ingredient = Ingredient( + INGREDIENT_TYPE_SAUCE, + HOT_SAUCE_NAME, + HOT_SAUCE_PRICE + ) + + burger.set_buns(bun) + burger.add_ingredient(ingredient) + + assert burger.get_receipt() == EXPECTED_RECEIPT_SINGLE_SAUCE diff --git a/1_task/tests/test_database.py b/1_task/tests/test_database.py new file mode 100644 index 000000000..4c18eff0c --- /dev/null +++ b/1_task/tests/test_database.py @@ -0,0 +1,25 @@ +from praktikum.database import Database +from praktikum.bun import Bun +from praktikum.ingredient import Ingredient + + +class TestDatabase: + def test_database_init(self): + database = Database() + + assert len(database.buns) == 3 + assert len(database.ingredients) == 6 + + def test_available_buns(self): + database = Database() + buns = database.available_buns() + + assert isinstance(buns, list) + assert all(isinstance(bun, Bun) for bun in buns) + + def test_available_ingredients(self): + database = Database() + ingredients = database.available_ingredients() + + assert isinstance(ingredients, list) + assert all(isinstance(ingredient, Ingredient) for ingredient in ingredients) diff --git a/1_task/tests/test_ingredient.py b/1_task/tests/test_ingredient.py new file mode 100644 index 000000000..5feed1f97 --- /dev/null +++ b/1_task/tests/test_ingredient.py @@ -0,0 +1,53 @@ +from praktikum.ingredient import Ingredient +from praktikum.ingredient_types import ( + INGREDIENT_TYPE_SAUCE, + INGREDIENT_TYPE_FILLING +) + +from tests.data.test_data import ( + HOT_SAUCE_NAME, + HOT_SAUCE_PRICE, + CUTLET_NAME, + CUTLET_PRICE, + SAUSAGE_NAME, + SAUSAGE_PRICE +) + + + +class TestIngredient: + + def test_ingredient_init(self): + ingredient = Ingredient( + INGREDIENT_TYPE_SAUCE, + HOT_SAUCE_NAME, + HOT_SAUCE_PRICE + ) + + assert ingredient.type == INGREDIENT_TYPE_SAUCE + assert ingredient.name == HOT_SAUCE_NAME + assert ingredient.price == HOT_SAUCE_PRICE + + def test_get_price(self): + ingredient = Ingredient( + INGREDIENT_TYPE_FILLING, + CUTLET_NAME, + CUTLET_PRICE + ) + assert ingredient.get_price() == CUTLET_PRICE + + def test_get_name(self): + ingredient = Ingredient( + INGREDIENT_TYPE_FILLING, + SAUSAGE_NAME, + SAUSAGE_PRICE + ) + assert ingredient.get_name() == SAUSAGE_NAME + + def test_get_type(self): + ingredient = Ingredient( + INGREDIENT_TYPE_SAUCE, + HOT_SAUCE_NAME, + HOT_SAUCE_PRICE + ) + assert ingredient.get_type() == INGREDIENT_TYPE_SAUCE