Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,14 @@ the package ships without a bump fails the release gate.

## [Unreleased]

## [0.1.1] - 2026-09-03

### Changed

- Raised the package's coverage gate from 90% to 96% after adding renderer
coverage for trusted inline HTML and image URLs, plus failure coverage for
missing pages and malformed search indexes.

## [0.1.0] - 2026-09-02

### Added
Expand Down
11 changes: 6 additions & 5 deletions mix.exs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
defmodule Managoat.Docs.MixProject do
use Mix.Project

@version "0.1.0"
@version "0.1.1"
@source_url "https://github.com/managoat/managoat_docs"

def project do
Expand All @@ -19,10 +19,11 @@ defmodule Managoat.Docs.MixProject do
docs: docs(),
dialyzer: dialyzer(),
test_coverage: [
# What this suite measures on its own: the compiler, the renderer and
# the checks, driven by the fixture manual. Raise it as the library's
# own tests grow; never lower it.
summary: [threshold: 90],
# The compiler, renderer and guardrail checks currently measure
# 96.93%, driven by the fixture manual and hostile public inputs. The
# remaining renderer misses are defensive dependency-failure paths;
# keep a little headroom for a real branch and never lower this gate.
summary: [threshold: 96],
# The two `use` macros run at test-compile time, before cover
# instruments anything, so they always report 0%. They are exercised
# by the fixture module and by docs_test.exs (and by every host's
Expand Down
48 changes: 48 additions & 0 deletions test/managoat/docs/checks_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,39 @@ defmodule Managoat.Docs.ChecksTest do
end
end

describe "pages_resolve/1" do
test "reports a missing home page and a slug with an invalid response" do
missing_home = Module.concat(__MODULE__, "MissingHome#{System.unique_integer([:positive])}")

Module.create(
missing_home,
quote do
def get(""), do: :error
def slugs, do: []
end,
Macro.Env.location(__ENV__)
)

assert Checks.pages_resolve(missing_home) == [
"there is no home page: the nav names no index.md"
]

invalid_page = Module.concat(__MODULE__, "InvalidPage#{System.unique_integer([:positive])}")

Module.create(
invalid_page,
quote do
def get(""), do: {:ok, %{title: "Home", body: "body"}}
def get("broken"), do: :error
def slugs, do: ["broken"]
end,
Macro.Env.location(__ENV__)
)

assert Checks.pages_resolve(invalid_page) == [~s("broken" does not resolve: :error)]
end
end

describe "anchors_resolve/1" do
test "names a link to an anchor the target page does not render" do
docs = manual(Map.put(@sound, "index.md", "# Home\n\n[x](setup.md#no-such-heading)\n"))
Expand Down Expand Up @@ -245,5 +278,20 @@ defmodule Managoat.Docs.ChecksTest do
assert closes =~ "`</`"
assert count =~ "decodes to 2 entries"
end

test "search_index_json_safe/1 reports malformed JSON" do
stub = Module.concat(__MODULE__, "InvalidJson#{System.unique_integer([:positive])}")

Module.create(
stub,
quote do
def search_index, do: []
def search_index_json, do: "not json"
end,
Macro.Env.location(__ENV__)
)

assert Checks.search_index_json_safe(stub) == ["search_index_json/0 is not valid JSON"]
end
end
end
19 changes: 19 additions & 0 deletions test/managoat/docs/markdown_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,25 @@ end|
assert html =~ "&lt;img"
end

test "inline HTML is neutralized on the trusted path too" do
html = Markdown.to_trusted_html("before <em onclick=steal()>inside</em> after")

refute html =~ "<em"
assert html =~ "&lt;em"
assert html =~ "inside"
end

test "trusted markdown keeps safe images and unwraps unsafe ones" do
html =
Markdown.to_trusted_html(
"![safe](https://example.test/diagram.png) ![unsafe](data:image/svg+xml;base64,AAAA)"
)

assert html =~ ~s(src="https://example.test/diagram.png")
refute html =~ "data:image"
assert html =~ "unsafe"
end

test "headings carry GFM-style ids so #anchor links resolve" do
html = Markdown.to_trusted_html("## Back up `MASTER_SECRETS_KEY`\n\n## Crons: alerting")
assert html =~ ~s(<h2 id="back-up-master_secrets_key">)
Expand Down