Regex.scan(body)
+ |> Enum.map(fn [_, path] -> path end)
+
+ (markdown_links ++ html_links)
+ |> Enum.uniq()
|> Enum.filter(&internal_blog_link?/1)
end
@@ -90,6 +109,9 @@ defmodule Blogex.LinkValidator do
iex> validate_link("/blog/engineering/My-Post")
{:error, "slug must be lowercase alphanumeric with hyphens: My-Post"}
+
+ iex> validate_link("/blog/engineering/tag/pi.dev")
+ :ok
"""
@spec validate_link(String.t()) :: :ok | {:error, String.t()}
def validate_link(link) when is_binary(link) do
@@ -99,7 +121,13 @@ defmodule Blogex.LinkValidator do
{blog_id_str, slug_part} ->
case Map.fetch(@valid_blog_ids, blog_id_str) do
- {:ok, _blog_atom} -> validate_slug(slug_part)
+ {:ok, _blog_atom} ->
+ if String.starts_with?(slug_part, "tag/") do
+ validate_tag(String.replace_prefix(slug_part, "tag/", ""))
+ else
+ validate_slug(slug_part)
+ end
+
:error -> {:error, "unknown blog ID: #{blog_id_str}"}
end
end
@@ -209,4 +237,12 @@ defmodule Blogex.LinkValidator do
{:error, "slug must be lowercase alphanumeric with hyphens: #{slug}"}
end
end
+
+ @doc false
+ @spec validate_tag(String.t()) :: :ok | {:error, String.t()}
+ defp validate_tag(tag) when tag == "" do
+ {:error, "empty tag in tag link"}
+ end
+
+ defp validate_tag(_tag), do: :ok
end
diff --git a/blogex/test/blogex/blog_integration_test.exs b/blogex/test/blogex/blog_integration_test.exs
index 5e6ee61..351bf5c 100644
--- a/blogex/test/blogex/blog_integration_test.exs
+++ b/blogex/test/blogex/blog_integration_test.exs
@@ -59,7 +59,7 @@ defmodule Blogex.BlogIntegrationTest do
""")
[{TestBlogValid, _bytecode}] = Code.compile_file(tmp_file, __ENV__.file)
- assert TestBlogValid.title() == "Test Blog"
+ assert apply(TestBlogValid, :title, []) == "Test Blog"
File.rm!(tmp_file)
end
diff --git a/blogex/test/blogex/link_validator_test.exs b/blogex/test/blogex/link_validator_test.exs
index a68017c..3c42010 100644
--- a/blogex/test/blogex/link_validator_test.exs
+++ b/blogex/test/blogex/link_validator_test.exs
@@ -13,6 +13,22 @@ defmodule Blogex.LinkValidatorTest do
]
end
+ test "extracts internal blog links from HTML body" do
+ body =
+ ~s(Check out hello world and release v1.
)
+
+ assert LinkValidator.extract_links(body) == [
+ "/blog/engineering/hello-world",
+ "/blog/releases/v1-0-0"
+ ]
+ end
+
+ test "extracts tag page links from HTML body" do
+ body = ~s(See pi.dev posts.
)
+
+ assert LinkValidator.extract_links(body) == ["/blog/engineering/tag/pi.dev"]
+ end
+
test "ignores external links" do
body = "See [GitHub](https://github.com) and [internal](/blog/engineering/post)."
@@ -132,6 +148,25 @@ defmodule Blogex.LinkValidatorTest do
assert LinkValidator.validate_link("/blog/engineering/post#section") == :ok
end
+ test "allows tag page links" do
+ assert LinkValidator.validate_link("/blog/engineering/tag/pi.dev") == :ok
+ assert LinkValidator.validate_link("/blog/releases/tag/v1") == :ok
+ end
+
+ test "allows tag page links with fragments" do
+ assert LinkValidator.validate_link("/blog/engineering/tag/pi.dev#top") == :ok
+ end
+
+ test "rejects tag page link with empty tag" do
+ assert LinkValidator.validate_link("/blog/engineering/tag/") ==
+ {:error, "empty tag in tag link"}
+ end
+
+ test "rejects unknown blog ID in tag page link" do
+ assert LinkValidator.validate_link("/blog/unknown/tag/pi.dev") ==
+ {:error, "unknown blog ID: unknown"}
+ end
+
test "rejects non-blog path" do
assert LinkValidator.validate_link("/about") ==
{:error, "not a blog link: /about"}
diff --git a/blogex/test/blogex/registry_test.exs b/blogex/test/blogex/registry_test.exs
index cfc7c72..bacbbdd 100644
--- a/blogex/test/blogex/registry_test.exs
+++ b/blogex/test/blogex/registry_test.exs
@@ -1,7 +1,6 @@
defmodule Blogex.RegistryTest do
use ExUnit.Case
- import Blogex.Test.PostBuilder
alias Blogex.Registry
defmodule AlphaBlog do
diff --git a/blogex/test/blogex/router_test.exs b/blogex/test/blogex/router_test.exs
index 004bf75..90edb51 100644
--- a/blogex/test/blogex/router_test.exs
+++ b/blogex/test/blogex/router_test.exs
@@ -1,6 +1,6 @@
defmodule Blogex.RouterTest do
use ExUnit.Case
- use Plug.Test
+ import Plug.Test
import Blogex.Test.PostBuilder
alias Blogex.Test.FakeBlog