- Отдельная PostgreSQL в tmpfs (RAM) - Отдельный Tracker на порту 8101 - Полная изоляция от прода - ./test.sh для запуска
125 lines
3.9 KiB
Python
125 lines
3.9 KiB
Python
"""
|
|
Conftest для E2E тестов Team Board.
|
|
Ожидает что тестовый Tracker уже запущен на порту 8101 (через test.sh).
|
|
"""
|
|
import os
|
|
import pytest
|
|
import pytest_asyncio
|
|
import httpx
|
|
import uuid
|
|
from typing import Dict, Any
|
|
|
|
TEST_PORT = os.environ.get("TEST_PORT", "8101")
|
|
BASE_URL = f"http://localhost:{TEST_PORT}/api/v1"
|
|
WS_URL = f"ws://localhost:{TEST_PORT}"
|
|
|
|
|
|
@pytest.fixture(scope="session")
|
|
def base_url():
|
|
return BASE_URL
|
|
|
|
|
|
@pytest.fixture(scope="session")
|
|
def ws_url():
|
|
return WS_URL
|
|
|
|
|
|
@pytest_asyncio.fixture
|
|
async def admin_token(base_url: str) -> str:
|
|
async with httpx.AsyncClient() as client:
|
|
response = await client.post(f"{base_url}/auth/login", json={
|
|
"login": "admin", "password": "teamboard"
|
|
})
|
|
assert response.status_code == 200, f"Login failed: {response.text}"
|
|
return response.json()["token"]
|
|
|
|
|
|
@pytest.fixture
|
|
def agent_token():
|
|
return "tb-coder-dev-token"
|
|
|
|
|
|
@pytest_asyncio.fixture
|
|
async def http_client(base_url: str, admin_token: str):
|
|
headers = {"Authorization": f"Bearer {admin_token}"}
|
|
async with httpx.AsyncClient(base_url=base_url, headers=headers, timeout=30.0) as client:
|
|
yield client
|
|
|
|
|
|
@pytest_asyncio.fixture
|
|
async def agent_client(base_url: str, agent_token: str):
|
|
headers = {"Authorization": f"Bearer {agent_token}"}
|
|
async with httpx.AsyncClient(base_url=base_url, headers=headers, timeout=30.0) as client:
|
|
yield client
|
|
|
|
|
|
@pytest_asyncio.fixture
|
|
async def test_project(http_client: httpx.AsyncClient) -> Dict[str, Any]:
|
|
slug = f"test-{uuid.uuid4().hex[:8]}"
|
|
response = await http_client.post("/projects", json={
|
|
"name": f"Test {slug}", "slug": slug, "description": "E2E test",
|
|
})
|
|
assert response.status_code == 200
|
|
project = response.json()
|
|
yield project
|
|
await http_client.delete(f"/projects/{project['id']}")
|
|
|
|
|
|
@pytest_asyncio.fixture
|
|
async def test_user(http_client: httpx.AsyncClient) -> Dict[str, Any]:
|
|
slug = f"user-{uuid.uuid4().hex[:8]}"
|
|
response = await http_client.post("/members", json={
|
|
"name": f"User {slug}", "slug": slug, "type": "human", "role": "member",
|
|
})
|
|
assert response.status_code == 200
|
|
yield response.json()
|
|
|
|
|
|
@pytest_asyncio.fixture
|
|
async def test_agent(http_client: httpx.AsyncClient) -> Dict[str, Any]:
|
|
slug = f"agent-{uuid.uuid4().hex[:8]}"
|
|
response = await http_client.post("/members", json={
|
|
"name": f"Agent {slug}", "slug": slug, "type": "agent", "role": "member",
|
|
"agent_config": {"capabilities": ["coding"], "labels": ["backend"],
|
|
"chat_listen": "mentions", "task_listen": "assigned"},
|
|
})
|
|
assert response.status_code == 200
|
|
yield response.json()
|
|
|
|
|
|
@pytest_asyncio.fixture
|
|
async def test_task(http_client: httpx.AsyncClient, test_project: Dict[str, Any]) -> Dict[str, Any]:
|
|
response = await http_client.post(f"/tasks?project_id={test_project['id']}", json={
|
|
"title": "Test Task", "description": "E2E test", "type": "task",
|
|
"status": "backlog", "priority": "medium",
|
|
})
|
|
assert response.status_code == 200
|
|
yield response.json()
|
|
|
|
|
|
@pytest_asyncio.fixture
|
|
async def test_label(http_client: httpx.AsyncClient) -> Dict[str, Any]:
|
|
name = f"label-{uuid.uuid4().hex[:8]}"
|
|
response = await http_client.post("/labels", json={"name": name, "color": "#ff5733"})
|
|
assert response.status_code == 200
|
|
label = response.json()
|
|
yield label
|
|
await http_client.delete(f"/labels/{label['id']}")
|
|
|
|
|
|
# ---------- Helpers ----------
|
|
|
|
def assert_uuid(value: str):
|
|
try:
|
|
uuid.UUID(value)
|
|
except (ValueError, TypeError):
|
|
pytest.fail(f"'{value}' is not a valid UUID")
|
|
|
|
|
|
def assert_timestamp(value: str):
|
|
import datetime
|
|
try:
|
|
datetime.datetime.fromisoformat(value.replace('Z', '+00:00'))
|
|
except ValueError:
|
|
pytest.fail(f"'{value}' is not a valid ISO timestamp")
|