yak(link-validator-never-sees-compiled-html-internal-link-validation-is-vacuous-ddd1): Link validator never sees compiled HTML — internal-link validation is vacuous
This commit is contained in:
@@ -93,4 +93,38 @@ defmodule Blogex.BlogIntegrationTest do
|
||||
File.rm!(tmp_file)
|
||||
end
|
||||
end
|
||||
|
||||
describe "validation sees compiled post bodies" do
|
||||
# `_validate_links/2` runs on `post.body` *after* NimblePublisher rendered
|
||||
# the markdown, so validation only works if extraction reads the compiled
|
||||
# HTML. This pins that contract against a real compiled body: if
|
||||
# extraction ever goes back to scanning markdown syntax only, it fails.
|
||||
test "extract_links finds internal links in a compiled post body" do
|
||||
tmp_file = Path.join(System.tmp_dir!(), "test_blog_compiled_body.ex")
|
||||
|
||||
File.write!(tmp_file, """
|
||||
defmodule TestBlogCompiledBody do
|
||||
use Blogex.Blog,
|
||||
blog_id: :test,
|
||||
app: :blogex,
|
||||
from: "priv/blog/test/2026/01-03-internal-link-post.md",
|
||||
title: "Test Blog",
|
||||
base_path: "/blog/test"
|
||||
end
|
||||
""")
|
||||
|
||||
[{TestBlogCompiledBody, _bytecode}] = Code.compile_file(tmp_file, __ENV__.file)
|
||||
|
||||
# `apply/3`: the module is compiled at runtime, so a static call would
|
||||
# warn ("module is not available") on every test compile.
|
||||
[post] = apply(TestBlogCompiledBody, :all_posts, [])
|
||||
|
||||
# The markdown source is `[hello world](/blog/engineering/hello-world)`.
|
||||
assert post.body =~ "/blog/engineering/hello-world"
|
||||
|
||||
assert "/blog/engineering/hello-world" in Blogex.LinkValidator.extract_links(post.body)
|
||||
|
||||
File.rm!(tmp_file)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -29,6 +29,21 @@ defmodule Blogex.LinkValidatorTest do
|
||||
assert LinkValidator.extract_links(body) == ["/blog/engineering/tag/pi.dev"]
|
||||
end
|
||||
|
||||
# HTML anchors are not "the href right after <a (" - they are whatever the
|
||||
# renderer emits. Pinned with the shapes a hand-rolled scan misses.
|
||||
test "extracts internal blog links whatever the anchor's attributes look like" do
|
||||
body =
|
||||
~s(<p><a class="post-link" href="/blog/engineering/first">first</a>) <>
|
||||
~s( <a href=/blog/releases/second>second</a>) <>
|
||||
~s( <a data-x="1" aria-label="third" href='/blog/engineering/third'>third</a></p>)
|
||||
|
||||
assert LinkValidator.extract_links(body) == [
|
||||
"/blog/engineering/first",
|
||||
"/blog/releases/second",
|
||||
"/blog/engineering/third"
|
||||
]
|
||||
end
|
||||
|
||||
test "ignores external links" do
|
||||
body = "See [GitHub](https://github.com) and [internal](/blog/engineering/post)."
|
||||
|
||||
@@ -157,6 +172,12 @@ defmodule Blogex.LinkValidatorTest do
|
||||
assert LinkValidator.validate_link("/blog/engineering/tag/pi.dev#top") == :ok
|
||||
end
|
||||
|
||||
# Tags are user-defined: the documented rule restricts *slugs*, not tags.
|
||||
test "accepts tag links with dots and capitals" do
|
||||
assert LinkValidator.validate_link("/blog/engineering/tag/pi.dev") == :ok
|
||||
assert LinkValidator.validate_link("/blog/engineering/tag/Pi.dev") == :ok
|
||||
end
|
||||
|
||||
test "rejects tag page link with empty tag" do
|
||||
assert LinkValidator.validate_link("/blog/engineering/tag/") ==
|
||||
{:error, "empty tag in tag link"}
|
||||
@@ -267,5 +288,40 @@ defmodule Blogex.LinkValidatorTest do
|
||||
]
|
||||
}
|
||||
end
|
||||
|
||||
# `_validate_links/2` hands this module `post.body`, which NimblePublisher
|
||||
# already rendered to HTML, so failure reporting has to work on HTML bodies.
|
||||
# These are the two reports the compile-time integration tests depend on.
|
||||
test "reports an unknown blog ID in an HTML body" do
|
||||
body = ~s(<p><a href="/blog/unknown/broken">bad</a></p>)
|
||||
|
||||
assert LinkValidator.validate_body(body, :engineering, post_id: "html-post") == {
|
||||
:error,
|
||||
[
|
||||
{
|
||||
1,
|
||||
"/blog/unknown/broken",
|
||||
"unknown blog ID: unknown",
|
||||
post_id: "html-post"
|
||||
}
|
||||
]
|
||||
}
|
||||
end
|
||||
|
||||
test "reports an invalid slug in an HTML body" do
|
||||
body = ~s(<p><a href="/blog/engineering/My-Post">bad</a></p>)
|
||||
|
||||
assert LinkValidator.validate_body(body, :engineering, post_id: "html-post") == {
|
||||
:error,
|
||||
[
|
||||
{
|
||||
1,
|
||||
"/blog/engineering/My-Post",
|
||||
"slug must be lowercase alphanumeric with hyphens: My-Post",
|
||||
post_id: "html-post"
|
||||
}
|
||||
]
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user