defmodule BDS.TemplateLookupPriorityTest do use ExUnit.Case, async: false import Ecto.Query alias BDS.Rendering.TemplateSelection setup do :ok = Ecto.Adapters.SQL.Sandbox.checkout(BDS.Repo) temp_dir = Path.join(System.tmp_dir!(), "bds-tlp-#{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: "TLP Test", data_path: temp_dir}) {:ok, _metadata} = BDS.Metadata.update_project_metadata(project.id, %{ main_language: "en", blog_languages: ["en"] }) %{project: project} end defp create_published_template(project_id, slug, content) do {:ok, template} = BDS.Templates.create_template(%{ project_id: project_id, title: "Template #{slug}", kind: :post, content: content }) if template.slug != slug do BDS.Repo.update!(Ecto.Changeset.change(template, slug: slug)) end {:ok, published} = BDS.Templates.publish_template(template.id) published end defp create_tag(project_id, name, post_template_slug) do {:ok, tag} = BDS.Tags.create_tag(%{ project_id: project_id, name: name, post_template_slug: post_template_slug }) tag end describe "resolve_post_template_slug/3" do test "level 1: returns post's own template_slug when set", %{project: project} do result = TemplateSelection.resolve_post_template_slug( project.id, ["some-tag"], ["article"] ) assert result == nil end test "level 2: falls back to tag's post_template_slug", %{project: project} do _tag = create_tag(project.id, "photo", "photo-layout") result = TemplateSelection.resolve_post_template_slug( project.id, ["photo"], [] ) assert result == "photo-layout" end test "level 2: uses first matching tag with a template slug", %{project: project} do _tag1 = create_tag(project.id, "news", nil) _tag2 = create_tag(project.id, "gallery", "gallery-layout") result = TemplateSelection.resolve_post_template_slug( project.id, ["news", "gallery"], [] ) assert result == "gallery-layout" end test "level 3: falls back to category's post_template_slug", %{project: project} do {:ok, _} = BDS.Metadata.add_category(project.id, "review") {:ok, _} = BDS.Metadata.update_category_settings(project.id, "review", %{ "post_template_slug" => "review-layout" }) result = TemplateSelection.resolve_post_template_slug( project.id, [], ["review"] ) assert result == "review-layout" end test "level 2 takes priority over level 3", %{project: project} do _tag = create_tag(project.id, "featured", "featured-layout") {:ok, _} = BDS.Metadata.add_category(project.id, "review") {:ok, _} = BDS.Metadata.update_category_settings(project.id, "review", %{ "post_template_slug" => "review-layout" }) result = TemplateSelection.resolve_post_template_slug( project.id, ["featured"], ["review"] ) assert result == "featured-layout" end test "level 4: returns nil when no tag or category has a template", %{project: project} do _tag = create_tag(project.id, "plain", nil) result = TemplateSelection.resolve_post_template_slug( project.id, ["plain"], ["article"] ) assert result == nil end end describe "BundledDefaultTemplatesExistOutsideProjectData" do test "single-post bundled template resolves with no Template rows", %{project: project} do assert [] = BDS.Repo.all(from t in BDS.Templates.Template, where: t.project_id == ^project.id) {:ok, source} = TemplateSelection.load_template_source(project.id, :post, nil) assert source =~ ~s(data-template="single-post") end test "post-list bundled template resolves with no Template rows", %{project: project} do assert [] = BDS.Repo.all(from t in BDS.Templates.Template, where: t.project_id == ^project.id) {:ok, source} = TemplateSelection.load_template_source(project.id, :list, nil) assert source =~ ~s({% if archive_context %}) end test "not-found bundled template resolves with no Template rows", %{project: project} do assert [] = BDS.Repo.all(from t in BDS.Templates.Template, where: t.project_id == ^project.id) {:ok, source} = TemplateSelection.load_template_source(project.id, :not_found, nil) assert source =~ ~s(data-template="not-found") assert source =~ "404" end test "bundled defaults resolve even when project has no templates directory", %{ project: project } do template_dir = Path.join(BDS.Projects.project_data_dir(project), "templates") refute File.exists?(template_dir) {:ok, source} = TemplateSelection.load_template_source(project.id, :post, nil) assert source =~ ~s(data-template="single-post") end end describe "UserTemplateDirectoryOverridesBundledDefaults" do test "published project template with default slug overrides bundled single-post", %{ project: project } do _template = create_published_template( project.id, "single-post", "
{{ page.title }}
" ) {:ok, source} = TemplateSelection.load_template_source(project.id, :post, nil) assert source =~ ~s(data-template="user-single-post") refute source =~ ~s(data-template="single-post") end test "published project template overrides bundled default when slug requested explicitly", %{ project: project } do _template = create_published_template( project.id, "single-post", "
{{ page.title }}
" ) {:ok, source} = TemplateSelection.load_template_source(project.id, :post, "single-post") assert source =~ ~s(data-template="user-single-post") refute source =~ ~s(data-template="single-post") end end describe "end-to-end template lookup with rendering" do test "post renders with tag-specific template when no post template set", %{ project: project } do template = create_published_template(project.id, "photo-layout", "

PHOTO: {{ page.title }}

") _tag = create_tag(project.id, "photo", template.slug) {:ok, post} = BDS.Posts.create_post(%{ project_id: project.id, title: "My Photo Post", content: "photo content", language: "en", tags: ["photo"] }) resolved_slug = TemplateSelection.resolve_post_template_slug( project.id, post.tags, post.categories ) assert resolved_slug == "photo-layout" {:ok, rendered} = TemplateSelection.load_template_source(project.id, :post, resolved_slug) assert rendered =~ "PHOTO:" end test "post renders with category-specific template when no post or tag template", %{ project: project } do template = create_published_template( project.id, "review-layout", "

REVIEW: {{ page.title }}

" ) {:ok, _} = BDS.Metadata.add_category(project.id, "review") {:ok, _} = BDS.Metadata.update_category_settings(project.id, "review", %{ "post_template_slug" => template.slug }) {:ok, post} = BDS.Posts.create_post(%{ project_id: project.id, title: "My Review", content: "review content", language: "en", categories: ["review"] }) resolved_slug = TemplateSelection.resolve_post_template_slug( project.id, post.tags, post.categories ) assert resolved_slug == "review-layout" {:ok, rendered} = TemplateSelection.load_template_source(project.id, :post, resolved_slug) assert rendered =~ "REVIEW:" end end end