Implement create_and_publish_script/1 and add tests for D2-3/D2-4 spec gaps
This commit is contained in:
@@ -140,8 +140,8 @@ All reconciled to follow code. Specs must be self-consistent and match code.
|
||||
|---|---|---|---|
|
||||
| ~~D2-1~~ | ~~RemoveCategory rule~~ | ~~metadata.allium:100~~ | **Resolved:** 2 tests added in metadata_test.exs — remove_category removes category+settings from state/files/DB (6 assertions); remove_category is a no-op for non-existent category |
|
||||
| ~~D2-2~~ | ~~CreateAndPublishTemplate rule~~ | template.allium:105 | **Resolved:** added `create_and_publish_template/1` (validates Liquid, creates published template + writs file), 3 tests added (happy path file assertions, invalid liquid rejection, slug dedup on title conflict) |
|
||||
| D2-3 | CreateAndPublishScript rule | script.allium:160 | Write test: create+publish in one step |
|
||||
| D2-4 | UniqueScriptSlug dedup | script.allium:115 | Write test: two scripts same title → dedup slug |
|
||||
| ~~D2-3~~ | ~~CreateAndPublishScript rule~~ | ~~script.allium:160~~ | **Resolved:** `create_and_publish_script/1` implemented in scripts.ex, 4 tests added (happy path with file assertions + macro entrypoint default + invalid Lua rejection + slug dedup on title conflict) |
|
||||
| ~~D2-4~~ | ~~UniqueScriptSlug dedup~~ | ~~script.allium:115~~ | **Resolved:** test added asserting two scripts with same title produce unique slugs (`dup-slug` → `dup-slug-2`) |
|
||||
| D2-5 | FrontmatterRoundtrip invariant | post.allium:223 | Write test: write file, read back, assert all DB fields match |
|
||||
| D2-6 | SidecarRoundtrip invariant | media.allium:198 | Write test: write sidecar, read back, assert all fields match |
|
||||
| D2-7 | ConditionalPostFields: nil fields absent from frontmatter | frontmatter.allium:398 | Write test: post with nil excerpt/author/language → fields not in file |
|
||||
|
||||
@@ -42,6 +42,56 @@ defmodule BDS.Scripts do
|
||||
|> Repo.insert()
|
||||
end
|
||||
|
||||
@spec create_and_publish_script(attrs()) :: script_result()
|
||||
def create_and_publish_script(attrs) do
|
||||
project_id = attr(attrs, :project_id)
|
||||
title = attr(attrs, :title) || ""
|
||||
kind = attr(attrs, :kind)
|
||||
content = attr(attrs, :content) || ""
|
||||
|
||||
case validate_script(content) do
|
||||
:ok ->
|
||||
slug = unique_slug(project_id, Slug.slugify(title), "script")
|
||||
entrypoint = attr(attrs, :entrypoint) || default_entrypoint(kind)
|
||||
file_path = script_file_path(slug)
|
||||
now = Persistence.now_ms()
|
||||
|
||||
changeset =
|
||||
%Script{}
|
||||
|> Script.changeset(%{
|
||||
id: Ecto.UUID.generate(),
|
||||
project_id: project_id,
|
||||
slug: slug,
|
||||
title: title,
|
||||
kind: kind,
|
||||
entrypoint: entrypoint,
|
||||
enabled: true,
|
||||
version: 1,
|
||||
file_path: file_path,
|
||||
status: :published,
|
||||
content: nil,
|
||||
created_at: now,
|
||||
updated_at: now
|
||||
})
|
||||
|
||||
with {:ok, script} <- Repo.insert(changeset) do
|
||||
full_path = full_file_path(script.project_id, file_path)
|
||||
File.mkdir_p!(Path.dirname(full_path))
|
||||
|
||||
:ok =
|
||||
Persistence.atomic_write(
|
||||
full_path,
|
||||
serialize_script_file(script, content)
|
||||
)
|
||||
|
||||
{:ok, script}
|
||||
end
|
||||
|
||||
{:error, reason} ->
|
||||
{:error, {:invalid_script, reason}}
|
||||
end
|
||||
end
|
||||
|
||||
@spec get_script(String.t()) :: Script.t() | nil
|
||||
def get_script(script_id) do
|
||||
case Repo.get(Script, script_id) do
|
||||
|
||||
@@ -155,6 +155,114 @@ defmodule BDS.ScriptsTest do
|
||||
assert published.status == :published
|
||||
end
|
||||
|
||||
test "create_and_publish_script creates a published script with file in one step", %{
|
||||
project: project,
|
||||
temp_dir: temp_dir
|
||||
} do
|
||||
assert {:ok, script} =
|
||||
BDS.Scripts.create_and_publish_script(%{
|
||||
project_id: project.id,
|
||||
title: "Published Utility",
|
||||
kind: :utility,
|
||||
content: "function main() return 'ok' end"
|
||||
})
|
||||
|
||||
assert script.status == :published
|
||||
assert script.content == nil
|
||||
assert script.slug == "published-utility"
|
||||
assert script.enabled == true
|
||||
assert script.version == 1
|
||||
assert script.entrypoint == "main"
|
||||
assert script.file_path == "scripts/published-utility.lua"
|
||||
|
||||
full_path = Path.join(temp_dir, script.file_path)
|
||||
assert File.exists?(full_path)
|
||||
|
||||
contents = File.read!(full_path)
|
||||
assert contents =~ "---\nid: #{script.id}\n"
|
||||
assert contents =~ "projectId: #{project.id}\n"
|
||||
assert contents =~ "slug: published-utility\n"
|
||||
assert contents =~ "title: Published Utility\n"
|
||||
assert contents =~ "kind: utility\n"
|
||||
assert contents =~ "entrypoint: main\n"
|
||||
assert contents =~ "enabled: true\n"
|
||||
assert contents =~ "version: 1\n"
|
||||
assert contents =~ ~r/createdAt: '\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z'\n/
|
||||
assert contents =~ ~r/updatedAt: '\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z'\n/
|
||||
assert contents =~ "\n---\nfunction main() return 'ok' end\n"
|
||||
end
|
||||
|
||||
test "create_and_publish_script uses macro default entrypoint", %{
|
||||
project: project
|
||||
} do
|
||||
assert {:ok, script} =
|
||||
BDS.Scripts.create_and_publish_script(%{
|
||||
project_id: project.id,
|
||||
title: "Render Helper",
|
||||
kind: :macro,
|
||||
content: "function render() end"
|
||||
})
|
||||
|
||||
assert script.entrypoint == "render"
|
||||
assert script.slug == "render-helper"
|
||||
end
|
||||
|
||||
test "create_and_publish_script rejects invalid Lua syntax", %{project: project} do
|
||||
assert {:error, {:invalid_script, _reason}} =
|
||||
BDS.Scripts.create_and_publish_script(%{
|
||||
project_id: project.id,
|
||||
title: "Bad Script",
|
||||
kind: :utility,
|
||||
content: "function main( missing end"
|
||||
})
|
||||
end
|
||||
|
||||
test "create_and_publish_script deduplicates slug on title conflict", %{project: project} do
|
||||
assert {:ok, _first} =
|
||||
BDS.Scripts.create_and_publish_script(%{
|
||||
project_id: project.id,
|
||||
title: "Same Title",
|
||||
kind: :utility,
|
||||
content: "function main() return 'v1' end"
|
||||
})
|
||||
|
||||
assert {:ok, second} =
|
||||
BDS.Scripts.create_and_publish_script(%{
|
||||
project_id: project.id,
|
||||
title: "Same Title",
|
||||
kind: :utility,
|
||||
content: "function main() return 'v2' end"
|
||||
})
|
||||
|
||||
assert second.slug == "same-title-2"
|
||||
assert second.status == :published
|
||||
end
|
||||
|
||||
test "create_script deduplicates slug on title conflict (UniqueScriptSlug)", %{
|
||||
project: project
|
||||
} do
|
||||
assert {:ok, first} =
|
||||
BDS.Scripts.create_script(%{
|
||||
project_id: project.id,
|
||||
title: "Dup Slug",
|
||||
kind: :utility,
|
||||
content: "function main() return 'first' end"
|
||||
})
|
||||
|
||||
assert first.slug == "dup-slug"
|
||||
|
||||
assert {:ok, second} =
|
||||
BDS.Scripts.create_script(%{
|
||||
project_id: project.id,
|
||||
title: "Dup Slug",
|
||||
kind: :utility,
|
||||
content: "function main() return 'second' end"
|
||||
})
|
||||
|
||||
assert second.slug == "dup-slug-2"
|
||||
refute second.id == first.id
|
||||
end
|
||||
|
||||
test "rebuild_scripts_from_files recreates published scripts from disk", %{
|
||||
project: project,
|
||||
temp_dir: temp_dir
|
||||
|
||||
Reference in New Issue
Block a user