163 lines
5.5 KiB
Go
163 lines
5.5 KiB
Go
package entry
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// TestRenderHTMLEscapesRawHTML ist ein Sicherheitstest.
|
|
//
|
|
// Das gerenderte Ergebnis wird in der Weboberfläche ohne weiteres Escaping
|
|
// eingesetzt. Der Schutz davor, dass jemand über einen Eintrag ein <script>
|
|
// einschleust, hängt damit allein daran, dass goldmark kein Roh-HTML durchreicht.
|
|
// Wird diese Einstellung je gelockert, schlägt dieser Test an.
|
|
func TestRenderHTMLEscapesRawHTML(t *testing.T) {
|
|
boesartig := []string{
|
|
"<script>alert(1)</script>",
|
|
"<img src=x onerror=alert(1)>",
|
|
"<iframe src=\"https://fremd.example\"></iframe>",
|
|
}
|
|
|
|
for _, eingabe := range boesartig {
|
|
html, err := RenderHTML(eingabe, "/p/test/e/")
|
|
if err != nil {
|
|
t.Fatalf("RenderHTML(%q): %v", eingabe, err)
|
|
}
|
|
if strings.Contains(html, "<script") || strings.Contains(html, "onerror") || strings.Contains(html, "<iframe") {
|
|
t.Errorf("Roh-HTML kam durch: %q → %q", eingabe, html)
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestRenderHTMLResolvesLinks prüft, dass [[bezeichner]] zu einem Link wird.
|
|
func TestRenderHTMLResolvesLinks(t *testing.T) {
|
|
html, err := RenderHTML("Siehe [[server-zugang]] für Details.", "/p/test/e/")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if !strings.Contains(html, `href="/p/test/e/server-zugang"`) {
|
|
t.Errorf("der Verweis wurde nicht aufgelöst: %s", html)
|
|
}
|
|
}
|
|
|
|
// TestRenderHTMLKeepsMarkdown prüft, dass gewöhnliches Markdown funktioniert.
|
|
func TestRenderHTMLKeepsMarkdown(t *testing.T) {
|
|
html, err := RenderHTML("# Überschrift\n\n- Punkt eins\n- Punkt zwei\n\n`code`", "/p/test/e/")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
for _, erwartet := range []string{"<h1", "<ul>", "<li>", "<code>"} {
|
|
if !strings.Contains(html, erwartet) {
|
|
t.Errorf("%s fehlt im Ergebnis: %s", erwartet, html)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestRenderPlainStripsLinkSyntax(t *testing.T) {
|
|
got := RenderPlain("Siehe [[server-zugang]] für Details.")
|
|
if strings.Contains(got, "[[") {
|
|
t.Errorf("die Verweis-Klammern blieben stehen: %q", got)
|
|
}
|
|
if !strings.Contains(got, "server-zugang") {
|
|
t.Errorf("der Bezeichner ging verloren: %q", got)
|
|
}
|
|
}
|
|
|
|
// ── Textausschnitte ──────────────────────────────────────────────────────────
|
|
|
|
func TestSnippetAroundMatch(t *testing.T) {
|
|
text := strings.Repeat("Vorlauf. ", 40) + "GESUCHT " + strings.Repeat("Nachlauf. ", 40)
|
|
|
|
got := Snippet(text, "GESUCHT")
|
|
|
|
if !strings.Contains(got, "GESUCHT") {
|
|
t.Error("der Treffer fehlt im Ausschnitt")
|
|
}
|
|
// Der Ausschnitt soll kurz sein — er dient dem Einordnen, nicht dem Lesen.
|
|
if len(got) > 400 {
|
|
t.Errorf("Ausschnitt ist %d Zeichen lang, das ist zu viel", len(got))
|
|
}
|
|
// Vorne und hinten wurde gekürzt, das muss sichtbar sein.
|
|
if !strings.HasPrefix(got, "…") || !strings.HasSuffix(got, "…") {
|
|
t.Errorf("die Auslassungszeichen fehlen: %q", got)
|
|
}
|
|
}
|
|
|
|
func TestSnippetWithoutMatch(t *testing.T) {
|
|
// Kein wörtlicher Treffer — etwa weil der Fund über die Präfix-Suche kam.
|
|
got := Snippet("Ein kurzer Text ohne den Suchbegriff.", "deploy")
|
|
|
|
if got == "" {
|
|
t.Error("ohne Treffer kam gar kein Ausschnitt zurück")
|
|
}
|
|
if !strings.Contains(got, "kurzer Text") {
|
|
t.Errorf("es wurde nicht der Textanfang genommen: %q", got)
|
|
}
|
|
}
|
|
|
|
func TestSnippetShortText(t *testing.T) {
|
|
got := Snippet("Kurz.", "Kurz")
|
|
if got != "Kurz." {
|
|
t.Errorf("Snippet = %q, erwartet %q", got, "Kurz.")
|
|
}
|
|
}
|
|
|
|
// TestHighlightSnippetEscapesFirst ist ein Sicherheitstest: Der Inhalt muss
|
|
// escaped werden, BEVOR die Hervorhebung eingesetzt wird. Andersherum könnte ein
|
|
// Eintrag eigenes HTML in die Trefferliste einschleusen.
|
|
func TestHighlightSnippetEscapesFirst(t *testing.T) {
|
|
got := HighlightSnippet("Ein <script>alert(1)</script> im Text", "script")
|
|
|
|
if strings.Contains(got, "<script>") {
|
|
t.Errorf("unescaptes HTML im Ausschnitt: %q", got)
|
|
}
|
|
// Die spitzen Klammern des Inhalts müssen escaped sein. Geprüft wird auf
|
|
// "<" statt auf "<script>", weil die Hervorhebung mitten im
|
|
// escapten Text steht: "<<mark>script</mark>>".
|
|
if !strings.Contains(got, "<") || !strings.Contains(got, ">") {
|
|
t.Errorf("der Inhalt wurde nicht escaped: %q", got)
|
|
}
|
|
// Die Hervorhebung selbst muss echtes HTML sein.
|
|
if !strings.Contains(got, "<mark>") {
|
|
t.Errorf("der Treffer wurde nicht hervorgehoben: %q", got)
|
|
}
|
|
}
|
|
|
|
func TestHighlightSnippetWithoutQuery(t *testing.T) {
|
|
got := HighlightSnippet("Text mit <Klammern>", "")
|
|
if strings.Contains(got, "<mark>") {
|
|
t.Error("ohne Suchbegriff wurde etwas hervorgehoben")
|
|
}
|
|
if strings.Contains(got, "<Klammern>") {
|
|
t.Errorf("der Inhalt wurde nicht escaped: %q", got)
|
|
}
|
|
}
|
|
|
|
// ── Titel raten ──────────────────────────────────────────────────────────────
|
|
|
|
func TestTitleFromContent(t *testing.T) {
|
|
cases := map[string]string{
|
|
"# Deployment\n\nText": "Deployment",
|
|
"## Unterüberschrift\nX": "Unterüberschrift",
|
|
"\n\nErste echte Zeile\nX": "Erste echte Zeile",
|
|
"": "Ohne Titel",
|
|
" \n \n": "Ohne Titel",
|
|
}
|
|
|
|
for eingabe, erwartet := range cases {
|
|
if got := TitleFromContent(eingabe); got != erwartet {
|
|
t.Errorf("TitleFromContent(%q) = %q, erwartet %q", eingabe, got, erwartet)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestTitleFromContentIsShortened(t *testing.T) {
|
|
lang := strings.Repeat("Wort ", 100)
|
|
got := TitleFromContent(lang)
|
|
|
|
if len(got) > 130 {
|
|
t.Errorf("der Titel ist %d Zeichen lang — er wurde nicht gekürzt", len(got))
|
|
}
|
|
}
|