- graph blueprint: GET /api/graph resolves note_links to target notes by
normalized title (owner-scoped, non-trashed, self-excluded) → {nodes, edges}
of connected notes.
- GraphView: hand-rolled force simulation (repulsion + edge springs + centering,
cooling over ~400 frames), SVG nodes/edges, click a node to open it (reuses the
editor with link navigation). Sidebar Graph entry + route; empty state.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FRgehjoz7Yv8LkUfADxACm
99 lines
2.8 KiB
Python
99 lines
2.8 KiB
Python
import pytest
|
|
|
|
from thoughtsync.app import create_app
|
|
from thoughtsync.models.note import NOTE_COLORS, Note
|
|
from thoughtsync.notes import is_empty_note, normalize_color, parse_link_titles
|
|
|
|
|
|
@pytest.fixture
|
|
def app():
|
|
return create_app()
|
|
|
|
|
|
def test_is_empty_note():
|
|
assert is_empty_note(None, None)
|
|
assert is_empty_note("", " ")
|
|
assert not is_empty_note("title", "")
|
|
assert not is_empty_note("", "body")
|
|
|
|
|
|
def test_normalize_color():
|
|
assert normalize_color("blue") == "blue"
|
|
assert normalize_color("chartreuse") == "default"
|
|
assert normalize_color(None) == "default"
|
|
assert normalize_color(123) == "default"
|
|
|
|
|
|
def test_palette_has_core_colors():
|
|
for c in ("default", "red", "orange", "yellow", "green", "teal", "blue", "purple", "pink", "gray"):
|
|
assert c in NOTE_COLORS
|
|
|
|
|
|
def test_serialize_shape():
|
|
n = Note(title="t", body="b", color="blue", pinned=True, archived=False)
|
|
s = n.serialize()
|
|
assert s["title"] == "t"
|
|
assert s["body"] == "b"
|
|
assert s["color"] == "blue"
|
|
assert s["pinned"] is True
|
|
assert s["archived"] is False
|
|
assert s["trashed"] is False
|
|
|
|
|
|
async def test_notes_list_requires_auth(app):
|
|
client = app.test_client()
|
|
resp = await client.get("/api/notes")
|
|
assert resp.status_code == 401
|
|
|
|
|
|
async def test_notes_create_requires_auth(app):
|
|
client = app.test_client()
|
|
resp = await client.post("/api/notes", json={"body": "hi"})
|
|
assert resp.status_code == 401
|
|
|
|
|
|
async def test_search_requires_auth(app):
|
|
client = app.test_client()
|
|
resp = await client.get("/api/notes/search?q=hello")
|
|
assert resp.status_code == 401
|
|
|
|
|
|
async def test_add_item_requires_auth(app):
|
|
client = app.test_client()
|
|
resp = await client.post("/api/notes/00000000-0000-0000-0000-000000000000/items", json={"text": "x"})
|
|
assert resp.status_code == 401
|
|
|
|
|
|
async def test_upload_attachment_requires_auth(app):
|
|
client = app.test_client()
|
|
resp = await client.post("/api/notes/00000000-0000-0000-0000-000000000000/attachments")
|
|
assert resp.status_code == 401
|
|
|
|
|
|
async def test_reorder_requires_auth(app):
|
|
client = app.test_client()
|
|
resp = await client.post("/api/notes/reorder", json={"ids": []})
|
|
assert resp.status_code == 401
|
|
|
|
|
|
def test_parse_link_titles():
|
|
titles = parse_link_titles("see [[Alpha]] and [[ beta ]] and [[Alpha]] again")
|
|
assert titles == ["alpha", "beta"]
|
|
|
|
|
|
def test_parse_link_titles_empty():
|
|
assert parse_link_titles(None) == []
|
|
assert parse_link_titles("no links here") == []
|
|
|
|
|
|
async def test_titles_requires_auth(app):
|
|
client = app.test_client()
|
|
resp = await client.get("/api/notes/titles")
|
|
assert resp.status_code == 401
|
|
|
|
|
|
async def test_graph_requires_auth(app):
|
|
client = app.test_client()
|
|
resp = await client.get("/api/graph")
|
|
assert resp.status_code == 401
|