// Copyright (c) 2026 Petr Balvín (https://petrbalvin.org) // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 package biblio import ( "strings" "testing" ) func TestParseStructuredAndRaw(t *testing.T) { entries := Parse([]map[string]any{ { "title": "Observational evidence from supernovae", "authors": []any{ map[string]any{"name": "Riess, A. G.", "orcid": "0000-0002-1825-0097"}, "Filippenko, A. V.", }, "venue": "The Astronomical Journal", "year": "1998", "doi": "doi:10.1103/PhysRevD.59.103502.", }, { "raw": "Goldhaber, G. a kol., arXiv:astro-ph/0408076, doi:10.1088/1475-7516/2004/12/010.", }, }) if len(entries) != 2 { t.Fatalf("entries = %d", len(entries)) } first := entries[0] if first.Num != 1 || first.Title == "" || len(first.Authors) != 2 { t.Fatalf("first = %+v", first) } if first.Authors[0].ORCID != "0000-0002-1825-0097" { t.Fatalf("author orcid lost: %+v", first.Authors[0]) } if first.DOI != "10.1103/PhysRevD.59.103502" { t.Fatalf("doi not cleaned: %q", first.DOI) } second := entries[1] if second.Num != 2 || second.DOI != "10.1088/1475-7516/2004/12/010" || second.ArXiv != "astro-ph/0408076" { t.Fatalf("raw entry not enriched: %+v", second) } } func TestListHTMLAndPlace(t *testing.T) { entries := Parse([]map[string]any{ {"title": "Alpha", "doi": "10.1000/alpha", "authors": []any{"A. Author"}}, }) list := ListHTML(entries) for _, want := range []string{ `
`, `
  • `, `href="https://doi.org/10.1000/alpha"`, "orcid", "A. Author.", } { if want == "orcid" { if strings.Contains(list, want) { t.Fatalf("unexpected orcid: %s", list) } continue } if !strings.Contains(list, want) { t.Fatalf("list missing %q:\n%s", want, list) } } // The marker is replaced in place; without it the list is appended. body := "

    Intro.

    \n

    " + Marker + "

    \n

    After.

    \n" placed := Place(body, entries) if !strings.Contains(placed, "Intro.") || !strings.Contains(placed, "After.") || strings.Contains(placed, Marker) { t.Fatalf("marker placement wrong:\n%s", placed) } if !strings.HasSuffix(Place("

    Only.

    ", entries), "
  • \n") { t.Fatal("append mode missing") } if Place("

    x

    ", nil) != "

    x

    " { t.Fatal("empty entries should not change the body") } } func TestLinkCitationsRewritesTextOnly(t *testing.T) { entries := Parse([]map[string]any{{"title": "A"}, {"title": "B"}}) html := "

    Work [1] and [2], even [3] out of range.

    " + "

    Code stays: list[1] = 2.

    " + "
    array[2]=x
    " + `text [1] inside link` + `href untouched` out := LinkCitations(html, entries) if !strings.Contains(out, `[1]`) { t.Fatalf("inline [1] not linked:\n%s", out) } if strings.Contains(out, `href="#ref-3"`) { t.Fatal("out-of-range [3] was linked") } if !strings.Contains(out, "list[1] = 2") { t.Fatal("code content was rewritten") } if !strings.Contains(out, "
    array[2]=x
    ") { t.Fatal("pre content was rewritten") } // Inside an existing anchor the marker stays text; no nesting. if !strings.Contains(out, ">text [1] inside link") { t.Fatalf("anchor text was rewritten:\n%s", out) } if !strings.Contains(out, `data-q="[2]"`) { t.Fatal("attribute value was rewritten") } } func TestStructuredEntryStripsTokensFromText(t *testing.T) { entries := Parse([]map[string]any{{ "title": "Timescale stretch parameterization, doi:10.1088/1475-7516/2004/12/010.", "authors": []any{"Goldhaber, G. a kol."}, }}) e := entries[0] if e.DOI != "10.1088/1475-7516/2004/12/010" { t.Fatalf("doi not pulled out: %q", e.DOI) } line := ListHTML(entries) if got := strings.Count(line, "refs-link"); got != 1 { t.Fatalf("expected one identifier link, got %d:\n%s", got, line) } if before := line[:strings.Index(line, `doi:10.5555/tdssc.2026<`, `href="https://doi.org/10.9999/other">doi:10.9999/other<`, } { if !strings.Contains(list, want) { t.Fatalf("list missing %q:\n%s", want, list) } } if strings.Count(list, `href="/api/volumen/posts/tdssc"`) != 2 { t.Fatalf("internal link count wrong:\n%s", list) } cits := Citations(entries) if cits[0]["url"] != "/api/volumen/posts/tdssc" { t.Fatalf("citation url not internal: %v", cits[0]) } // The canonical identifier survives unchanged next to the internal link. if cits[0]["identifier"] != "https://doi.org/10.5555/tdssc.2026" { t.Fatalf("citation identifier changed: %v", cits[0]) } if _, ok := cits[2]["url"]; ok { t.Fatalf("external citation gained a url: %v", cits[2]) } if _, ok := cits[3]["identifier"]; ok { t.Fatalf("bare citation gained an identifier: %v", cits[3]) } } // A raw entry's internal link is escaped like every other href: the slug // it names comes from the store, and a hand-edited file could carry a // quote that must not open an attribute in the rendered list. func TestRawEntryEscapesInternalLink(t *testing.T) { entries := Parse([]map[string]any{ {"raw": "Viz doi:10.5555/hostile.2026 (2020).", "doi": "10.5555/hostile.2026"}, }) entries[0].Internal = `/api/volumen/posts/x" onmouseover="alert(1)` out := entries[0].identifierLinks() if strings.Contains(out, `" onmouseover=`) { t.Fatalf("the internal link broke out of its attribute: %q", out) } if !strings.Contains(out, "href=\"/api/volumen/posts/x" onmouseover="alert(1)\"") { t.Fatalf("the internal link lost its target: %q", out) } }