fix: fixed bds2: protocol handling

This commit is contained in:
2026-06-30 21:07:26 +02:00
parent 49675a49d2
commit ba9634c478
29 changed files with 2876 additions and 2242 deletions

View File

@@ -4,29 +4,74 @@ defmodule BDS.Desktop.DeepLinkTest do
alias BDS.Desktop.DeepLink
setup do
topic = "deep-link-test:#{System.unique_integer([:positive])}"
Phoenix.PubSub.subscribe(BDS.PubSub, topic)
{:ok, pid} = DeepLink.start_link(name: nil, pubsub: BDS.PubSub, topic: topic)
{:ok, pid} = DeepLink.start_link(name: nil)
on_exit(fn -> if Process.alive?(pid), do: GenServer.stop(pid) end)
%{pid: pid, topic: topic}
%{pid: pid}
end
test "broadcasts a bds2:// open_url event to the shell topic", %{pid: pid} do
test "delivers a bds2:// link (wx charlist payload) to an attached shell", %{pid: pid} do
:ok = DeepLink.attach(self(), pid)
url = "bds2://new-post?title=Hello&url=https://example.com"
send(pid, {:open_url, [url]})
# This is the exact shape erlang :wx sends on macOS: {:open_url, charlist}.
send(pid, {:open_url, ~c"bds2://new-post?title=Hello&url=https://example.com"})
assert_receive {:blogmark_deep_link, ^url}, 500
end
test "also accepts a binary payload", %{pid: pid} do
:ok = DeepLink.attach(self(), pid)
url = "bds2://new-post?title=Bin"
send(pid, {:open_url, url})
assert_receive {:blogmark_deep_link, ^url}, 500
end
test "queues a link that arrives before a shell attaches, then replays it", %{pid: pid} do
url = "bds2://new-post?title=Cold&url=https://example.com"
send(pid, {:open_url, String.to_charlist(url)})
# Nothing is connected yet, so nothing is delivered.
refute_receive {:blogmark_deep_link, _}, 100
:ok = DeepLink.attach(self(), pid)
assert_receive {:blogmark_deep_link, ^url}, 500
end
test "replays multiple cold-start links in arrival order", %{pid: pid} do
send(pid, {:open_url, ~c"bds2://new-post?title=One"})
send(pid, {:open_url, ~c"bds2://new-post?title=Two"})
:ok = DeepLink.attach(self(), pid)
assert_receive {:blogmark_deep_link, "bds2://new-post?title=One"}, 500
assert_receive {:blogmark_deep_link, "bds2://new-post?title=Two"}, 500
end
test "queues again after the attached shell dies", %{pid: pid} do
task = Task.async(fn -> DeepLink.attach(self(), pid) end)
Task.await(task)
# task process is now dead; DeepLink should observe the DOWN and re-queue.
Process.sleep(50)
url = "bds2://new-post?title=After&url=https://example.com"
send(pid, {:open_url, String.to_charlist(url)})
:ok = DeepLink.attach(self(), pid)
assert_receive {:blogmark_deep_link, ^url}, 500
end
test "ignores non-bds2 open_url events", %{pid: pid} do
send(pid, {:open_url, ["https://example.com"]})
:ok = DeepLink.attach(self(), pid)
send(pid, {:open_url, ~c"https://example.com"})
refute_receive {:blogmark_deep_link, _url}, 200
end
test "ignores unrelated messages", %{pid: pid} do
:ok = DeepLink.attach(self(), pid)
send(pid, {:open_file, ["/tmp/x"]})
refute_receive {:blogmark_deep_link, _url}, 200

View File

@@ -904,6 +904,59 @@ defmodule BDS.Desktop.ShellLiveTest do
assert html =~ ~s(data-tab-id="#{created_post.id}")
end
test "blogmark deep link with a project_id switches to that project and imports there",
%{project: active} do
{:ok, other} =
Projects.create_project(%{
name: "Other Blog",
data_path: Path.join(System.tmp_dir!(), "bds-other-#{System.unique_integer([:positive])}")
})
{:ok, view, _html} = live_isolated(build_conn(), BDS.Desktop.ShellLive)
url =
"bds2://new-post?title=" <>
URI.encode_www_form("Cross Project") <>
"&url=" <> URI.encode_www_form("https://example.com/x") <>
"&project_id=" <> other.id
send(view.pid, {:blogmark_deep_link, url})
html = render(view)
created_post = Repo.get_by!(Post, title: "Cross Project")
# Imported into the link's project, not the one that was active.
assert created_post.project_id == other.id
refute created_post.project_id == active.id
# And the shell switched the active project to the link's target.
assert Projects.get_active_project().id == other.id
assert html =~ ~s(data-tab-id="#{created_post.id}")
end
test "blogmark deep link with an unknown project_id fails without importing", %{project: active} do
{:ok, view, _html} = live_isolated(build_conn(), BDS.Desktop.ShellLive)
post_count_before = Repo.aggregate(Post, :count, :id)
url =
"bds2://new-post?title=" <>
URI.encode_www_form("Stale Link") <>
"&url=" <> URI.encode_www_form("https://example.com/s") <>
"&project_id=does-not-exist-00000000-0000-0000-0000-000000000000"
send(view.pid, {:blogmark_deep_link, url})
_html = render(view)
# No post is created, and the active project is left untouched.
assert Repo.aggregate(Post, :count, :id) == post_count_before
assert Projects.get_active_project().id == active.id
# The user is told the targeted project does not exist (error toast entry).
entries = :sys.get_state(view.pid).socket.assigns.output_entries
assert Enum.any?(entries, fn entry ->
entry.level == "error" and entry.message =~ "does not exist here"
end)
end
test "settings sidebar selections expose a scroll target for the preferences editor" do
{:ok, view, _html} = live_isolated(build_conn(), BDS.Desktop.ShellLive)