import json import sys from pathlib import Path import httpx import pytest import respx sys.path.insert(0, str(Path(__file__).resolve().parents[1])) import qa_client # noqa: E402 @pytest.fixture(autouse=True) def qa_env(monkeypatch): monkeypatch.setenv("QA_SERVICE_URL", "https://qa.example.com") monkeypatch.setenv("QA_SERVICE_API_KEY", "svc-key-789") @pytest.mark.asyncio async def test_publish_doc_posts_to_docs(): with respx.mock: route = respx.post("https://qa.example.com/docs").mock( return_value=httpx.Response(200, json={"slug": "abc", "url": "https://qa.example.com/docs/abc"}) ) result = await qa_client.publish_doc("Заголовок", "текст", "https://trello.com/c/x") assert route.called request = route.calls.last.request assert request.headers["X-API-Key"] == "svc-key-789" assert result == {"slug": "abc", "url": "https://qa.example.com/docs/abc"} @pytest.mark.asyncio async def test_update_doc_puts_to_docs_slug(): with respx.mock: route = respx.put("https://qa.example.com/docs/abc").mock( return_value=httpx.Response(200, json={"slug": "abc", "url": "https://qa.example.com/docs/abc"}) ) result = await qa_client.update_doc( "abc", "Новый заголовок", "новый текст", "https://trello.com/c/x" ) assert route.called request = route.calls.last.request assert request.headers["X-API-Key"] == "svc-key-789" assert result == {"slug": "abc", "url": "https://qa.example.com/docs/abc"} @pytest.mark.asyncio async def test_update_doc_missing_slug_raises(): with respx.mock: respx.put("https://qa.example.com/docs/does-not-exist").mock( return_value=httpx.Response(404, json={"detail": "Doc not found"}) ) with pytest.raises(httpx.HTTPStatusError): await qa_client.update_doc("does-not-exist", "T", "текст") def test_update_doc_requires_config(monkeypatch): monkeypatch.delenv("QA_SERVICE_URL", raising=False) monkeypatch.delenv("QA_SERVICE_API_KEY", raising=False) with pytest.raises(qa_client.QaServiceConfigError): qa_client._config() @pytest.mark.asyncio async def test_get_doc_fetches_raw_by_slug_or_url(): doc = {"slug": "abc", "markdown": "текст", "scenarios": []} with respx.mock: route = respx.get("https://qa.example.com/docs/abc/raw").mock( return_value=httpx.Response(200, json=doc) ) by_slug = await qa_client.get_doc("abc") by_url = await qa_client.get_doc("https://qa.example.com/docs/abc/") assert route.call_count == 2 assert route.calls.last.request.headers["X-API-Key"] == "svc-key-789" assert by_slug == by_url == doc @pytest.mark.asyncio async def test_publish_doc_sends_problem(): with respx.mock: route = respx.post("https://qa.example.com/docs").mock( return_value=httpx.Response(200, json={"slug": "abc", "url": "u"}) ) await qa_client.publish_doc("T", "текст", problem="Падает экспорт") body = json.loads(route.calls.last.request.content) assert body["problem"] == "Падает экспорт" assert body["card_url"] is None @pytest.mark.asyncio async def test_publish_task_uses_bearer_api_key(): with respx.mock: route = respx.post("https://qa.example.com/tasks").mock( return_value=httpx.Response(200, json={"slug": "t1"}) ) await qa_client.publish_task("front", "Кнопка", "текст", "https://trello.com/c/x") request = route.calls.last.request assert request.headers["Authorization"] == "Bearer svc-key-789" assert json.loads(request.content) == { "title": "Кнопка", "markdown": "текст", "target": "front", "card_url": "https://trello.com/c/x", } @pytest.mark.asyncio async def test_tasks_work_with_tasks_token_only(monkeypatch): # Claude на фронте: основного ключа нет, только токен заданий. monkeypatch.delenv("QA_SERVICE_API_KEY") monkeypatch.setenv("QA_TASKS_TOKEN", "tasks-tok") with respx.mock: route = respx.get("https://qa.example.com/tasks/t1/raw").mock( return_value=httpx.Response(200, json={"slug": "t1"}) ) await qa_client.get_task("https://qa.example.com/tasks/t1") assert route.calls.last.request.headers["Authorization"] == "Bearer tasks-tok" with pytest.raises(qa_client.QaServiceConfigError): qa_client._config() @pytest.mark.asyncio async def test_update_and_list_tasks(): with respx.mock: put = respx.put("https://qa.example.com/tasks/t1").mock( return_value=httpx.Response(200, json={"slug": "t1"}) ) listing = respx.get("https://qa.example.com/tasks").mock( return_value=httpx.Response(200, json=[]) ) await qa_client.update_task("t1", "back", "T", "v2") await qa_client.list_tasks(card_url="https://trello.com/c/abc", target=None) assert json.loads(put.calls.last.request.content)["target"] == "back" assert dict(listing.calls.last.request.url.params) == {"card": "https://trello.com/c/abc"}