defmodule Blogex.SEOTest do use ExUnit.Case, async: true import Blogex.Test.PostBuilder alias Blogex.SEO defmodule StubBlog do def base_path, do: "/blog/eng" def title, do: "Engineering Blog" def description, do: "Our engineering insights" end @base_url "https://example.com" describe "meta_tags/3" do test "includes post title and description" do post = build(title: "My Title", description: "My Description") meta = SEO.meta_tags(post, @base_url, StubBlog) assert meta.title == "My Title" assert meta.description == "My Description" end test "builds canonical URL from base_url and post id" do post = build(id: "hello-world") meta = SEO.meta_tags(post, @base_url, StubBlog) assert meta.og_url == "https://example.com/blog/eng/hello-world" end test "includes OpenGraph article metadata" do post = build(author: "Alice", date: ~D[2026-06-15], tags: ["a", "b"]) meta = SEO.meta_tags(post, @base_url, StubBlog) assert meta.og_type == "article" assert meta.article_author == "Alice" assert meta.article_published_time == "2026-06-15" assert meta.article_tags == ["a", "b"] end test "includes og_image when post has an image" do post = build(image: "/images/cover.png") meta = SEO.meta_tags(post, @base_url, StubBlog) assert meta.og_image == "https://example.com/images/cover.png" end test "og_image is nil when post has no image" do post = build() meta = SEO.meta_tags(post, @base_url, StubBlog) assert meta.og_image == nil end test "og_image uses absolute URL with base_url prefix" do post = build(image: "/assets/post-hero.jpg") meta = SEO.meta_tags(post, "https://myapp.dev", StubBlog) assert meta.og_image == "https://myapp.dev/assets/post-hero.jpg" end end describe "meta_tags_for_blog/2" do test "returns website type" do meta = SEO.meta_tags_for_blog(StubBlog, @base_url) assert meta.og_type == "website" end test "includes blog title and description" do meta = SEO.meta_tags_for_blog(StubBlog, @base_url) assert meta.og_title == "Engineering Blog" assert meta.og_description == "Our engineering insights" end test "has no og_image for blog listing" do meta = SEO.meta_tags_for_blog(StubBlog, @base_url) assert meta.og_image == nil end test "builds correct blog URL" do meta = SEO.meta_tags_for_blog(StubBlog, @base_url) assert meta.og_url == "https://example.com/blog/eng" end test "uses summary twitter card for blog listing" do meta = SEO.meta_tags_for_blog(StubBlog, @base_url) assert meta.twitter_card == "summary" end end describe "sitemap/2" do defmodule BlogA do def base_path, do: "/blog/a" def all_posts, do: [Blogex.Test.PostBuilder.build(id: "post-a", date: ~D[2026-01-01])] end defmodule BlogB do def base_path, do: "/blog/b" def all_posts, do: [Blogex.Test.PostBuilder.build(id: "post-b", date: ~D[2026-02-01])] end test "includes URLs from all blog modules" do xml = SEO.sitemap([BlogA, BlogB], @base_url) assert xml =~ "https://example.com/blog/a/post-a" assert xml =~ "https://example.com/blog/b/post-b" end test "produces valid sitemap XML" do xml = SEO.sitemap([BlogA], @base_url) assert xml =~ ~s() assert xml =~ "" end test "includes lastmod from post date" do xml = SEO.sitemap([BlogA], @base_url) assert xml =~ "2026-01-01" end end end