- Postgres в compose; пакет db (SQLAlchemy async): clients, windows, requests. - Все эндпоинты, кроме /health, — только с ключом в X-API-Key, иначе 401; без выпущенных ключей доступа нет. В базе — только sha256 ключа. - Задача и окно сессии привязаны к клиенту: чужой ключ их не видит. - Воркер пишет журнал: окно (открыто, последнее обращение, закрыто) и задачу (в очереди, начало, конец, итог, данные без секретов). Сбой записи не роняет задачу. - python -m db.cli create|list|revoke|stats; make key-create/key-list/key-revoke/stats. - smoke и loadtest с ключом. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
9dc2c0b619
commit
1a4bd8d672
34 files changed
+857
-81
No files matched your search
+24
-3
@@ -1,5 +1,6 @@
|
||||
"""api и worker в одном процессе без RabbitMQ и Redis: Redis — fakeredis, отправка в очередь подменена
|
||||
доставкой сообщения прямо в WindowPool воркера (tests/unit, tests/browser)."""
|
||||
import asyncio
|
||||
import json
|
||||
from contextlib import contextmanager
|
||||
|
||||
@@ -7,6 +8,9 @@ import fakeredis
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
import main
|
||||
from db import clients
|
||||
from db.engine import connect as connect_db
|
||||
from db.journal import Journal
|
||||
from tasks import store as task_store
|
||||
from tasks.broker import RUN_TASK
|
||||
from worker import windows
|
||||
@@ -49,17 +53,34 @@ class Harness:
|
||||
return self.client.portal.call(fn, *args)
|
||||
|
||||
|
||||
def make_client_key(db, name="test") -> str:
|
||||
async def create():
|
||||
await db.init()
|
||||
return (await clients.create_client(db, name))[1]
|
||||
key = asyncio.run(create())
|
||||
asyncio.run(db.close()) # соединения sqlite привязаны к циклу asyncio.run — закрываем, приложение откроет свои
|
||||
return key
|
||||
|
||||
|
||||
@contextmanager
|
||||
def run_env(monkeypatch, tmp_path, slots=2):
|
||||
server = fakeredis.FakeServer()
|
||||
store = task_store.TaskStore(fakeredis.aioredis.FakeRedis(server=server, decode_responses=True))
|
||||
pool = windows.WindowPool(store, WORKER_ID, slots=slots)
|
||||
db = connect_db(f"sqlite+aiosqlite:///{tmp_path / 'robotex.db'}")
|
||||
key = make_client_key(db)
|
||||
pool = windows.WindowPool(store, WORKER_ID, slots=slots, journal=Journal(db))
|
||||
harness = Harness(store, pool, fakeredis.FakeRedis(server=server, decode_responses=True))
|
||||
monkeypatch.setattr(windows, "USER_DATA_ROOT", tmp_path)
|
||||
harness.db, harness.api_key = db, key
|
||||
# профили — в отдельном каталоге: в tmp_path ещё и файл базы
|
||||
profiles = tmp_path / "profiles"
|
||||
profiles.mkdir()
|
||||
harness.profiles = profiles
|
||||
monkeypatch.setattr(windows, "USER_DATA_ROOT", profiles)
|
||||
monkeypatch.setattr(main, "store", store)
|
||||
monkeypatch.setattr(main, "database", db)
|
||||
monkeypatch.setattr(main, "dispatch", harness.dispatch)
|
||||
harness.heartbeat()
|
||||
with TestClient(main.app) as client:
|
||||
with TestClient(main.app, headers={"X-API-Key": key}) as client:
|
||||
harness.client = client
|
||||
yield harness
|
||||
client.portal.call(pool.close_all)
|
||||
Reference in new issue
Block a user