fix: A1-16 keep public project content out of repo via per-user content location and machine-local project registry

This commit is contained in:
2026-05-29 21:58:46 +02:00
parent 9d5764b251
commit cf8b0af15f
17 changed files with 148 additions and 408 deletions

View File

@@ -157,6 +157,48 @@ defmodule BDS.ProjectsTest do
assert Repo.aggregate(Project, :count, :id) == 1
end
test "the default project's public content folder lives outside the repo and private dir" do
Repo.delete_all(Project)
assert {:ok, default_project} = BDS.Projects.ensure_default_project()
data_dir = BDS.Projects.project_data_dir(default_project)
# Public content must live under the per-user default content location,
# never in the application repo (priv/data) nor the private app dir.
refute String.starts_with?(data_dir, Path.expand("../../priv/data", __DIR__))
refute String.starts_with?(data_dir, BDS.Projects.private_dir())
assert String.starts_with?(data_dir, Application.fetch_env!(:bds, :default_content_root))
end
test "project_data_dir never falls back into the application repo" do
# A project without an explicit data_path resolves to the per-user default
# content location, not priv/data/projects/<id> inside the repo.
project = %Project{id: "no-path-#{System.unique_integer([:positive])}", data_path: nil}
data_dir = BDS.Projects.project_data_dir(project)
refute String.starts_with?(data_dir, Path.expand("../../priv/data", __DIR__))
assert String.starts_with?(data_dir, Application.fetch_env!(:bds, :default_content_root))
end
test "project locations are recorded in a machine-local registry under private_dir", %{
temp_root: temp_root
} do
external_dir = Path.join(temp_root, "registry-blog")
File.mkdir_p!(external_dir)
assert {:ok, project} =
BDS.Projects.create_project(%{name: "Registry Blog", data_path: external_dir})
registry = BDS.Projects.project_registry()
assert registry[project.id] == external_dir
assert String.starts_with?(BDS.Projects.registry_path(), BDS.Projects.private_dir())
assert {:ok, _deleted} = BDS.Projects.delete_project(project.id)
refute Map.has_key?(BDS.Projects.project_registry(), project.id)
end
test "delete_project rejects the default and active projects", %{temp_root: temp_root} do
Repo.delete_all(Project)

View File

@@ -2,6 +2,16 @@ cache_root = Path.join(System.tmp_dir!(), "bds-test-cache-#{System.unique_intege
File.mkdir_p!(cache_root)
Application.put_env(:bds, :project_cache_root, cache_root)
# Public, user-owned default project content lives under a per-user default
# content location — never the repo or the private app dir. Tests redirect it
# to a throwaway temp dir so first-launch defaults never touch the developer's
# home directory.
content_root =
Path.join(System.tmp_dir!(), "bds-test-content-#{System.unique_integer([:positive])}")
File.mkdir_p!(content_root)
Application.put_env(:bds, :default_content_root, content_root)
Enum.each(["LC_ALL", "LC_MESSAGES", "LANG"], fn variable ->
System.put_env(variable, "en_US.UTF-8")
end)
@@ -10,6 +20,7 @@ ExUnit.start()
ExUnit.after_suite(fn _results ->
File.rm_rf(cache_root)
File.rm_rf(content_root)
end)
Ecto.Adapters.SQL.Sandbox.mode(BDS.Repo, :manual)