751 lines
25 KiB
Markdown
751 lines
25 KiB
Markdown
## 🔑 Подготовка и авторизация
|
|||
|
|
|
||
|
|
По условиям задания запрещено использовать личную учетную запись для получения OAuth-токена.
|
||
|
|
|
||
|
|
1. Зарегистрируйте отдельный тестовый аккаунт на Яндексе или воспользуйтесь [Полигоном API Яндекс Диска](https://yandex.ru/dev/disk/poligon/).
|
||
|
|
2. Получите OAuth-токен с правами доступа к Яндекс Диску (`cloud_api:disk.read`, `cloud_api:disk.write` или полный доступ к Диску).
|
||
|
|
3. Скопируйте полученный токен.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 🚀 Локальный запуск
|
||
|
|
|
||
|
|
### 1. Клонирование репозитория
|
||
|
|
|
||
|
|
```bash
|
||
|
|
git clone [https://github.com/](https://github.com/)<your-username>/yandex-disk-api-tests.git
|
||
|
|
cd yandex-disk-api-tests
|
||
|
|
|
||
|
|
```
|
||
|
|
|
||
|
|
### 2. Создание и активация виртуального окружения
|
||
|
|
|
||
|
|
* **Linux / macOS:**
|
||
|
|
```bash
|
||
|
|
python3 -m venv .venv
|
||
|
|
source .venv/bin/activate
|
||
|
|
|
||
|
|
```
|
||
|
|
|
||
|
|
|
||
|
|
* **Windows (PowerShell):**
|
||
|
|
```powershell
|
||
|
|
python -m venv .venv
|
||
|
|
.venv\\Scripts\\Activate.ps1
|
||
|
|
|
||
|
|
```
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
### 3. Установка зависимостей
|
||
|
|
|
||
|
|
```bash
|
||
|
|
pip install --upgrade pip
|
||
|
|
pip install -r requirements.txt
|
||
|
|
|
||
|
|
```
|
||
|
|
|
||
|
|
### 4. Настройка переменных окружения
|
||
|
|
|
||
|
|
Создайте файл `.env` на основе примера `.env.example`:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
cp .env.example .env
|
||
|
|
|
||
|
|
```
|
||
|
|
|
||
|
|
Откройте файл `.env` и укажите ваш токен:
|
||
|
|
|
||
|
|
```ini
|
||
|
|
YANDEX_DISK_TOKEN=ваш_oauth_токен_здесь
|
||
|
|
|
||
|
|
```
|
||
|
|
|
||
|
|
### 5. Запуск тестов
|
||
|
|
|
||
|
|
* Запуск всех тестов с подробным выводом:
|
||
|
|
```bash
|
||
|
|
pytest
|
||
|
|
|
||
|
|
```
|
||
|
|
|
||
|
|
|
||
|
|
* Запуск только CRUD-тестов:
|
||
|
|
```bash
|
||
|
|
pytest -m crud
|
||
|
|
|
||
|
|
```
|
||
|
|
|
||
|
|
|
||
|
|
* Запуск только негативных тестов:
|
||
|
|
```bash
|
||
|
|
pytest -m negative
|
||
|
|
|
||
|
|
```
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## ⚙️ Настройка CI/CD (GitHub Actions)
|
||
|
|
|
||
|
|
В репозитории настроен автоматический запуск тестов при каждом пуше и Pull Request в ветки `main` / `master`.
|
||
|
|
|
||
|
|
Чтобы тесты выполнялись в GitHub Actions:
|
||
|
|
|
||
|
|
1. Перейдите в ваш репозиторий на GitHub: **Settings** -> **Secrets and variables** -> **Actions**.
|
||
|
|
2. Нажмите кнопку **New repository secret**.
|
||
|
|
3. Добавьте секрет:
|
||
|
|
* **Name:** `YANDEX_DISK_TOKEN`
|
||
|
|
* **Value:** `<ваш_oauth_токен>`
|
||
|
|
|
||
|
|
|
||
|
|
4. После сохранения секрета тесты будут автоматически запускаться во вкладке **Actions**.
|
||
|
|
"""
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
### Структура репозитория
|
||
|
|
|
||
|
|
```text
|
||
|
|
yandex-disk-api-tests/
|
||
|
|
├── .github/
|
||
|
|
│ └── workflows/
|
||
|
|
│ └── tests.yml # CI/CD пайплайн для GitHub Actions
|
||
|
|
├── api/
|
||
|
|
│ ├── __init__.py # Маркер пакета Python
|
||
|
|
│ └── disk_client.py # HTTP-клиент (API Wrapper для Яндекс Диска)
|
||
|
|
├── tests/
|
||
|
|
│ ├── __init__.py # Маркер пакета Python
|
||
|
|
│ ├── conftest.py # Сессионные фикстуры и teardown временных ресурсов
|
||
|
|
│ └── test_disk_crud.py # Тестовые сценарии (GET, POST, PUT, DELETE + негативные)
|
||
|
|
├── .env.example # Пример файла с переменными окружения
|
||
|
|
├── .gitignore # Правила исключения временных файлов и секретов
|
||
|
|
├── pytest.ini # Глобальные параметры запуска Pytest
|
||
|
|
├── requirements.txt # Зависимости проекта
|
||
|
|
└── README.md # Описание проекта и инструкция по запуску
|
||
|
|
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### 1. Зависимости и конфигурация
|
||
|
|
|
||
|
|
#### `requirements.txt`
|
||
|
|
|
||
|
|
```text
|
||
|
|
pytest>=8.0.0
|
||
|
|
requests>=2.31.0
|
||
|
|
python-dotenv>=1.0.0
|
||
|
|
allure-pytest>=2.13.0
|
||
|
|
|
||
|
|
```
|
||
|
|
|
||
|
|
#### `.gitignore`
|
||
|
|
|
||
|
|
```gitignore
|
||
|
|
# Секреты и локальные переменные окружения
|
||
|
|
.env
|
||
|
|
.env.local
|
||
|
|
*.env
|
||
|
|
|
||
|
|
# Виртуальные окружения Python
|
||
|
|
venv/
|
||
|
|
.venv/
|
||
|
|
env/
|
||
|
|
ENV/
|
||
|
|
|
||
|
|
# Байткод и кэш Python
|
||
|
|
__pycache__/
|
||
|
|
*.py[cod]
|
||
|
|
*$py.class
|
||
|
|
*.so
|
||
|
|
build/
|
||
|
|
develop-eggs/
|
||
|
|
dist/
|
||
|
|
downloads/
|
||
|
|
eggs/
|
||
|
|
.eggs/
|
||
|
|
lib/
|
||
|
|
lib64/
|
||
|
|
parts/
|
||
|
|
sdist/
|
||
|
|
var/
|
||
|
|
wheels/
|
||
|
|
*.egg-info/
|
||
|
|
.installed.cfg
|
||
|
|
*.egg
|
||
|
|
|
||
|
|
# Конфигурации IDE и редакторов
|
||
|
|
.vscode/
|
||
|
|
.idea/
|
||
|
|
*.swp
|
||
|
|
*.swo
|
||
|
|
|
||
|
|
# Артефакты тестов и отчетов
|
||
|
|
.pytest_cache/
|
||
|
|
.coverage
|
||
|
|
htmlcov/
|
||
|
|
allure-results/
|
||
|
|
allure-report/
|
||
|
|
.cache/
|
||
|
|
|
||
|
|
```
|
||
|
|
|
||
|
|
#### `.env.example`
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# OAuth-токен от тестовой учетной записи Яндекс Диска / Полигона API
|
||
|
|
# Получить токен: https://yandex.ru/dev/disk/poligon/
|
||
|
|
YANDEX_DISK_TOKEN=y0_AgAAAA...
|
||
|
|
|
||
|
|
```
|
||
|
|
|
||
|
|
#### `pytest.ini`
|
||
|
|
|
||
|
|
```ini
|
||
|
|
[pytest]
|
||
|
|
testpaths = tests
|
||
|
|
python_files = test_*.py
|
||
|
|
python_classes = Test*
|
||
|
|
python_functions = test_*
|
||
|
|
addopts = -v -s --tb=short
|
||
|
|
markers =
|
||
|
|
crud: основные CRUD-сценарии API (GET, POST, PUT, DELETE)
|
||
|
|
negative: негативные тесты на обработку ошибок
|
||
|
|
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### 2. Исходный код клиента и тестов
|
||
|
|
|
||
|
|
#### `api/__init__.py`
|
||
|
|
|
||
|
|
*(Пустой файл)*
|
||
|
|
|
||
|
|
#### `api/disk_client.py`
|
||
|
|
|
||
|
|
```python
|
||
|
|
"""
|
||
|
|
Клиент для взаимодействия с REST API Яндекс Диска v1.
|
||
|
|
Официальная документация: https://yandex.ru/dev/disk/api/concepts/about.html
|
||
|
|
Полигон API: https://cloud-api.yandex.net
|
||
|
|
"""
|
||
|
|
|
||
|
|
from typing import Optional
|
||
|
|
import requests
|
||
|
|
|
||
|
|
|
||
|
|
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):
|
||
|
|
"""
|
||
|
|
Инициализирует клиент с преднастроенной сессией и OAuth-заголовком.
|
||
|
|
|
||
|
|
:param token: OAuth-токен Яндекс Диска.
|
||
|
|
:param timeout: Максимальное время ожидания ответа сервиса (в секундах).
|
||
|
|
"""
|
||
|
|
self.timeout = timeout
|
||
|
|
self.session = requests.Session()
|
||
|
|
|
||
|
|
# Яндекс Диск требует передавать токен в формате "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_folder').
|
||
|
|
:return: requests.Response (201 Created при успехе).
|
||
|
|
"""
|
||
|
|
params = {"path": path}
|
||
|
|
return self.session.put(self.BASE_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 при успехе).
|
||
|
|
"""
|
||
|
|
params = {"path": path}
|
||
|
|
if fields:
|
||
|
|
params["fields"] = fields
|
||
|
|
return self.session.get(self.BASE_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 при синхронном удалении).
|
||
|
|
"""
|
||
|
|
params = {
|
||
|
|
"path": path,
|
||
|
|
"permanently": str(permanently).lower()
|
||
|
|
}
|
||
|
|
return self.session.delete(self.BASE_URL, params=params, timeout=self.timeout)
|
||
|
|
|
||
|
|
```
|
||
|
|
|
||
|
|
#### `tests/__init__.py`
|
||
|
|
|
||
|
|
*(Пустой файл)*
|
||
|
|
|
||
|
|
#### `tests/conftest.py`
|
||
|
|
|
||
|
|
```python
|
||
|
|
"""
|
||
|
|
Конфигурация фикстур 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:
|
||
|
|
"""
|
||
|
|
Сессионная фикстура получения OAuth-токена.
|
||
|
|
Если токен не передан, прерывает прогон тестов с понятным сообщением.
|
||
|
|
"""
|
||
|
|
token = os.getenv("YANDEX_DISK_TOKEN")
|
||
|
|
if not token or not token.strip():
|
||
|
|
pytest.skip(
|
||
|
|
"Пропуск тестов: переменная окружения YANDEX_DISK_TOKEN не задана. "
|
||
|
|
"Задайте её в файле .env или через экспорт окружения."
|
||
|
|
)
|
||
|
|
return token.strip()
|
||
|
|
|
||
|
|
|
||
|
|
@pytest.fixture(scope="session")
|
||
|
|
def disk_client(auth_token: str) -> YandexDiskClient:
|
||
|
|
"""
|
||
|
|
Создает один общий экземпляр YandexDiskClient на всю тестовую сессию.
|
||
|
|
"""
|
||
|
|
return YandexDiskClient(token=auth_token)
|
||
|
|
|
||
|
|
|
||
|
|
@pytest.fixture(scope="function")
|
||
|
|
def temp_folder(disk_client: YandexDiskClient):
|
||
|
|
"""
|
||
|
|
Фикстура изолированной папки для теста с автоматической очисткой (Teardown).
|
||
|
|
Создает директорию перед тестом и гарантированно удаляет её после.
|
||
|
|
"""
|
||
|
|
folder_name = f"pytest_fixture_{uuid.uuid4().hex[:8]}"
|
||
|
|
|
||
|
|
# SETUP: Создание папки
|
||
|
|
create_resp = disk_client.create_folder(folder_name)
|
||
|
|
assert create_resp.status_code == 201, (
|
||
|
|
f"Ошибка инициализации фикстуры папки: {create_resp.status_code}, {create_resp.text}"
|
||
|
|
)
|
||
|
|
|
||
|
|
# Передача управления в тест
|
||
|
|
yield folder_name
|
||
|
|
|
||
|
|
# TEARDOWN: Гарантированная очистка созданного ресурса
|
||
|
|
disk_client.delete_resource(folder_name, permanently=True)
|
||
|
|
|
||
|
|
```
|
||
|
|
|
||
|
|
#### `tests/test_disk_crud.py`
|
||
|
|
|
||
|
|
```python
|
||
|
|
"""
|
||
|
|
Набор автоматических тестов для проверки REST API Яндекс Диска.
|
||
|
|
|
||
|
|
Покрывает:
|
||
|
|
- PUT: создание новой папки;
|
||
|
|
- GET: получение метаданных ресурса;
|
||
|
|
- POST: публикация ресурса и генерация публичной ссылки;
|
||
|
|
- DELETE: удаление ресурса мимо корзины;
|
||
|
|
- Негативные сценарии: дублирование, 404, неверный токен.
|
||
|
|
"""
|
||
|
|
|
||
|
|
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: создание новой папки.
|
||
|
|
Ожидаемый статус: 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 data.get("method") == "GET", "Метод перехода по ссылке должен быть 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: чтение метаданных существующей папки.
|
||
|
|
Ожидаемый статус: 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: публикация ресурса (открытие публичного доступа).
|
||
|
|
Ожидаемый статус: 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 info_data["public_url"].startswith("https://"), "Некорректный формат публичной ссылки"
|
||
|
|
|
||
|
|
def test_delete_folder_delete_success(self, disk_client: YandexDiskClient):
|
||
|
|
"""
|
||
|
|
Проверка метода DELETE: удаление ресурса.
|
||
|
|
Ожидаемый статус: 204 No Content. Последующий GET-запрос должен возвращать 404 Not Found.
|
||
|
|
"""
|
||
|
|
folder_name = f"dir_delete_{uuid.uuid4().hex[:8]}"
|
||
|
|
|
||
|
|
# Шаг 1: Создаем ресурс
|
||
|
|
create_resp = disk_client.create_folder(folder_name)
|
||
|
|
assert create_resp.status_code == 201
|
||
|
|
|
||
|
|
# Шаг 2: Удаляем созданный ресурс
|
||
|
|
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}"
|
||
|
|
|
||
|
|
# Шаг 3: Проверяем, что ресурс действительно отсутствует
|
||
|
|
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_conflict(self, disk_client: YandexDiskClient, temp_folder: str):
|
||
|
|
"""
|
||
|
|
Повторное создание уже существующей папки.
|
||
|
|
Ожидаемый статус: 409 Conflict.
|
||
|
|
"""
|
||
|
|
response = disk_client.create_folder(temp_folder)
|
||
|
|
assert response.status_code == 409, f"Ожидался 409, получен {response.status_code}"
|
||
|
|
|
||
|
|
data = response.json()
|
||
|
|
assert data.get("error") == "PlatformResourceAlreadyExists"
|
||
|
|
|
||
|
|
def test_get_non_existent_resource_not_found(self, disk_client: YandexDiskClient):
|
||
|
|
"""
|
||
|
|
Запрос метаданных несуществующего ресурса.
|
||
|
|
Ожидаемый статус: 404 Not Found.
|
||
|
|
"""
|
||
|
|
non_existent_path = f"dir_not_exist_{uuid.uuid4().hex}"
|
||
|
|
response = disk_client.get_resource_info(non_existent_path)
|
||
|
|
assert response.status_code == 404, f"Ожидался 404, получен {response.status_code}"
|
||
|
|
|
||
|
|
data = response.json()
|
||
|
|
assert data.get("error") == "DiskNotFoundError"
|
||
|
|
|
||
|
|
def test_unauthorized_request(self):
|
||
|
|
"""
|
||
|
|
Отправка запроса с невалидным токеном авторизации.
|
||
|
|
Ожидаемый статус: 401 Unauthorized.
|
||
|
|
"""
|
||
|
|
unauth_client = YandexDiskClient(token="invalid_dummy_token_999")
|
||
|
|
response = unauth_client.get_resource_info("any_test_folder")
|
||
|
|
assert response.status_code == 401, f"Ожидался 401, получен {response.status_code}"
|
||
|
|
|
||
|
|
data = response.json()
|
||
|
|
assert data.get("error") == "UnauthorizedError"
|
||
|
|
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### 3. CI/CD конфигурация
|
||
|
|
|
||
|
|
#### `.github/workflows/tests.yml`
|
||
|
|
|
||
|
|
```yaml
|
||
|
|
name: Run Yandex Disk API Tests
|
||
|
|
|
||
|
|
on:
|
||
|
|
push:
|
||
|
|
branches: [ main, master ]
|
||
|
|
pull_request:
|
||
|
|
branches: [ main, master ]
|
||
|
|
workflow_dispatch:
|
||
|
|
|
||
|
|
jobs:
|
||
|
|
test:
|
||
|
|
name: API Automation Tests
|
||
|
|
runs-on: ubuntu-latest
|
||
|
|
|
||
|
|
steps:
|
||
|
|
- name: Checkout source 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
|
||
|
|
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### 4. Оформление для GitHub: `README.md`
|
||
|
|
|
||
|
|
```markdown
|
||
|
|
# Yandex Disk REST API Automation Tests
|
||
|
|
|
||
|
|
Проект автоматизированного тестирования сервиса **Яндекс Диск** на базе **Python**, **pytest** и **requests**.
|
||
|
|
|
||
|
|
Проект проверяет работу ключевых HTTP-методов REST API:
|
||
|
|
- **PUT** — создание папки (создание ресурса);
|
||
|
|
- **GET** — получение метаданных созданного ресурса;
|
||
|
|
- **POST** — публикация ресурса (открытие публичного доступа);
|
||
|
|
- **DELETE** — удаление ресурса мимо Корзины;
|
||
|
|
- **Негативные сценарии** — дублирование ресурса (409), поиск отсутствующего ресурса (404), невалидный токен (401).
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 🛠 Стек технологий
|
||
|
|
- **Python 3.10+**
|
||
|
|
- **pytest** — тестовый фреймворк, фикстуры setup/teardown, маркировка тестов
|
||
|
|
- **requests** — HTTP-клиент с управлением сессиями
|
||
|
|
- **python-dotenv** — чтение конфигураций и токенов из `.env`
|
||
|
|
- **GitHub Actions** — автоматический прогон тестов при пуше/PR
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 📁 Структура проекта
|
||
|
|
|
||
|
|
```text
|
||
|
|
yandex-disk-api-tests/
|
||
|
|
├── .github/
|
||
|
|
│ └── workflows/
|
||
|
|
│ └── tests.yml # CI/CD пайплайн (GitHub Actions)
|
||
|
|
├── api/
|
||
|
|
│ ├── __init__.py
|
||
|
|
│ └── disk_client.py # Клиент Яндекс Диска (GET, POST, PUT, DELETE)
|
||
|
|
├── tests/
|
||
|
|
│ ├── __init__.py
|
||
|
|
│ ├── conftest.py # Сессионные фикстуры и изоляция тестовых данных
|
||
|
|
│ └── test_disk_crud.py # Тестовые сценарии
|
||
|
|
├── .env.example # Пример переменных окружения
|
||
|
|
├── .gitignore # Исключения версионирования
|
||
|
|
├── pytest.ini # Настройки запуска pytest
|
||
|
|
├── requirements.txt # Внешние зависимости
|
||
|
|
└── README.md # Документация
|
||
|
|
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 🔑 Подготовка токена авторизации
|
||
|
|
|
||
|
|
> ⚠️ По требованию задания запрещено использовать личную учетную запись для получения OAuth-токена.
|
||
|
|
|
||
|
|
1. Войдите в отдельный тестовый аккаунт на Яндексе.
|
||
|
|
2. Перейдите в [Полигон API Яндекс Диска](https://yandex.ru/dev/disk/poligon/) или создайте приложение в [Яндекс OAuth](https://oauth.yandex.ru/) с правами на Яндекс Диск.
|
||
|
|
3. Скопируйте полученный OAuth-токен.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 🚀 Локальный запуск
|
||
|
|
|
||
|
|
### 1. Клонирование репозитория
|
||
|
|
|
||
|
|
```bash
|
||
|
|
git clone [https://github.com/](https://github.com/)<your-username>/yandex-disk-api-tests.git
|
||
|
|
cd yandex-disk-api-tests
|
||
|
|
|
||
|
|
```
|
||
|
|
|
||
|
|
### 2. Развертывание виртуального окружения
|
||
|
|
|
||
|
|
* **Linux / macOS:**
|
||
|
|
```bash
|
||
|
|
python3 -m venv .venv
|
||
|
|
source .venv/bin/activate
|
||
|
|
|
||
|
|
```
|
||
|
|
|
||
|
|
|
||
|
|
* **Windows (PowerShell):**
|
||
|
|
```powershell
|
||
|
|
python -m venv .venv
|
||
|
|
.venv\Scripts\Activate.ps1
|
||
|
|
|
||
|
|
```
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
### 3. Установка зависимостей
|
||
|
|
|
||
|
|
```bash
|
||
|
|
pip install --upgrade pip
|
||
|
|
pip install -r requirements.txt
|
||
|
|
|
||
|
|
```
|
||
|
|
|
||
|
|
### 4. Настройка переменных окружения
|
||
|
|
|
||
|
|
Создайте файл `.env` на основе шаблона:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
cp .env.example .env
|
||
|
|
|
||
|
|
```
|
||
|
|
|
||
|
|
Укажите ваш OAuth-токен в файле `.env`:
|
||
|
|
|
||
|
|
```ini
|
||
|
|
YANDEX_DISK_TOKEN=ваш_токен_здесь
|
||
|
|
|
||
|
|
```
|
||
|
|
|
||
|
|
### 5. Запуск автотестов
|
||
|
|
|
||
|
|
* Запуск всех тестов:
|
||
|
|
```bash
|
||
|
|
pytest
|
||
|
|
|
||
|
|
```
|
||
|
|
|
||
|
|
|
||
|
|
* Запуск только CRUD-тестов:
|
||
|
|
```bash
|
||
|
|
pytest -m crud
|
||
|
|
|
||
|
|
```
|
||
|
|
|
||
|
|
|
||
|
|
* Запуск только негативных тестов:
|
||
|
|
```bash
|
||
|
|
pytest -m negative
|
||
|
|
|
||
|
|
```
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## ⚙️ Настройка CI/CD (GitHub Actions)
|
||
|
|
|
||
|
|
В проекте настроен автоматический запуск тестов при каждом пуше в ветки `main`/`master`.
|
||
|
|
|
||
|
|
Для работы тестов в GitHub Actions:
|
||
|
|
|
||
|
|
1. Перейдите в репозиторий: **Settings** → **Secrets and variables** → **Actions**.
|
||
|
|
2. Нажмите **New repository secret**.
|
||
|
|
3. Задайте имя: `YANDEX_DISK_TOKEN`, значение: `<ваш OAuth-токен>`.
|
||
|
|
4. Сохраните секрет. Теперь при каждом коммите тесты будут запускаться автоматически.
|
||
|
|
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### 5. Инструкция по публикации в GitHub
|
||
|
|
|
||
|
|
Выполните следующие команды в терминале для отправки проекта в новый публичный репозиторий:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# 1. Перейдите в папку проекта
|
||
|
|
cd yandex-disk-api-tests
|
||
|
|
|
||
|
|
# 2. Инициализируйте локальный репозиторий Git
|
||
|
|
git init
|
||
|
|
|
||
|
|
# 3. Добавьте файлы и сделайте первый коммит
|
||
|
|
git add .
|
||
|
|
git commit -m "feat: initial commit with Yandex Disk API tests and CI"
|
||
|
|
|
||
|
|
# 4. Переименуйте ветку в main
|
||
|
|
git branch -M main
|
||
|
|
|
||
|
|
# 5. Привяжите удаленный репозиторий GitHub (замените URL на свой)
|
||
|
|
git remote add origin https://github.com/<ваш-логин-github>/yandex-disk-api-tests.git
|
||
|
|
|
||
|
|
# 6. Отправьте код на GitHub
|
||
|
|
git push -u origin main
|
||
|
|
|
||
|
|
```
|