munin/internal/hilfe/hilfe_test.go

188 lines
5.4 KiB
Go

package hilfe
import (
"io/fs"
"regexp"
"strings"
"testing"
)
// TestJedeSeiteHatEineDatei stellt sicher, dass die Liste in hilfe.go und die
// Dateien in seiten/ 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 TestJedeSeiteHatEineDatei(t *testing.T) {
gelistet := map[string]bool{}
for _, s := range Alle() {
gelistet[s.Slug] = true
if _, err := s.Markdown(); err != nil {
t.Errorf("Seite %q: %v", s.Slug, err)
}
}
dateien, err := fs.Glob(seitenFS, "seiten/*.md")
if err != nil {
t.Fatal(err)
}
for _, d := range dateien {
slug := strings.TrimSuffix(strings.TrimPrefix(d, "seiten/"), ".md")
if !gelistet[slug] {
t.Errorf("Datei %s steht nicht in der Seitenliste", d)
}
}
}
// TestInterneLinksFuehrenZuSeiten 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 TestInterneLinksFuehrenZuSeiten(t *testing.T) {
link := regexp.MustCompile(`\(/hilfe/([a-z0-9-]+)\)`)
for _, s := range Alle() {
md, err := s.Markdown()
if err != nil {
t.Fatal(err)
}
for _, treffer := range link.FindAllStringSubmatch(md, -1) {
if _, ok := Finde(treffer[1]); !ok {
t.Errorf("Seite %q verlinkt auf unbekannte Hilfeseite %q", s.Slug, treffer[1])
}
}
}
}
func TestFindeUnbekannteSeite(t *testing.T) {
if _, ok := Finde("gibt-es-nicht"); ok {
t.Error("Finde meldet eine Seite, die es nicht gibt")
}
}
func TestRenderTabellenUndKeinRohHTML(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")
}
}
// TestAlleGibtKopie stellt sicher, dass ein Aufrufer die Navigation nicht
// für alle anderen verändern kann.
func TestAlleGibtKopie(t *testing.T) {
a := Alle()
a[0].Titel = "verändert"
if Alle()[0].Titel == "verändert" {
t.Error("Alle gibt die interne Liste statt einer Kopie heraus")
}
}
// Die drei Lesergruppen, für die Tests.
var (
projektLeser = Leser{}
workspaceAdmin = Leser{WorkspaceAdmin: true}
plattformAdmin = Leser{PlattformAdmin: true}
)
func TestSichtbarJeLesergruppe(t *testing.T) {
enthaelt := func(seiten []Seite, slug string) bool {
for _, s := range seiten {
if s.Slug == slug {
return true
}
}
return false
}
for _, f := range []struct {
name string
leser Leser
workspaceSeite, plattSei bool
}{
{"Projektzugang", projektLeser, false, false},
{"Workspace-Admin", workspaceAdmin, true, false},
{"Plattform-Admin", plattformAdmin, true, true},
} {
sichtbar := Sichtbar(f.leser)
if enthaelt(sichtbar, "workspace-admin") != f.workspaceSeite {
t.Errorf("%s: Seite für Workspace-Admins sichtbar = %v", f.name, !f.workspaceSeite)
}
if enthaelt(sichtbar, "plattform-admin") != f.plattSei {
t.Errorf("%s: Seite für Plattform-Admins sichtbar = %v", f.name, !f.plattSei)
}
if !enthaelt(sichtbar, "start") {
t.Errorf("%s sieht die Startseite nicht", f.name)
}
}
if len(Sichtbar(plattformAdmin)) != len(Alle()) {
t.Error("der Plattform-Admin sieht nicht alle Seiten")
}
}
// TestSeitenVerlinkenNurSichtbares: 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 TestSeitenVerlinkenNurSichtbares(t *testing.T) {
link := regexp.MustCompile(`\(/hilfe/([a-z0-9-]+)[#)]`)
for _, leser := range []Leser{projektLeser, workspaceAdmin} {
for _, s := range Sichtbar(leser) {
md, err := s.Markdown()
if err != nil {
t.Fatal(err)
}
for _, treffer := range link.FindAllStringSubmatch(md, -1) {
if ziel, ok := Finde(treffer[1]); ok && !ziel.DarfLesen(leser) {
t.Errorf("Seite %q verlinkt auf %q, die dieser Leser nicht sieht (%+v)", s.Slug, ziel.Slug, leser)
}
}
}
}
}
// TestAnkerInLinksGibtEs: 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 TestHilfelinksZeigenAufVorhandeneAbschnitte
// in internal/web.
func TestAnkerInLinksGibtEs(t *testing.T) {
idMuster := regexp.MustCompile(`<h[1-6] id="([^"]+)"`)
anker := map[string]map[string]bool{}
for _, s := range Alle() {
md, err := s.Markdown()
if err != nil {
t.Fatal(err)
}
html, err := Render(md)
if err != nil {
t.Fatal(err)
}
anker[s.Slug] = map[string]bool{}
for _, m := range idMuster.FindAllStringSubmatch(html, -1) {
anker[s.Slug][m[1]] = true
}
}
link := regexp.MustCompile(`\]\((?:/hilfe/([a-z0-9-]+))?#([a-z0-9-]+)\)`)
for _, s := range Alle() {
md, _ := s.Markdown()
for _, m := range link.FindAllStringSubmatch(md, -1) {
ziel := m[1]
if ziel == "" {
ziel = s.Slug
}
if _, gibt := anker[ziel]; !gibt {
t.Errorf("%s: Link auf unbekannte Seite %q", s.Slug, ziel)
continue
}
if !anker[ziel][m[2]] {
t.Errorf("%s: Abschnitt #%s gibt es auf %q nicht", s.Slug, m[2], ziel)
}
}
}
}