Обновить doc/doc.md

This commit is contained in:
admin committed 2026-09-17 21:30:12 +00:00
1 parent 246f1cc34c
commit 8849b7449e
1 file changed
-438
-438
View File
@@ -1,441 +1,3 @@
```python
import os
import zipfile
# Prepare the project files
project_structure = {
"requirements.txt": """pytest>=8.0.0
requests>=2.31.0
python-dotenv>=1.0.0
allure-pytest>=2.13.0
""",
".env.example": """# OAuth-токен от Яндекс Полигона / тестового аккаунта
# Получить токен можно на странице полигона API Яндекс Диска:
# https://yandex.ru/dev/disk/poligon/
YANDEX_DISK_TOKEN=y0_AgAAAA...
""",
".gitignore": """# Python artifacts
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
# Virtual environments
venv/
.venv/
env/
ENV/
# Environment variables & secrets
.env
.env.local
# IDE & editors
.vscode/
.idea/
*.swp
*.swo
# Testing & coverage
.pytest_cache/
.coverage
htmlcov/
allure-results/
allure-report/
.cache/
""",
"pytest.ini": """[pytest]
testpaths = tests
python_files = test_*.py
python_classes = Test*
python_functions = test_*
addopts = -v -s --tb=short
markers =
crud: CRUD operations tests
negative: Negative tests
""",
"api/__init__.py": "",
"api/disk_client.py": '''"""
Модуль клиента для взаимодействия с REST API Яндекс Диска.
Документация API: https://yandex.ru/dev/disk/api/concepts/about.html
Полигон: https://cloud-api.yandex.net
"""
import requests
from typing import Optional, Dict, Any
class YandexDiskClient:
"""
HTTP-клиент-обёртка для выполнения запросов к API Яндекс Диска.
Инкапсулирует базовые методы (GET, POST, PUT, DELETE) и авторизацию.
"""
BASE_URL = "https://cloud-api.yandex.net/v1/disk/resources"
def __init__(self, token: str, timeout: int = 10):
"""
Инициализация сессии с предустановленными заголовками авторизации.
:param token: OAuth-токен сервиса Яндекс
:param timeout: Дефолтный таймаут для HTTP-запросов в секундах
"""
self.timeout = timeout
self.session = requests.Session()
# Все запросы к API Яндекс Диска требуют заголовок Authorization с префиксом OAuth
self.session.headers.update({
"Authorization": f"OAuth {token}",
"Accept": "application/json",
"Content-Type": "application/json"
})
def create_folder(self, path: str) -> requests.Response:
"""
PUT /v1/disk/resources
Создание папки на Яндекс Диске.
:param path: Путь к создаваемой папке (например, 'test_dir' или 'disk:/test_dir')
:return: Объект ответа requests.Response (201 Created при успехе)
"""
url = self.BASE_URL
params = {"path": path}
return self.session.put(url, params=params, timeout=self.timeout)
def get_resource_info(self, path: str, fields: Optional[str] = None) -> requests.Response:
"""
GET /v1/disk/resources
Получение метаинформации о ресурсе (файле или папке).
:param path: Путь к ресурсу на Диске
:param fields: Список возвращаемых полей через запятую (для фильтрации ответа)
:return: Объект ответа requests.Response (200 OK при успехе)
"""
url = self.BASE_URL
params = {"path": path}
if fields:
params["fields"] = fields
return self.session.get(url, params=params, timeout=self.timeout)
def publish_resource(self, path: str) -> requests.Response:
"""
POST /v1/disk/resources/publish
Публикация ресурса (создание публичной ссылки).
:param path: Путь к ресурсу, который необходимо опубликовать
:return: Объект ответа requests.Response (200 OK при успехе)
"""
url = f"{self.BASE_URL}/publish"
params = {"path": path}
return self.session.post(url, params=params, timeout=self.timeout)
def unpublish_resource(self, path: str) -> requests.Response:
"""
PUT /v1/disk/resources/unpublish
Закрытие публичного доступа к ресурсу.
:param path: Путь к ресурсу, доступ к которому закрывается
:return: Объект ответа requests.Response (200 OK при успехе)
"""
url = f"{self.BASE_URL}/unpublish"
params = {"path": path}
return self.session.put(url, params=params, timeout=self.timeout)
def delete_resource(self, path: str, permanently: bool = True) -> requests.Response:
"""
DELETE /v1/disk/resources
Удаление файла или папки.
:param path: Путь к удаляемому ресурсу
:param permanently: True — удалить безвозвратно мимо Корзины; False — переместить в Корзину
:return: Объект ответа requests.Response (204 No Content при успешном синхронном удалении)
"""
url = self.BASE_URL
params = {
"path": path,
"permanently": str(permanently).lower()
}
return self.session.delete(url, params=params, timeout=self.timeout)
''',
"tests/__init__.py": "",
"tests/conftest.py": '''"""
Конфигурационный файл Pytest с фикстурами для инициализации клиента и тестовых данных.
"""
import os
import uuid
import pytest
from dotenv import load_dotenv
from api.disk_client import YandexDiskClient
# Загрузка переменных окружения из .env файла
load_dotenv()
@pytest.fixture(scope="session")
def auth_token() -> str:
"""
Фикстура получения токена авторизации.
Проверяет наличие токена в переменных окружения. Если токен не задан,
тесты пропускаются с информативным сообщением.
"""
token = os.getenv("YANDEX_DISK_TOKEN")
if not token or token.strip() == "":
pytest.skip(
"YANDEX_DISK_TOKEN не найден в переменных окружения. "
"Задайте его в .env файле или через export YANDEX_DISK_TOKEN=..."
)
return token.strip()
@pytest.fixture(scope="session")
def disk_client(auth_token: str) -> YandexDiskClient:
"""
Фикстура создания экземпляра API-клиента на всю тестовую сессию.
"""
return YandexDiskClient(token=auth_token)
@pytest.fixture(scope="function")
def temp_folder(disk_client: YandexDiskClient):
"""
Фикстура создания изолированной временной папки для теста.
Гарантирует удаление созданной папки после завершения теста (Teardown).
"""
unique_suffix = uuid.uuid4().hex[:8]
folder_path = f"pytest_temp_{unique_suffix}"
# Setup: создаем папку
create_resp = disk_client.create_folder(folder_path)
assert create_resp.status_code == 201, f"Не удалось подготовить фикстуру папки: {create_resp.text}"
# Передаем управление в тест
yield folder_path
# Teardown: удаляем папку навсегда (мимо корзины)
disk_client.delete_resource(folder_path, permanently=True)
''',
"tests/test_disk_crud.py": '''"""
Набор автотестов для проверки REST API Яндекс Диска.
Покрывает методы:
- PUT: создание папки (создание ресурса)
- GET: получение метаданных ресурса
- POST: публикация ресурса (открытие публичного доступа)
- DELETE: удаление ресурса (очистка)
А также негативные сценарии (дублирование, несуществующий ресурс).
"""
import uuid
import pytest
from api.disk_client import YandexDiskClient
@pytest.mark.crud
class TestYandexDiskCRUD:
"""Тесты на основные CRUD-операции с ресурсами Яндекс Диска."""
def test_create_folder_put_success(self, disk_client: YandexDiskClient):
"""
Проверка создания папки (PUT-запрос).
Ожидаемый результат: HTTP 201 Created, в ответе присутствует ссылка на созданный ресурс (href).
"""
folder_name = f"dir_create_{uuid.uuid4().hex[:8]}"
try:
response = disk_client.create_folder(folder_name)
assert response.status_code == 201, f"Ожидался 201, получено {response.status_code}: {response.text}"
data = response.json()
assert "href" in data, "В теле ответа отсутствует поле 'href'"
assert "method" in data, "В теле ответа отсутствует поле 'method'"
assert data["method"] == "GET"
finally:
# Очистка ресурса после теста
disk_client.delete_resource(folder_name, permanently=True)
def test_get_resource_info_get_success(self, disk_client: YandexDiskClient, temp_folder: str):
"""
Проверка получения метаданных ресурса (GET-запрос).
Ожидаемый результат: HTTP 200 OK, тип ресурса 'dir', имя совпадает с созданным.
"""
response = disk_client.get_resource_info(temp_folder)
assert response.status_code == 200, f"Ожидался 200, получено {response.status_code}: {response.text}"
data = response.json()
assert data.get("type") == "dir", f"Ожидался тип 'dir', получено {data.get('type')}"
assert data.get("name") == temp_folder, f"Имя ресурса {data.get('name')} не совпадает с {temp_folder}"
assert "created" in data, "Поле 'created' отсутствует в метаданных"
def test_publish_resource_post_success(self, disk_client: YandexDiskClient, temp_folder: str):
"""
Проверка публикации ресурса (POST-запрос).
Ожидаемый результат: HTTP 200 OK, у папки появляется публичный URL и ключ.
"""
response = disk_client.publish_resource(temp_folder)
assert response.status_code == 200, f"Ожидался 200, получено {response.status_code}: {response.text}"
# Проверяем метаданные папки после публикации через GET
info_resp = disk_client.get_resource_info(temp_folder)
assert info_resp.status_code == 200
info_data = info_resp.json()
assert "public_url" in info_data, "После публикации поле 'public_url' отсутствует"
assert "public_key" in info_data, "После публикации поле 'public_key' отсутствует"
assert info_data["public_url"].startswith("https://"), "Публичный URL имеет некорректный формат"
def test_delete_folder_delete_success(self, disk_client: YandexDiskClient):
"""
Проверка удаления ресурса (DELETE-запрос).
Ожидаемый результат: HTTP 204 No Content при удалении, последующий GET возвращает 404 Not Found.
"""
folder_name = f"dir_delete_{uuid.uuid4().hex[:8]}"
# Предварительно создаем папку для последующего удаления
create_resp = disk_client.create_folder(folder_name)
assert create_resp.status_code == 201
# Удаление
delete_resp = disk_client.delete_resource(folder_name, permanently=True)
assert delete_resp.status_code == 204, f"Ожидался 204, получено {delete_resp.status_code}: {delete_resp.text}"
# Проверка отсутствия ресурса
get_resp = disk_client.get_resource_info(folder_name)
assert get_resp.status_code == 404, f"Ресурс все еще существует, статус {get_resp.status_code}"
@pytest.mark.negative
class TestYandexDiskNegative:
"""Негативные тесты для проверки корректности обработки ошибок API."""
def test_create_duplicate_folder_put_conflict(self, disk_client: YandexDiskClient, temp_folder: str):
"""
Повторное создание уже существующей папки.
Ожидаемый результат: HTTP 409 Conflict.
"""
response = disk_client.create_folder(temp_folder)
assert response.status_code == 409, f"Ожидался 409 Conflict, получено {response.status_code}"
data = response.json()
assert data.get("error") == "PlatformResourceAlreadyExists", f"Неожиданный код ошибки: {data.get('error')}"
def test_get_non_existent_resource_get_not_found(self, disk_client: YandexDiskClient):
"""
Запрос метаданных несуществующего ресурса.
Ожидаемый результат: HTTP 404 Not Found.
"""
non_existent_path = f"does_not_exist_{uuid.uuid4().hex}"
response = disk_client.get_resource_info(non_existent_path)
assert response.status_code == 404, f"Ожидался 404 Not Found, получено {response.status_code}"
data = response.json()
assert data.get("error") == "DiskNotFoundError", f"Неожиданный код ошибки: {data.get('error')}"
def test_unauthorized_access(self):
"""
Выполнение запроса с недействительным токеном.
Ожидаемый результат: HTTP 401 Unauthorized.
"""
invalid_client = YandexDiskClient(token="invalid_dummy_token_123")
response = invalid_client.get_resource_info("any_folder")
assert response.status_code == 401, f"Ожидался 401 Unauthorized, получено {response.status_code}"
data = response.json()
assert data.get("error") == "UnauthorizedError", f"Неожиданный код ошибки: {data.get('error')}"
''',
".github/workflows/tests.yml": """name: Run Yandex Disk API Tests
on:
push:
branches: [ main, master ]
pull_request:
branches: [ main, master ]
workflow_dispatch:
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Python 3.11
uses: actions/setup-python@v5
with:
python-version: "3.11"
cache: "pip"
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
- name: Run pytest
env:
YANDEX_DISK_TOKEN: ${{ secrets.YANDEX_DISK_TOKEN }}
run: |
pytest -v
""",
"README.md": """# Yandex Disk REST API Automation Tests
Проект автоматизированного тестирования сервиса **Яндекс Диск** на базе **Python**, **pytest** и **requests**.
Проект разработан в соответствии с техническим заданием и проверяет работу ключевых методов REST API:
- **PUT** — создание ресурса (папки)
- **GET** — чтение метаданных ресурса
- **POST** — публикация ресурса (получение публичной ссылки)
- **DELETE** — удаление ресурса (очистка диска)
Также включены негативные сценарии (попытка создания дубликата, обращение к несуществующему ресурсу, невалидный токен).
---
## 🛠 Стек технологий
- **Python 3.10+**
- **Pytest** — тестовый фреймворк, фикстуры, параметризация
- **Requests** — HTTP-клиент
- **Python-dotenv** — управление секретами и переменными окружения
- **GitHub Actions** — CI/CD пайплайн для автоматического прогона тестов
---
## 📁 Структура проекта
```text
yandex-disk-api-tests/
├── .github/
│ └── workflows/
│ └── tests.yml # CI/CD пайплайн (GitHub Actions)
├── api/
│ ├── __init__.py
│ └── disk_client.py # API-клиент (обёртка над requests с методами GET, POST, PUT, DELETE)
├── tests/
│ ├── __init__.py
│ ├── conftest.py # Сессионные фикстуры и teardown временных ресурсов
│ └── test_disk_crud.py # Позитивные и негативные тестовые сценарии
├── .env.example # Шаблон переменных окружения
├── .gitignore # Исключения для git
├── pytest.ini # Конфигурация запуска Pytest
├── requirements.txt # Список зависимостей
└── README.md # Документация проекта
```
---
## 🔑 Подготовка и авторизация
По условиям задания запрещено использовать личную учетную запись для получения OAuth-токена.