Files

116 lines
5.1 KiB
Python

"""Exercise the actual vendored SDK and native bot against a local HTTP API double."""
import asyncio
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
import json
from pathlib import Path
import sys
import tempfile
import threading
import unittest
from unittest.mock import patch
ROOT = Path(__file__).resolve().parents[1]
sys.path[:0] = [str(ROOT), str(ROOT / "vendor")]
from gusvoice_bot import AsyncClient, Client, GusVoiceApiError
from awg_gus.bot import Bot
from awg_gus.core import Delivery, State
from test_bot import CHAT, MSG, OWNER, FakeAwg, callback, message
class Handler(BaseHTTPRequestHandler):
def log_message(self, *args):
pass
def do_POST(self):
body = self.rfile.read(int(self.headers.get("Content-Length", "0")))
method = self.path.rsplit("/", 1)[-1]
self.server.received.append((method, self.headers, body))
if self.headers.get("Authorization") != "Bot gvb_test":
code, result = 401, {"ok": False, "error_code": 401, "description": "Unauthorized"}
elif self.server.fail_next:
self.server.fail_next = False
code, result = 429, {"ok": False, "error_code": 429, "description": "slow down", "retry_after": 0}
else:
code = 200
data = json.loads(body) if "application/json" in self.headers.get("Content-Type", "") else {}
if method in {"sendMessage", "editMessageText"}:
if data and (not isinstance(data.get("chat_id"), str) or data.get("parse_mode") not in {None, "HTML"}):
raise AssertionError("Wrong native API contract")
payload = {"message_id": MSG, "chat": {"id": CHAT, "type": "dm"}, "text": data.get("text", "")}
elif method == "getUpdates":
payload = []
elif method == "getWebhookInfo":
payload = {"url": ""}
else:
payload = True
result = {"ok": True, "result": payload}
raw = json.dumps(result).encode()
self.send_response(code)
self.send_header("Content-Type", "application/json")
self.send_header("Content-Length", str(len(raw)))
self.end_headers()
self.wfile.write(raw)
class TransportTests(unittest.IsolatedAsyncioTestCase):
def setUp(self):
self.server = ThreadingHTTPServer(("127.0.0.1", 0), Handler)
self.server.received = []
self.server.fail_next = False
self.thread = threading.Thread(target=self.server.serve_forever, daemon=True)
self.thread.start()
self.url = f"http://127.0.0.1:{self.server.server_port}"
def tearDown(self):
self.server.shutdown()
self.server.server_close()
self.thread.join()
async def test_real_sdk_native_menu_and_config_upload(self):
with tempfile.TemporaryDirectory() as tmp:
state = State(Path(tmp) / "state.db", "test")
client = AsyncClient("gvb_test", self.url, discover=False)
bot = Bot({"owners": [OWNER]}, FakeAwg(), Delivery(client), state)
try:
await bot.process(message(1, "/menu"))
await asyncio.gather(*bot.tasks.values())
with patch("awg_gus.bot.shutil.which", return_value=None):
await bot.process(callback(2, "conf:alice"))
await asyncio.gather(*bot.tasks.values())
methods = [r[0] for r in self.server.received]
self.assertIn("answerCallbackQuery", methods)
messages = [r for r in self.server.received if r[0] == "sendMessage"]
self.assertEqual(json.loads(messages[0][2])["chat_id"], CHAT)
self.assertIn(b'name="files[0]"', messages[-1][2])
self.assertIn(b'filename="alice.conf"', messages[-1][2])
self.assertIn("multipart/form-data", messages[-1][1]["Content-Type"])
finally:
state.db.close()
async def test_sdk_429_retry_preserves_uuid_and_payload(self):
self.server.fail_next = True
client = AsyncClient("gvb_test", self.url, discover=False)
await client.send_message(CHAT, "hello")
self.assertEqual(len(self.server.received), 2)
self.assertEqual(self.server.received[0][2], self.server.received[1][2])
async def test_sdk_unauthorized_not_retried(self):
client = AsyncClient("gvb_wrong", self.url, discover=False)
with self.assertRaises(GusVoiceApiError) as error:
await client.send_message(CHAT, "hello")
self.assertEqual(error.exception.code, 401)
self.assertEqual(len(self.server.received), 1)
async def test_delivery_edits_by_uuid_without_arithmetic(self):
delivery = Delivery(AsyncClient("gvb_test", self.url, discover=False))
await delivery.show(CHAT, "new screen", [[{"text": "Menu", "callback_data": "home"}]], MSG)
method, _, raw = self.server.received[0]
self.assertEqual(method, "editMessageText")
payload = json.loads(raw)
self.assertEqual(payload["message_id"], MSG)
self.assertTrue(payload["reply_markup"]["inline_keyboard"])
if __name__ == "__main__":
unittest.main()