munin/internal/markdown/markdown_test.go
2026-10-19 10:13:56 +02:00

90 lines
3 KiB
Go

package markdown
import (
"strings"
"testing"
)
// TestGFMExtensions: Tabellen, Durchstreichen, Checklisten und automatische
// Links funktionieren — in Einträgen genauso wie in der Hilfe (AUSBAUSTUFEN.md 5.3).
func TestGFMExtensions(t *testing.T) {
cases := []struct {
name, source, want string
}{
{"Tabelle", "| a | b |\n|---|---|\n| 1 | 2 |", "<table>"},
{"Durchstreichen", "~~veraltet~~", "<del>veraltet</del>"},
{"Checkliste offen", "- [ ] offen", `<input disabled="" type="checkbox">`},
{"Checkliste erledigt", "- [x] erledigt", `<input checked="" disabled="" type="checkbox">`},
{"automatischer Link", "siehe https://example.org", `<a href="https://example.org">`},
}
// Beide Renderer prüfen: Sie sollen sich nur in den Ankern unterscheiden.
renderer := map[string]func(string) (string, error){
"Render": Render,
"RenderMitAnkern": RenderWithAnchors,
}
for rName, render := range renderer {
for _, f := range cases {
html, err := render(f.source)
if err != nil {
t.Fatalf("%s/%s: %v", rName, f.name, err)
}
if !strings.Contains(html, f.want) {
t.Errorf("%s/%s: %q fehlt in %q", rName, f.name, f.want, html)
}
}
}
}
// TestNoRawHTML ist der wichtigste Test dieses Pakets: Die Weboberfläche setzt
// das Ergebnis ohne weiteres Escaping in die Seite. Das geht nur, solange hier
// weder Skripte noch gefährliche Link-Ziele durchkommen — auch nicht mit den
// GFM-Erweiterungen.
func TestNoRawHTML(t *testing.T) {
evil := []string{
"<script>alert(1)</script>",
`<img src=x onerror="alert(1)">`,
"[klick](javascript:alert(1))",
"| a |\n|---|\n| <script>alert(1)</script> |",
"- [ ] <script>alert(1)</script>",
}
for _, source := range evil {
for _, render := range []func(string) (string, error){Render, RenderWithAnchors} {
html, _ := render(source)
if strings.Contains(html, "<script") || strings.Contains(html, "onerror") || strings.Contains(html, "javascript:") {
t.Errorf("gefährlicher Inhalt durchgelassen: %q → %q", source, html)
}
}
}
}
// TestAnchor: Nur die Hilfe bekommt ids an den Überschriften, mit gefalteten
// Umlauten und eindeutig je Seite.
func TestAnchor(t *testing.T) {
source := "## Zugänge verwalten\n\n## Protokoll\n\n## Protokoll\n\n## ???"
html, err := RenderWithAnchors(source)
if err != nil {
t.Fatal(err)
}
for _, id := range []string{`id="zugaenge-verwalten"`, `id="protokoll"`, `id="protokoll-1"`, `id="abschnitt"`} {
if !strings.Contains(html, id) {
t.Errorf("%s fehlt in %q", id, html)
}
}
// Ein zweiter Aufruf fängt wieder von vorn an — die vergebenen ids der
// ersten Seite dürfen nicht hinüberwirken.
again, _ := RenderWithAnchors("## Protokoll")
if !strings.Contains(again, `id="protokoll"`) {
t.Errorf("zweite Seite bekommt nicht die einfache id: %q", again)
}
// Einträge: keine ids, damit Eintragstext nicht mit ids der Seite
// kollidieren kann.
entry, _ := Render(source)
if strings.Contains(entry, "id=") {
t.Errorf("Eintrag hat Überschrift-ids bekommen: %q", entry)
}
}