217 lines
7.0 KiB
Elixir
217 lines
7.0 KiB
Elixir
defmodule BDS.ImageImportPipelineTest do
|
|
use ExUnit.Case, async: false
|
|
|
|
import Phoenix.ConnTest
|
|
import Phoenix.LiveViewTest
|
|
|
|
alias BDS.Desktop.{FilePicker, Overlay}
|
|
alias BDS.{Metadata, AI, Repo}
|
|
|
|
@endpoint BDS.Desktop.Endpoint
|
|
|
|
setup do
|
|
:ok = Ecto.Adapters.SQL.Sandbox.checkout(Repo)
|
|
Ecto.Adapters.SQL.Sandbox.mode(Repo, {:shared, self()})
|
|
|
|
temp_dir =
|
|
Path.join(System.tmp_dir!(), "bds-image-import-#{System.unique_integer([:positive])}")
|
|
|
|
File.mkdir_p!(temp_dir)
|
|
on_exit(fn -> File.rm_rf(temp_dir) end)
|
|
|
|
{:ok, project} = BDS.Projects.create_project(%{name: "Image Import Test", data_path: temp_dir})
|
|
%{project: project, temp_dir: temp_dir}
|
|
end
|
|
|
|
# ── FilePicker multi-select parsing ────────────────────────────────────────
|
|
|
|
describe "FilePicker.choose_files/2" do
|
|
test "single selection returns a single-item list" do
|
|
# Simulate what osascript returns for regular choose file
|
|
result = FilePicker.parse_choose_files_result("/Users/test/image.png", false)
|
|
assert result == {:ok, "/Users/test/image.png"}
|
|
end
|
|
|
|
test "multi selection parses newline-separated paths" do
|
|
result =
|
|
FilePicker.parse_choose_files_result(
|
|
"/Users/test/photo1.jpg\n/Users/test/photo2.png\n/Users/test/photo3.heic",
|
|
true
|
|
)
|
|
|
|
assert result ==
|
|
{:ok, ["/Users/test/photo1.jpg", "/Users/test/photo2.png", "/Users/test/photo3.heic"]}
|
|
end
|
|
|
|
test "multi selection filters out empty lines" do
|
|
result =
|
|
FilePicker.parse_choose_files_result(
|
|
"/Users/test/photo1.jpg\n\n/Users/test/photo2.png\n \n/Users/test/photo3.heic\n",
|
|
true
|
|
)
|
|
|
|
assert result ==
|
|
{:ok, ["/Users/test/photo1.jpg", "/Users/test/photo2.png", "/Users/test/photo3.heic"]}
|
|
end
|
|
|
|
test "multi selection with single file returns list with one element" do
|
|
result = FilePicker.parse_choose_files_result("/Users/test/photo1.jpg", true)
|
|
assert result == {:ok, ["/Users/test/photo1.jpg"]}
|
|
end
|
|
end
|
|
|
|
# ── Metadata image_import_concurrency ───────────────────────────────────────
|
|
|
|
describe "Metadata image_import_concurrency" do
|
|
test "defaults to 4 for new projects", %{project: project} do
|
|
{:ok, metadata} = Metadata.get_project_metadata(project.id)
|
|
assert metadata.image_import_concurrency == 4
|
|
end
|
|
|
|
test "clamps to minimum 1", %{project: project} do
|
|
{:ok, _metadata} =
|
|
Metadata.update_project_metadata(project.id, %{image_import_concurrency: 0})
|
|
|
|
{:ok, metadata} = Metadata.get_project_metadata(project.id)
|
|
assert metadata.image_import_concurrency == 1
|
|
end
|
|
|
|
test "clamps to maximum 8", %{project: project} do
|
|
{:ok, _metadata} =
|
|
Metadata.update_project_metadata(project.id, %{image_import_concurrency: 100})
|
|
|
|
{:ok, metadata} = Metadata.get_project_metadata(project.id)
|
|
assert metadata.image_import_concurrency == 8
|
|
end
|
|
|
|
test "persists and reads correctly", %{project: project} do
|
|
{:ok, _metadata} =
|
|
Metadata.update_project_metadata(project.id, %{image_import_concurrency: 3})
|
|
|
|
{:ok, metadata} = Metadata.get_project_metadata(project.id)
|
|
assert metadata.image_import_concurrency == 3
|
|
end
|
|
|
|
test "handles string input", %{project: project} do
|
|
{:ok, _metadata} =
|
|
Metadata.update_project_metadata(project.id, %{image_import_concurrency: "5"})
|
|
|
|
{:ok, metadata} = Metadata.get_project_metadata(project.id)
|
|
assert metadata.image_import_concurrency == 5
|
|
end
|
|
|
|
test "handles nil as default", %{project: project} do
|
|
{:ok, _metadata} =
|
|
Metadata.update_project_metadata(project.id, %{image_import_concurrency: nil})
|
|
|
|
{:ok, metadata} = Metadata.get_project_metadata(project.id)
|
|
assert metadata.image_import_concurrency == 4
|
|
end
|
|
|
|
test "reflects in form as string", %{project: project} do
|
|
{:ok, metadata} = Metadata.get_project_metadata(project.id)
|
|
|
|
form =
|
|
BDS.Desktop.ShellLive.SettingsEditor.ProjectSettings.project_form(metadata)
|
|
|
|
assert form["image_import_concurrency"] == "4"
|
|
end
|
|
end
|
|
|
|
# ── Overlay struct post_id ────────────────────────────────────────────────
|
|
|
|
describe "Overlay insert_media" do
|
|
test "open(:post, :insert_media, context) includes post_id" do
|
|
context = %{
|
|
current_tab: %{
|
|
type: :post,
|
|
id: "post-uuid-123",
|
|
title: "Test Post",
|
|
subtitle: "draft"
|
|
},
|
|
media: [],
|
|
insert_media_title: "Insert Media"
|
|
}
|
|
|
|
overlay = Overlay.open(:post, :insert_media, context)
|
|
assert overlay.post_id == "post-uuid-123"
|
|
end
|
|
|
|
test "post_id is nil when opened from non-post context" do
|
|
context = %{
|
|
current_tab: %{
|
|
type: :media,
|
|
id: "media-uuid-456",
|
|
title: "Test Media",
|
|
subtitle: "image/png"
|
|
},
|
|
media: [],
|
|
insert_media_title: "Insert Media"
|
|
}
|
|
|
|
overlay = Overlay.open(:media, :insert_media, context)
|
|
assert overlay == nil
|
|
end
|
|
|
|
test "set_search_query preserves post_id" do
|
|
overlay = %{
|
|
kind: :insert_media,
|
|
title: "Insert Media",
|
|
search_query: "",
|
|
results: [],
|
|
all_media: [],
|
|
post_id: "post-uuid-789"
|
|
}
|
|
|
|
result = Overlay.set_search_query(overlay, "search term")
|
|
assert result.post_id == "post-uuid-789"
|
|
end
|
|
end
|
|
|
|
# ── Airplane mode gating via shell_live event ─────────────────────────────
|
|
|
|
describe "overlay_add_images airplane mode gating" do
|
|
setup do
|
|
prev_auto = System.get_env("BDS_DESKTOP_AUTOMATION")
|
|
System.put_env("BDS_DESKTOP_AUTOMATION", "1")
|
|
|
|
on_exit(fn ->
|
|
if prev_auto,
|
|
do: System.put_env("BDS_DESKTOP_AUTOMATION", prev_auto),
|
|
else: System.delete_env("BDS_DESKTOP_AUTOMATION")
|
|
end)
|
|
end
|
|
|
|
test "shows toast in airplane mode when Add Gallery Images is clicked", %{project: project} do
|
|
{:ok, _project} = BDS.Projects.set_active_project(project.id)
|
|
|
|
assert :ok = AI.set_airplane_mode(true)
|
|
|
|
{:ok, post} =
|
|
BDS.Posts.create_post(%{
|
|
project_id: project.id,
|
|
title: "Gallery Post",
|
|
content: "Content"
|
|
})
|
|
|
|
{:ok, view, _html} = live_isolated(build_conn(), BDS.Desktop.ShellLive)
|
|
|
|
render_click(view, "pin_sidebar_item", %{
|
|
"route" => "post",
|
|
"id" => post.id,
|
|
"title" => post.title,
|
|
"subtitle" => "draft"
|
|
})
|
|
|
|
html =
|
|
view
|
|
|> element("[phx-click='add_gallery_images']")
|
|
|> render_click()
|
|
|
|
assert html =~ "Automatic AI actions stay gated by airplane mode"
|
|
|
|
assert :ok = AI.set_airplane_mode(false)
|
|
end
|
|
end
|
|
end
|