feat(workspaces): autocomplete trusted workspace paths — v0.50.162 (PR #880 by @franksong2702, closes #616)
Adds GET /api/workspaces/suggest endpoint and autocomplete dropdown in the Spaces panel. Suggestions limited to trusted roots (home, saved workspaces, boot default). Keyboard nav, Tab completion, hidden dir support. Symlink-escape and dotdot-escape invariants locked by regression tests.
This commit is contained in:
17
tests/test_issue616.py
Normal file
17
tests/test_issue616.py
Normal file
@@ -0,0 +1,17 @@
|
||||
import pathlib
|
||||
|
||||
|
||||
def test_workspace_suggest_endpoint_is_wired():
|
||||
src = pathlib.Path("api/routes.py").read_text(encoding="utf-8")
|
||||
assert '"/api/workspaces/suggest"' in src
|
||||
|
||||
|
||||
def test_spaces_panel_uses_workspace_suggest_autocomplete():
|
||||
src = pathlib.Path("static/panels.js").read_text(encoding="utf-8")
|
||||
assert "/api/workspaces/suggest" in src
|
||||
assert "wsAddSuggestions" in src
|
||||
assert "scheduleWorkspacePathSuggestions" in src
|
||||
assert "if(!prefix)" in src
|
||||
assert "dataset.path" in src
|
||||
assert "scrollIntoView" in src
|
||||
assert "_wsSuggestIndex=0" in src
|
||||
@@ -1,5 +1,5 @@
|
||||
"""Sprint 5 tests: workspace CRUD, file save, session index, JS serving."""
|
||||
import json, pathlib, uuid, urllib.request, urllib.error
|
||||
import json, pathlib, uuid, urllib.request, urllib.error, urllib.parse
|
||||
import os
|
||||
|
||||
from tests._pytest_port import BASE
|
||||
@@ -80,6 +80,34 @@ def test_workspace_add_requires_path():
|
||||
result, status = post("/api/workspaces/add", {})
|
||||
assert status == 400
|
||||
|
||||
def test_workspace_suggest_returns_trusted_directories(cleanup_test_sessions):
|
||||
_, ws = make_session_tracked(cleanup_test_sessions)
|
||||
child = make_workspace_child(ws, f"workspace-suggest-{uuid.uuid4().hex[:6]}")
|
||||
nested = make_workspace_child(child, "nested")
|
||||
prefix = str(child.parent / child.name[:12])
|
||||
data, status = get(f"/api/workspaces/suggest?prefix={urllib.parse.quote(prefix)}")
|
||||
assert status == 200
|
||||
assert str(child) in data["suggestions"]
|
||||
assert all(not pathlib.Path(p).name.startswith('.') for p in data["suggestions"])
|
||||
|
||||
def test_workspace_suggest_hides_untrusted_system_prefix():
|
||||
data, status = get("/api/workspaces/suggest?prefix=/etc")
|
||||
assert status == 200
|
||||
assert data["suggestions"] == []
|
||||
|
||||
def test_workspace_suggest_hidden_dirs_only_when_requested(cleanup_test_sessions):
|
||||
_, ws = make_session_tracked(cleanup_test_sessions)
|
||||
hidden = make_workspace_child(ws, ".workspace-hidden")
|
||||
visible = make_workspace_child(ws, "workspace-visible")
|
||||
base = str(ws) + "/"
|
||||
data, status = get(f"/api/workspaces/suggest?prefix={urllib.parse.quote(base)}")
|
||||
assert status == 200
|
||||
assert str(visible) in data["suggestions"]
|
||||
assert str(hidden) not in data["suggestions"]
|
||||
data2, status2 = get(f"/api/workspaces/suggest?prefix={urllib.parse.quote(base + '.w')}")
|
||||
assert status2 == 200
|
||||
assert str(hidden) in data2["suggestions"]
|
||||
|
||||
def test_workspace_remove(cleanup_test_sessions):
|
||||
_, ws = make_session_tracked(cleanup_test_sessions)
|
||||
child = make_workspace_child(ws, f"workspace-remove-{uuid.uuid4().hex[:6]}")
|
||||
|
||||
Reference in New Issue
Block a user