// Copyright (c) 2026 Petr Balvín (https://petrbalvin.org) // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 package post import ( "strings" "testing" "sourcedock.dev/petrbalvin/volumen/internal/biblio" ) // refsSample is a full post file with a [[refs]] array of tables, run // through the real frontmatter parser so the metadata shape the store // actually delivers is what is under test. const refsSample = `+++ title = "Citující práce" slug = "cite" lang = "cs" date = 2026-08-18 [[refs]] title = "Observational evidence from supernovae" authors = ["Riess, A. G. a kol."] venue = "The Astronomical Journal" year = 1998 doi = "doi:10.1103/PhysRevD.59.103502" [[refs]] raw = "Goldhaber, G. a kol., arXiv:astro-ph/0408076" +++ První tvrzení [1], druhé tvrzení [2] a inline kód ` + "`list[2] = 3`" + `. ` + "```" + `go pole[1] se nemění ` + "```" + ` ` + biblio.Marker + ` ` func TestRefsParseAndWeave(t *testing.T) { p := mustParse(t, refsSample) refs := p.Refs() if len(refs) != 2 { t.Fatalf("refs = %d, want 2", len(refs)) } if refs[0].Title == "" || refs[0].DOI != "10.1103/PhysRevD.59.103502" { t.Fatalf("structured ref = %+v", refs[0]) } if refs[1].ArXiv != "astro-ph/0408076" { t.Fatalf("raw ref not enriched: %+v", refs[1]) } // refs is reserved: it never leaks into the custom fields object. if fields := p.CustomFields(); fields != nil { if _, ok := fields["refs"]; ok { t.Fatal("refs leaked into custom fields") } } htmlOut, err := p.HTML() if err != nil { t.Fatalf("HTML: %v", err) } // Inline citations become anchors, the list is spliced at the marker, // identifiers in entries become resolver links, and bracketed // numbers inside code stay exactly as written. for _, want := range []string{ `[1]`, `
`, `
  • `, `href="https://doi.org/10.1103/PhysRevD.59.103502"`, "list[2] = 3", "pole[1] se nemění", } { if !strings.Contains(htmlOut, want) { t.Fatalf("html missing %q:\n%s", want, htmlOut) } } if strings.Contains(htmlOut, biblio.Marker) { t.Fatalf("marker not replaced:\n%s", htmlOut) } // A post without refs renders unchanged and carries none. bare := mustParse(t, "+++\ntitle = \"T\"\nslug = \"t\"\n+++\ntext [1]\n") if len(bare.Refs()) != 0 { t.Fatal("bare post has refs") } if html, _ := bare.HTML(); strings.Contains(html, "ref-cite") { t.Fatal("bare post got citation links") } } // A save from the editor must not drop the bibliography: the form never // carries refs, and the merge keeps untouched keys. func TestToFileSyncPreservesRefs(t *testing.T) { p := mustParse(t, refsSample) out, err := p.ToFile() if err != nil { t.Fatalf("ToFile: %v", err) } if !strings.Contains(out, "[[refs]]") || !strings.Contains(out, "10.1103/PhysRevD.59.103502") { t.Fatalf("refs lost on serialise:\n%s", out) } } func TestInternalCrossLinksAtRender(t *testing.T) { // The cited work is published under another slug in the instance: // the rendered list links to that post, and the entry's resolver // link disappears from the page. p := mustParse(t, refsSample) p.SetLinkIndex(map[string]string{"10.1103/physrevd.59.103502": "riess-1998"}) htmlOut, err := p.HTML() if err != nil { t.Fatalf("HTML: %v", err) } if !strings.Contains(htmlOut, `href="/api/volumen/posts/riess-1998"`) { t.Fatalf("internal link missing:\n%s", htmlOut) } if strings.Contains(htmlOut, `href="https://doi.org/10.1103/PhysRevD.59.103502"`) { t.Fatal("external resolver survived") } // The arXiv entry is not published here: it keeps its resolver. if !strings.Contains(htmlOut, `href="https://arxiv.org/abs/astro-ph/0408076"`) { t.Fatalf("unmatched entry lost its link:\n%s", htmlOut) } // A post citing its own DOI must not link to itself. self := mustParse(t, strings.Replace(refsSample, `slug = "cite"`, `slug = "cite" doi = "10.1103/PhysRevD.59.103502"`, 1)) self.SetLinkIndex(map[string]string{"10.1103/physrevd.59.103502": "cite"}) htmlOut, err = self.HTML() if err != nil { t.Fatalf("HTML: %v", err) } if strings.Contains(htmlOut, `href="/api/volumen/posts/cite"`) { t.Fatal("self-link was built") } if !strings.Contains(htmlOut, `href="https://doi.org/10.1103/PhysRevD.59.103502"`) { t.Fatalf("self cite lost its resolver link:\n%s", htmlOut) } // RefsLinked exposes the resolution to the API layer. linked := p.RefsLinked() if linked[0].Internal != "/api/volumen/posts/riess-1998" { t.Fatalf("RefsLinked[0] = %+v", linked[0]) } if linked[1].Internal != "" { t.Fatalf("RefsLinked[1] should be external: %+v", linked[1]) } }