feat: vault-aligned server v1 + UX polish
All checks were successful
CI / Windows build (push) Successful in 7m47s
All checks were successful
CI / Windows build (push) Successful in 7m47s
Redesign the optional FastAPI companion around vault files (manifest / PUT/GET/DELETE + OCR jobs) instead of legacy strokes_json notes. Wire a client Server settings panel for health/login. Polish shell UX: l10n for settings/home/board, sticky-board empty state, and a narrow-screen diagnostics FAB. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -14,6 +14,7 @@ sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
|
||||
|
||||
os.environ["BADNOTE_STORAGE_PATH"] = "/tmp/badnote_test_storage"
|
||||
os.environ["BADNOTE_QUEUE_PATH"] = "/tmp/badnote_test_queue"
|
||||
os.environ["BADNOTE_VAULT_PATH"] = "/tmp/badnote_test_vaults"
|
||||
os.environ["BADNOTE_JWT_SECRET"] = "test-secret-key-for-testing-only"
|
||||
|
||||
from badnote_server.config import settings # noqa: E402
|
||||
@@ -26,10 +27,13 @@ async def setup_db(tmp_path):
|
||||
"""Fresh DB and clean queue for each test."""
|
||||
db_path = str(tmp_path / "test.db")
|
||||
settings.db_path = db_path
|
||||
settings.vault_path = str(tmp_path / "vaults")
|
||||
# Clean the queue directory before each test
|
||||
queue_path = settings.queue_path
|
||||
if os.path.exists(queue_path):
|
||||
shutil.rmtree(queue_path)
|
||||
if os.path.exists(settings.vault_path):
|
||||
shutil.rmtree(settings.vault_path)
|
||||
await init_db()
|
||||
yield
|
||||
await close_db()
|
||||
|
||||
88
server/tests/test_vault_v1.py
Normal file
88
server/tests/test_vault_v1.py
Normal file
@@ -0,0 +1,88 @@
|
||||
"""Tests for v1 vault API (async, uses shared conftest)."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
async def _token(client) -> str:
|
||||
r = await client.post(
|
||||
"/api/v1/auth/register",
|
||||
json={"username": "vault_user", "password": "password123"},
|
||||
)
|
||||
if r.status_code == 409:
|
||||
r = await client.post(
|
||||
"/api/v1/auth/login",
|
||||
json={"username": "vault_user", "password": "password123"},
|
||||
)
|
||||
assert r.status_code in (200, 201), r.text
|
||||
return r.json()["token"]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_health(client):
|
||||
r = await client.get("/api/v1/health")
|
||||
assert r.status_code == 200
|
||||
body = r.json()
|
||||
assert body["api"] == "v1"
|
||||
assert "vault" in body["features"]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_vault_roundtrip(client, tmp_path, monkeypatch):
|
||||
from badnote_server.config import settings
|
||||
|
||||
monkeypatch.setattr(settings, "vault_path", str(tmp_path / "vaults"))
|
||||
|
||||
token = await _token(client)
|
||||
headers = {"Authorization": f"Bearer {token}"}
|
||||
|
||||
files = {"upload": ("hello.txt", b"hello vault", "text/plain")}
|
||||
r = await client.put(
|
||||
"/api/v1/vault/files/NotebookA/hello.txt",
|
||||
headers=headers,
|
||||
files=files,
|
||||
)
|
||||
assert r.status_code == 200, r.text
|
||||
assert r.json()["path"] == "NotebookA/hello.txt"
|
||||
assert r.json()["size"] == 11
|
||||
|
||||
r = await client.get("/api/v1/vault/manifest", headers=headers)
|
||||
assert r.status_code == 200
|
||||
paths = [f["path"] for f in r.json()["files"]]
|
||||
assert "NotebookA/hello.txt" in paths
|
||||
|
||||
r = await client.get(
|
||||
"/api/v1/vault/files/NotebookA/hello.txt",
|
||||
headers=headers,
|
||||
)
|
||||
assert r.status_code == 200
|
||||
assert r.content == b"hello vault"
|
||||
|
||||
r = await client.delete(
|
||||
"/api/v1/vault/files/NotebookA/hello.txt",
|
||||
headers=headers,
|
||||
)
|
||||
assert r.status_code == 200
|
||||
r = await client.get(
|
||||
"/api/v1/vault/files/NotebookA/hello.txt",
|
||||
headers=headers,
|
||||
)
|
||||
assert r.status_code == 404
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_path_escape_rejected(client, tmp_path, monkeypatch):
|
||||
from badnote_server.config import settings
|
||||
|
||||
monkeypatch.setattr(settings, "vault_path", str(tmp_path / "vaults"))
|
||||
|
||||
token = await _token(client)
|
||||
headers = {"Authorization": f"Bearer {token}"}
|
||||
files = {"upload": ("x", b"nope", "application/octet-stream")}
|
||||
r = await client.put(
|
||||
"/api/v1/vault/files/../../etc/passwd",
|
||||
headers=headers,
|
||||
files=files,
|
||||
)
|
||||
assert r.status_code in (400, 404)
|
||||
Reference in New Issue
Block a user