Добавлены все базовые файлы для запуска проекта
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
import asyncio
|
||||
import json
|
||||
|
||||
import httpx
|
||||
import pytest
|
||||
|
||||
from config import Settings
|
||||
from n8n_client import N8NClient, WebhookError
|
||||
|
||||
|
||||
CHAT_URL = "https://n8n.example.test/webhook/126bdb74-0630-415d-a8f7-6f699eac1a2c"
|
||||
UPLOAD_URL = "https://n8n.example.test/webhook/993d3a50-19a7-4c64-9d36-67c0ced130dd"
|
||||
|
||||
|
||||
def settings() -> Settings:
|
||||
return Settings(chat_webhook_url=CHAT_URL, upload_webhook_url=UPLOAD_URL)
|
||||
|
||||
|
||||
def test_chat_sends_exact_contract_and_reads_plain_text() -> None:
|
||||
received: list[httpx.Request] = []
|
||||
|
||||
def respond(request: httpx.Request) -> httpx.Response:
|
||||
received.append(request)
|
||||
return httpx.Response(200, text="Ответ специалиста или агента")
|
||||
|
||||
client = N8NClient(settings(), httpx.MockTransport(respond))
|
||||
first = asyncio.run(client.ask(" Вопрос 1 ", "my-store", "session-1"))
|
||||
second = asyncio.run(client.ask("Вопрос 2", "my-store", "session-1"))
|
||||
|
||||
assert first == second == "Ответ специалиста или агента"
|
||||
assert [request.method for request in received] == ["POST", "POST"]
|
||||
assert all(str(request.url) == CHAT_URL for request in received)
|
||||
assert [json.loads(request.content) for request in received] == [
|
||||
{"message": "Вопрос 1", "collection_name": "my-store", "session_id": "session-1"},
|
||||
{"message": "Вопрос 2", "collection_name": "my-store", "session_id": "session-1"},
|
||||
]
|
||||
|
||||
|
||||
def test_upload_uses_one_multipart_file_and_collection() -> None:
|
||||
received: list[httpx.Request] = []
|
||||
|
||||
def respond(request: httpx.Request) -> httpx.Response:
|
||||
received.append(request)
|
||||
return httpx.Response(200, json={"unverified": "response shape"})
|
||||
|
||||
client = N8NClient(settings(), httpx.MockTransport(respond))
|
||||
assert asyncio.run(client.upload("notes.md", b"# Notes\n", "my-store")) is None
|
||||
|
||||
assert len(received) == 1
|
||||
request = received[0]
|
||||
assert request.method == "POST"
|
||||
assert str(request.url) == UPLOAD_URL
|
||||
assert request.headers["content-type"].startswith("multipart/form-data; boundary=")
|
||||
assert b'name="collection_name"' in request.content
|
||||
assert b"my-store" in request.content
|
||||
assert b'name="file"; filename="notes.md"' in request.content
|
||||
assert b"Content-Type: text/markdown" in request.content
|
||||
assert b"# Notes\n" in request.content
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"filename,contents,collection",
|
||||
[
|
||||
("notes.txt", b"text", "my-store"),
|
||||
("notes.md", b"", "my-store"),
|
||||
("notes.md", b"text", " "),
|
||||
("notes.md", b"12345", "my-store"),
|
||||
],
|
||||
)
|
||||
def test_invalid_upload_never_calls_webhook(
|
||||
filename: str, contents: bytes, collection: str
|
||||
) -> None:
|
||||
called = False
|
||||
|
||||
def respond(request: httpx.Request) -> httpx.Response:
|
||||
nonlocal called
|
||||
called = True
|
||||
return httpx.Response(200)
|
||||
|
||||
options = Settings(
|
||||
chat_webhook_url=CHAT_URL,
|
||||
upload_webhook_url=UPLOAD_URL,
|
||||
max_file_bytes=4,
|
||||
)
|
||||
client = N8NClient(options, httpx.MockTransport(respond))
|
||||
with pytest.raises(ValueError):
|
||||
asyncio.run(client.upload(filename, contents, collection))
|
||||
assert not called
|
||||
|
||||
|
||||
def test_chat_http_error_and_empty_reply_are_not_answers() -> None:
|
||||
for response in (httpx.Response(502, text="failure"), httpx.Response(200, text=" ")):
|
||||
client = N8NClient(settings(), httpx.MockTransport(lambda _: response))
|
||||
with pytest.raises(WebhookError):
|
||||
asyncio.run(client.ask("Вопрос", "my-store", "session-1"))
|
||||
|
||||
|
||||
def test_timeout_does_not_retry_upload() -> None:
|
||||
attempts = 0
|
||||
|
||||
def respond(request: httpx.Request) -> httpx.Response:
|
||||
nonlocal attempts
|
||||
attempts += 1
|
||||
raise httpx.ReadTimeout("timeout", request=request)
|
||||
|
||||
client = N8NClient(settings(), httpx.MockTransport(respond))
|
||||
with pytest.raises(WebhookError, match="ожидания загрузки"):
|
||||
asyncio.run(client.upload("notes.md", b"# Notes", "my-store"))
|
||||
assert attempts == 1
|
||||
Reference in New Issue
Block a user