feat(api): FastAPI — auth, RFC 7807 и роуты фазы 1
Префикс /api/v1, operationId = "<tag>_<name>", чтобы Dart-клиент получил методы вроде accountsList, а не list_accounts_api_v1_accounts_get. Ошибки — application/problem+json. Auth: access-JWT на час + refresh на 30 дней, который хранится хэшем и ротируется, логин ограничен по частоте в памяти. Деньги в JSON — всегда позиционные строки (api/schemas/common.py): float в проводе потерял бы копейки, которые NUMERIC(24,10) бережёт. Тестовый harness поднимает свой Postgres через pg_ctl (pytest-postgresql), мигрирует его один раз на сессию и усекает таблицы после каждого теста.
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
from httpx import ASGITransport, AsyncClient
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def web_client(migrated: str, tmp_path: Path, monkeypatch):
|
||||
(tmp_path / "index.html").write_text("<!doctype html><title>spa</title>")
|
||||
(tmp_path / "main.dart.js").write_text("console.log(1)")
|
||||
monkeypatch.setenv("WEB_DIR", str(tmp_path))
|
||||
from fintracker.api.app import create_app
|
||||
from fintracker.config import get_settings
|
||||
from fintracker.db import reset_engine
|
||||
|
||||
get_settings.cache_clear()
|
||||
app = create_app()
|
||||
async with AsyncClient(transport=ASGITransport(app=app), base_url="http://test") as c:
|
||||
yield c
|
||||
get_settings.cache_clear()
|
||||
await reset_engine()
|
||||
|
||||
|
||||
async def test_spa_fallback_and_api_precedence(web_client):
|
||||
assert (await web_client.get("/")).text.startswith("<!doctype html>")
|
||||
assert (await web_client.get("/login")).text.startswith("<!doctype html>") # deep link
|
||||
assert (await web_client.get("/main.dart.js")).text == "console.log(1)"
|
||||
assert (await web_client.get("/api/v1/health")).json()["status"] == "ok"
|
||||
r = await web_client.get("/api/v1/nope")
|
||||
assert r.status_code == 404 and r.headers["content-type"].startswith("application/problem+json")
|
||||
assert (await web_client.get("/../etc/passwd")).text.startswith("<!doctype html>")
|
||||
Reference in New Issue
Block a user