munin/internal/help/help_test.go
2026-10-19 10:13:56 +02:00

188 lines
5.4 KiB
Go

package help
import (
"io/fs"
"regexp"
"strings"
"testing"
)
// TestEveryPageHasAFile stellt sicher, dass die Liste in help.go und die
// Dateien in pages/ zusammenpassen — in beide Richtungen. Eine Seite ohne
// Datei gäbe einen Fehler beim Aufruf, eine Datei ohne Listeneintrag wäre
// unerreichbar und würde unbemerkt veralten.
func TestEveryPageHasAFile(t *testing.T) {
listed := map[string]bool{}
for _, s := range All() {
listed[s.Slug] = true
if _, err := s.Markdown(); err != nil {
t.Errorf("Seite %q: %v", s.Slug, err)
}
}
files, err := fs.Glob(pagesFS, "pages/*.md")
if err != nil {
t.Fatal(err)
}
for _, d := range files {
slug := strings.TrimSuffix(strings.TrimPrefix(d, "pages/"), ".md")
if !listed[slug] {
t.Errorf("Datei %s steht nicht in der Seitenliste", d)
}
}
}
// TestInternalLinksLeadToPages prüft jeden Link der Form (/hilfe/…) in
// allen Seiten. Wird eine Seite umbenannt, fällt ein vergessener Verweis hier
// auf statt erst als 404 beim Lesen.
func TestInternalLinksLeadToPages(t *testing.T) {
link := regexp.MustCompile(`\(/hilfe/([a-z0-9-]+)\)`)
for _, s := range All() {
md, err := s.Markdown()
if err != nil {
t.Fatal(err)
}
for _, hits := range link.FindAllStringSubmatch(md, -1) {
if _, ok := Find(hits[1]); !ok {
t.Errorf("Seite %q verlinkt auf unbekannte Hilfeseite %q", s.Slug, hits[1])
}
}
}
}
func TestFindUnknownPage(t *testing.T) {
if _, ok := Find("gibt-es-nicht"); ok {
t.Error("Finde meldet eine Seite, die es nicht gibt")
}
}
func TestRenderTablesAndNoRawHTML(t *testing.T) {
html, err := Render("| a | b |\n|---|---|\n| 1 | 2 |\n\n<script>alert(1)</script>")
if err != nil {
t.Fatal(err)
}
if !strings.Contains(html, "<table>") {
t.Error("Tabellen werden nicht als Tabelle dargestellt")
}
if strings.Contains(html, "<script>") {
t.Error("Roh-HTML wird durchgelassen")
}
}
// TestAllReturnsCopy stellt sicher, dass ein Aufrufer die Navigation nicht
// für alle anderen verändern kann.
func TestAllReturnsCopy(t *testing.T) {
a := All()
a[0].Title = "verändert"
if All()[0].Title == "verändert" {
t.Error("Alle gibt die interne Liste statt einer Kopie heraus")
}
}
// Die drei Lesergruppen, für die Tests.
var (
projectReader = Reader{}
workspaceAdmin = Reader{WorkspaceAdmin: true}
platformAdmin = Reader{PlatformAdmin: true}
)
func TestVisiblePerAudience(t *testing.T) {
contains := func(pages []Page, slug string) bool {
for _, s := range pages {
if s.Slug == slug {
return true
}
}
return false
}
for _, f := range []struct {
name string
reader Reader
workspacePage, platformPage bool
}{
{"Projektzugang", projectReader, false, false},
{"Workspace-Admin", workspaceAdmin, true, false},
{"Plattform-Admin", platformAdmin, true, true},
} {
visible := Visible(f.reader)
if contains(visible, "workspace-admin") != f.workspacePage {
t.Errorf("%s: Seite für Workspace-Admins sichtbar = %v", f.name, !f.workspacePage)
}
if contains(visible, "plattform-admin") != f.platformPage {
t.Errorf("%s: Seite für Plattform-Admins sichtbar = %v", f.name, !f.platformPage)
}
if !contains(visible, "start") {
t.Errorf("%s sieht die Startseite nicht", f.name)
}
}
if len(Visible(platformAdmin)) != len(All()) {
t.Error("der Plattform-Admin sieht nicht alle Seiten")
}
}
// TestPagesLinkOnlyVisible: Ein Link auf eine Seite, die der Leser
// nicht sehen darf, liefe für ihn ins Leere (404). Geprüft für jede
// Lesergruppe: Was sie sieht, verlinkt nur auf das, was sie auch sieht.
func TestPagesLinkOnlyVisible(t *testing.T) {
link := regexp.MustCompile(`\(/hilfe/([a-z0-9-]+)[#)]`)
for _, reader := range []Reader{projectReader, workspaceAdmin} {
for _, s := range Visible(reader) {
md, err := s.Markdown()
if err != nil {
t.Fatal(err)
}
for _, hits := range link.FindAllStringSubmatch(md, -1) {
if target, ok := Find(hits[1]); ok && !target.CanRead(reader) {
t.Errorf("Seite %q verlinkt auf %q, die dieser Leser nicht sieht (%+v)", s.Slug, target.Slug, reader)
}
}
}
}
}
// TestLinkAnchorsExist: Jeder Link auf einen Abschnitt einer Hilfeseite —
// (/hilfe/seite#abschnitt) oder innerhalb der Seite (#abschnitt) — zeigt auf
// eine Überschrift, die es dort gibt. Sonst landet man oben auf der Seite
// statt beim gemeinten Abschnitt, und niemand merkt es.
//
// Die Hilfe-Links der Templates prüft TestHelpLinksPointToExistingSections
// in internal/web.
func TestLinkAnchorsExist(t *testing.T) {
idPattern := regexp.MustCompile(`<h[1-6] id="([^"]+)"`)
anchor := map[string]map[string]bool{}
for _, s := range All() {
md, err := s.Markdown()
if err != nil {
t.Fatal(err)
}
html, err := Render(md)
if err != nil {
t.Fatal(err)
}
anchor[s.Slug] = map[string]bool{}
for _, m := range idPattern.FindAllStringSubmatch(html, -1) {
anchor[s.Slug][m[1]] = true
}
}
link := regexp.MustCompile(`\]\((?:/hilfe/([a-z0-9-]+))?#([a-z0-9-]+)\)`)
for _, s := range All() {
md, _ := s.Markdown()
for _, m := range link.FindAllStringSubmatch(md, -1) {
target := m[1]
if target == "" {
target = s.Slug
}
if _, exists := anchor[target]; !exists {
t.Errorf("%s: Link auf unbekannte Seite %q", s.Slug, target)
continue
}
if !anchor[target][m[2]] {
t.Errorf("%s: Abschnitt #%s gibt es auf %q nicht", s.Slug, m[2], target)
}
}
}
}