diff --git a/.coverage b/.coverage
new file mode 100644
index 000000000..85a4bd240
Binary files /dev/null and b/.coverage differ
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 000000000..edb3945fe
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,217 @@
+# My files
+sandbox.py
+
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[codz]
+*$py.class
+
+# C extensions
+*.so
+
+# Distribution / packaging
+.Python
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+wheels/
+share/python-wheels/
+*.egg-info/
+.installed.cfg
+*.egg
+MANIFEST
+
+# PyInstaller
+# Usually these files are written by a python script from a template
+# before PyInstaller builds the exe, so as to inject date/other infos into it.
+*.manifest
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+.tox/
+.nox/
+.coverage.*
+.cache
+nosetests.xml
+coverage.xml
+*.cover
+*.py.cover
+.hypothesis/
+.pytest_cache/
+cover/
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+*.log
+local_settings.py
+db.sqlite3
+db.sqlite3-journal
+
+# Flask stuff:
+instance/
+.webassets-cache
+
+# Scrapy stuff:
+.scrapy
+
+# Sphinx documentation
+docs/_build/
+
+# PyBuilder
+.pybuilder/
+target/
+
+# Jupyter Notebook
+.ipynb_checkpoints
+
+# IPython
+profile_default/
+ipython_config.py
+
+# pyenv
+# For a library or package, you might want to ignore these files since the code is
+# intended to run in multiple environments; otherwise, check them in:
+# .python-version
+
+# pipenv
+# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
+# However, in case of collaboration, if having platform-specific dependencies or dependencies
+# having no cross-platform support, pipenv may install dependencies that don't work, or not
+# install all needed dependencies.
+# Pipfile.lock
+
+# UV
+# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
+# This is especially recommended for binary packages to ensure reproducibility, and is more
+# commonly ignored for libraries.
+# uv.lock
+
+# poetry
+# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
+# This is especially recommended for binary packages to ensure reproducibility, and is more
+# commonly ignored for libraries.
+# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
+# poetry.lock
+# poetry.toml
+
+# pdm
+# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
+# pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
+# https://pdm-project.org/en/latest/usage/project/#working-with-version-control
+# pdm.lock
+# pdm.toml
+.pdm-python
+.pdm-build/
+
+# pixi
+# Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
+# pixi.lock
+# Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
+# in the .venv directory. It is recommended not to include this directory in version control.
+.pixi
+
+# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
+__pypackages__/
+
+# Celery stuff
+celerybeat-schedule
+celerybeat.pid
+
+# Redis
+*.rdb
+*.aof
+*.pid
+
+# RabbitMQ
+mnesia/
+rabbitmq/
+rabbitmq-data/
+
+# ActiveMQ
+activemq-data/
+
+# SageMath parsed files
+*.sage.py
+
+# Environments
+.env
+.envrc
+.venv
+env/
+venv/
+ENV/
+env.bak/
+venv.bak/
+
+# Spyder project settings
+.spyderproject
+.spyproject
+
+# Rope project settings
+.ropeproject
+
+# mkdocs documentation
+/site
+
+# mypy
+.mypy_cache/
+.dmypy.json
+dmypy.json
+
+# Pyre type checker
+.pyre/
+
+# pytype static type analyzer
+.pytype/
+
+# Cython debug symbols
+cython_debug/
+
+# PyCharm
+# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
+# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
+# and can be added to the global gitignore or merged into this file. For a more nuclear
+# option (not recommended) you can uncomment the following to ignore the entire idea folder.
+# .idea/
+
+# Abstra
+# Abstra is an AI-powered process automation framework.
+# Ignore directories containing user credentials, local state, and settings.
+# Learn more at https://abstra.io/docs
+.abstra/
+
+# Visual Studio Code
+# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
+# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
+# and can be added to the global gitignore or merged into this file. However, if you prefer,
+# you could uncomment the following to ignore the entire vscode folder
+# .vscode/
+
+# Ruff stuff:
+.ruff_cache/
+
+# PyPI configuration file
+.pypirc
+
+# Marimo
+marimo/_static/
+marimo/_lsp/
+__marimo__/
+
+# Streamlit
+.streamlit/secrets.toml
diff --git a/README.md b/README.md
index 272081708..150b4eb5c 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,24 @@
## Задание 1: Юнит-тесты
+
+КОММЕНТАРИИ
+
+
+"ИСПРАВИТЬ"
+
+1. "Метод слишком сложен в выводе. Необходимо проверять содержимое вывода полностью"
+
+> Переписала: сгенерировала такой же чек, как выдает метод, потому что ТЗ или макета нет, больше неоткуда его взять.
+
+2. "Отсутствует отчет в pytest-cov"
+
+> Сорри, забыла удалить из .gitignore. Исправилась.
+
+
+
+___________________________________________________________________
+
+
### Автотесты для проверки программы, которая помогает заказать бургер в Stellar Burgers
### Реализованные сценарии
diff --git a/conftest.py b/conftest.py
new file mode 100644
index 000000000..ac35b6161
--- /dev/null
+++ b/conftest.py
@@ -0,0 +1,33 @@
+import sys, os
+print("CWD", os.getcwd())
+print("PATH before:", sys.path[0])
+
+
+import pytest
+import random
+from praktikum.bun import Bun
+from praktikum.ingredient import Ingredient
+from praktikum.database import Database
+import praktikum.ingredient_types as it
+from helpers import GenerateData
+
+
+
+@pytest.fixture
+def bun_creation():
+ name = GenerateData.generate_string(7)
+ price = GenerateData.generate_num()
+ bun = Bun(name, price)
+ return bun
+
+
+
+@pytest.fixture
+def ingredient_creation():
+ ingredient_type = random.choice([it.INGREDIENT_TYPE_FILLING, it.INGREDIENT_TYPE_SAUCE])
+ price = GenerateData.generate_num()
+ name = GenerateData.generate_string(7)
+ ingredient = Ingredient(ingredient_type, name, price)
+ return ingredient
+
+
diff --git a/helpers.py b/helpers.py
new file mode 100644
index 000000000..e32357ddc
--- /dev/null
+++ b/helpers.py
@@ -0,0 +1,55 @@
+import random
+import string
+from unittest.mock import Mock
+
+from praktikum.ingredient import Ingredient
+import praktikum.ingredient_types as it
+
+
+class GenerateData:
+
+ @classmethod
+ def generate_string(cls, length):
+ return ''.join(random.choice(string.ascii_lowercase) for i in range(length))
+
+
+ @classmethod
+ def generate_num(cls, min=1, max=100):
+ return random.randint(min, max)
+
+
+
+class CreateObject:
+
+ @classmethod
+ def create_mock_ingredient (cls):
+ mock_ingr = Mock()
+ mock_ingr.type = mock_ingr.get_type.return_value = random.choice([it.INGREDIENT_TYPE_FILLING, it.INGREDIENT_TYPE_SAUCE])
+ mock_ingr.name = mock_ingr.get_name.return_value = 'ingr_' + GenerateData.generate_string(4)
+ mock_ingr.price = mock_ingr.get_price.return_value = 100 + GenerateData.generate_num(1,90)
+ return mock_ingr
+
+
+ @classmethod
+ def create_list_mock_ingredients (cls, quantity):
+ list_mock_ingr = []
+ for i in range(quantity):
+ list_mock_ingr.append(cls.create_mock_ingredient())
+ return list_mock_ingr
+
+
+ @classmethod
+ def calculate_price_list_ingredients(cls, list_ingr):
+ length = len(list_ingr)
+ sum = 0
+ for i in range (length):
+ sum += list_ingr[i].price
+ return sum
+
+ @classmethod
+ def create_mock_bun (cls):
+ mock_bun = Mock()
+ mock_bun.name = mock_bun.get_name.return_value = 'bulka_' + GenerateData.generate_string(4)
+ mock_bun.price = mock_bun.get_price.return_value = 200 + GenerateData.generate_num(1,90)
+ return mock_bun
+
diff --git a/htmlcov/class_index.html b/htmlcov/class_index.html
new file mode 100644
index 000000000..a92411d8e
--- /dev/null
+++ b/htmlcov/class_index.html
@@ -0,0 +1,221 @@
+
+
+
+
+
+
+
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/pytest.ini b/pytest.ini
new file mode 100644
index 000000000..ea3fdc244
--- /dev/null
+++ b/pytest.ini
@@ -0,0 +1,4 @@
+[pytest]
+pythonpath = .
+testpaths = tests
+addopts = --import-mode=prepend
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 000000000..b0eef150f
Binary files /dev/null and b/requirements.txt differ
diff --git a/tests/test_bun.py b/tests/test_bun.py
new file mode 100644
index 000000000..717481208
--- /dev/null
+++ b/tests/test_bun.py
@@ -0,0 +1,36 @@
+import pytest
+import allure
+
+from praktikum.bun import Bun
+
+
+
+class TestBun:
+
+ @allure.title('Тестирование создания экземпляра Bun')
+ @allure.description('Проверка, что создан экземпляр с переменными, чьи значения равны заанным при создании')
+ @pytest.mark.parametrize('name,price', [
+ ['Bulka',100],
+ [None, None],
+ ['Bulka', None],
+ [None, 100]
+ ])
+ def test_init(self, name, price):
+ bun = Bun(name, price)
+ assert (bun.name == name) and (bun.price == price)
+
+
+ @allure.title('Тестирование метода get_name()')
+ @allure.description('Сравниваем результаты работы метода и значение name')
+ def test_get_name(self, bun_creation):
+ bun = bun_creation
+ assert (bun.get_name()== bun.name)
+
+
+ @allure.title('Тестирование метода get_price()')
+ @allure.description('Сравниваем результаты работы метода и переменной price')
+ def test_get_price(self, bun_creation):
+ bun = bun_creation
+ assert (bun.price == bun.get_price())
+
+
diff --git a/tests/test_burger.py b/tests/test_burger.py
new file mode 100644
index 000000000..388ab19d1
--- /dev/null
+++ b/tests/test_burger.py
@@ -0,0 +1,113 @@
+import pytest
+import allure
+from unittest.mock import Mock
+
+from praktikum.burger import Burger
+from helpers import CreateObject
+
+
+
+# Здесь создание булки и ингридиентов вынесены не в фикстуры, а в функции, потому что в методах нужно создавать более одного ингридиента и комбинацию булки и ингридиентов.
+# А писать одну фразу burger = Burger() в тесте а) короче, чем название фикстуры в параметрах, потом в теле, б) быстрее и экономнее, чем вызывать отдельную фикстуру и передавать из нее данные.
+
+
+class TestBurger:
+
+
+ @allure.title('Тестирование создания экземпляра burger')
+ @allure.description('Проверка, что создан экземпляр класса Burger, у него есть переменная bun со значением None и пустой список ingredients')
+ def test_init (self):
+ burger = Burger()
+ assert \
+ (burger) and \
+ (burger.bun is None) and \
+ (burger.ingredients == [])
+
+
+ @allure.title('Тестирование метода set_buns()')
+ @allure.description('Сравниваем исходную булочку и то, что возвращает метод set_buns()')
+ def test_set_buns (self):
+ mock_bun = CreateObject.create_mock_bun()
+ burger = Burger()
+ burger.set_buns(mock_bun)
+ assert burger.bun==mock_bun
+
+
+ @allure.title('Тестирование метода add_ingredient()')
+ @allure.description('Сравниваем исходный список ингредиентов и то, что возвращает метод add_ingredient()')
+ def test_add_ingredient (self):
+ mock_ingredient = CreateObject.create_mock_ingredient()
+ burger = Burger()
+ burger.add_ingredient(mock_ingredient)
+ assert mock_ingredient in burger.ingredients
+
+
+ @allure.title('Тестирование метода remove_ingredient()')
+ @allure.description('Сравниваем исходный список ингридиентов без одного из них с результатом работы метода remove_ingredient()')
+ def test_remove_ingredient (self):
+ ingr1 = CreateObject.create_mock_ingredient()
+ ingr2 = CreateObject.create_mock_ingredient()
+ ingr3 = CreateObject.create_mock_ingredient()
+ burger = Burger()
+ burger.ingredients = [ingr1, ingr2, ingr3]
+ burger.remove_ingredient(2)
+ assert burger.ingredients == [ingr1, ingr2]
+
+
+
+ @allure.title('Тестирование метода move_ingredient()')
+ @allure.description('Сравниваем исходный список ингредиентов с поменяными местами ингредиентами с результатов работы метода move_ingredient()')
+ def test_move_ingredient (self):
+ ingr1 = CreateObject.create_mock_ingredient()
+ ingr2 = CreateObject.create_mock_ingredient()
+ ingr3 = CreateObject.create_mock_ingredient()
+ burger = Burger()
+ burger.ingredients = [ingr1, ingr2, ingr3]
+ burger.move_ingredient(0,1)
+ assert burger.ingredients == [ingr2, ingr1, ingr3]
+
+
+ @allure.title('Тестирование метода get_price()')
+ @allure.description('Сравниваем сумму цен всех компонентов и результат работы метода get_price()')
+ def test_get_price(self):
+ mock_bun = CreateObject.create_mock_bun()
+ ingrs_moks = CreateObject.create_list_mock_ingredients(4)
+ burger = Burger()
+ burger.bun = mock_bun
+ burger.ingredients = ingrs_moks
+
+ assert (burger.get_price() == (mock_bun.price*2 + CreateObject.calculate_price_list_ingredients(ingrs_moks)))
+
+
+ @allure.title('Тестирование метода get_receipt()')
+ @allure.description('Сравниваем: 1. Количество строк чека равно кол-во ингредиентов + 2 (булки) + 2 (пустая строка и строка с ценой). 2. Цена в чеке совпадает с расчетной')
+ def test_get_receipt(self):
+ num_ingr = 4
+ mock_bun = CreateObject.create_mock_bun()
+ ingrs_moks = CreateObject.create_list_mock_ingredients(num_ingr)
+ burger = Burger()
+ burger.bun = mock_bun
+ burger.ingredients = ingrs_moks
+ burger_price_to_be = mock_bun.price*2 + CreateObject.calculate_price_list_ingredients(ingrs_moks)
+
+ receipt_from_classBurger = burger.get_receipt()
+
+ receipt_to_be = f'(==== {mock_bun.name} ====)\n'
+ for ingr in ingrs_moks:
+ receipt_to_be += f'= {ingr.type.lower()} {ingr.name} =\n'
+ receipt_to_be += f'(==== {mock_bun.name} ====)\n\n'
+ receipt_to_be += f'Price: {burger_price_to_be}'
+
+
+ assert (receipt_from_classBurger == receipt_to_be)
+
+
+
+
+"""
+(==== bulka_jrwx ====)
+= sauce ingr_duyq =
+= sauce ingr_ychh =
+= sauce ingr_mivg =
+(==== bulka_jrwx ====)
+"""
\ No newline at end of file
diff --git a/tests/test_database.py b/tests/test_database.py
new file mode 100644
index 000000000..8479425de
--- /dev/null
+++ b/tests/test_database.py
@@ -0,0 +1,34 @@
+import pytest
+import allure
+
+from praktikum.database import Database
+
+
+class TestDatabase:
+
+
+ # Ннууу... могу проверить, что экземпляр создается, а в нем создаются списки и заполняются данными: непустое И (в наличии и непустое) И (в наличии и непустое)
+
+ @allure.title('Тестирование создания экземпляря Database')
+ @allure.description('Проверка, что есть экземпляр, у него есть непустой список buns и непустой список ingredients')
+ def test_init (self):
+ db = Database()
+ assert db and db.buns and db.ingredients
+
+
+# Предвижу комментарий: "Создание экземпляра Database должно быть в фикстуре"
+# И сразу говорю, что специально не выносила: создание - одно выражение. Создавать фикстуру, а потом прописвать ее в каждом методе по объему больше плюс добавляются запуски фикстур, передача данных итд.
+
+ @allure.title('Тестирование метода available_buns()')
+ @allure.description('Сравнение результатов работы метода и переменной buns')
+ def test_available_bun (self):
+ db = Database()
+ assert (db.available_buns()==db.buns)
+
+
+
+ @allure.title('Тестирование метода available_ingredients()')
+ @allure.description('Сравнение результатов работы метода и переменной ingredients')
+ def test_available_ingredient (self):
+ db = Database()
+ assert (db.available_ingredients() == db.ingredients)
\ No newline at end of file
diff --git a/tests/test_ingredients.py b/tests/test_ingredients.py
new file mode 100644
index 000000000..fca28c42c
--- /dev/null
+++ b/tests/test_ingredients.py
@@ -0,0 +1,47 @@
+import pytest
+import allure
+import random
+
+from praktikum.ingredient import Ingredient
+import praktikum.ingredient_types as it
+from helpers import GenerateData
+
+
+class TestIngredient:
+
+
+ @allure.title('Тестирование создания экземпляра Ingredient')
+ @allure.description('Проверка, что создан экземпляр с переменными, чьи значения равны заанным при создании')
+ def test_init (self):
+ ingr_type = random.choice([it.INGREDIENT_TYPE_FILLING, it.INGREDIENT_TYPE_SAUCE])
+ ingr_price = float(GenerateData.generate_num())
+ ingr_name = GenerateData.generate_string(7)
+ ingr_whole = Ingredient(ingredient_type=ingr_type, name=ingr_name, price = ingr_price)
+ assert \
+ (ingr_whole.type == ingr_type) and \
+ (ingr_whole.price == ingr_price) and \
+ (ingr_whole.name == ingr_name)
+ # да, здесь можно вынести создание сразу всех данных в отдельный метод, но для одного раза это нерационально
+
+
+ @allure.title('Тестирование метода get_price()')
+ @allure.description('Сравнение результатов работы метода и переменной price.')
+ def test_get_price (self, ingredient_creation):
+ ingr = ingredient_creation
+ assert (ingr.get_price() == ingr.price)
+
+
+ @allure.title('Тестирование метода get_name()')
+ @allure.description('Сравнение результатов работы метода и переменной name')
+ def test_get_name (self, ingredient_creation):
+ ingr = ingredient_creation
+ assert (ingr.get_name() == ingr.name)
+
+
+ @allure.title('Тестирование метода get_type()')
+ @allure.description('Сравнение результатов работы метода и переменной type')
+ def test_get_type (self, ingredient_creation):
+ ingr = ingredient_creation
+ assert (ingr.get_type() == ingr.type)
+
+
\ No newline at end of file