package modules
import (
"html/template"
"strings"
"testing"
)
// lines builds the Line slice annotateStructure expects, mirroring what
// blobMod produces before the structure pass runs.
func lines(raw string) []Line {
out := []Line{}
for i, t := range strings.Split(raw, "\n") {
out = append(out, Line{N: i + 1, Text: template.HTML(t)})
}
return out
}
const sampleYAML = `defaults: &defaults
adapter: postgres
pool: 5
development:
<<: *defaults
database: dev
test:
<<: *defaults
database: test
hosts:
- a
- b
`
func TestFoldRangesYAML(t *testing.T) {
ls := lines(sampleYAML)
annotateStructure(ls, []byte(sampleYAML), "yaml")
want := map[int]int{
1: 3, // defaults: block ends at "pool: 5", not the blank line after
5: 7, // development:
9: 14, // test:, stopping short of the empty line 15
12: 14, // hosts:
}
for _, l := range ls {
if got, ok := want[l.N]; ok {
if l.FoldTo != got {
t.Errorf("line %d: FoldTo = %d, want %d", l.N, l.FoldTo, got)
}
} else if l.FoldTo != 0 {
t.Errorf("line %d: FoldTo = %d, want none", l.N, l.FoldTo)
}
}
if ls[1].Depth != 1 {
t.Errorf("line 2 depth = %d, want 1", ls[1].Depth)
}
if ls[13].Depth != 2 {
t.Errorf("line 14 depth = %d, want 2", ls[13].Depth)
}
}
func TestKeysAndAnchors(t *testing.T) {
ls := lines(sampleYAML)
annotateStructure(ls, []byte(sampleYAML), "yaml")
if ls[0].Key != "defaults" {
t.Errorf("line 1 key = %q, want %q", ls[0].Key, "defaults")
}
if ls[13].Key != "" {
t.Errorf("line 14 (sequence item) key = %q, want none", ls[13].Key)
}
// The anchor is reported on the line the &token is written on, not on
// the line the mapping it names begins.
a := ls[0].Anchor
if a == nil {
t.Fatal("line 1: no anchor")
}
if a.Name != "defaults" || a.Uses != 2 {
t.Errorf("anchor = %+v, want {defaults 2}", *a)
}
for _, n := range []int{6, 10} {
r := ls[n-1].Ref
if r == nil {
t.Fatalf("line %d: no ref", n)
}
if r.Name != "defaults" || r.Line != 1 {
t.Errorf("line %d ref = %+v, want defaults@1", n, *r)
}
if !strings.Contains(r.Preview, "adapter: postgres") {
t.Errorf("line %d preview = %q, want the resolved mapping", n, r.Preview)
}
}
}
// Anchors defined in the second document of a stream are still resolved:
// yaml.Unmarshal would only ever see the first.
func TestMultiDocumentAnchors(t *testing.T) {
const src = `---
kind: First
---
base: &b
x: 1
copy: *b
`
ls := lines(src)
annotateStructure(ls, []byte(src), "yaml")
if ls[3].Anchor == nil || ls[3].Anchor.Name != "b" {
t.Fatalf("line 4 anchor = %+v, want b", ls[3].Anchor)
}
if r := ls[5].Ref; r == nil || r.Line != 4 {
t.Fatalf("line 6 ref = %+v, want b@4", r)
}
}
func TestFoldRangesJSON(t *testing.T) {
const src = `{
"name": "gsg",
"deps": {
"a": 1
},
"list": [
2
]
}`
ls := lines(src)
annotateStructure(ls, []byte(src), "json")
if ls[0].FoldTo != 8 {
t.Errorf("line 1 FoldTo = %d, want 8", ls[0].FoldTo)
}
if ls[2].FoldTo != 4 {
t.Errorf("line 3 FoldTo = %d, want 4", ls[2].FoldTo)
}
if ls[5].FoldTo != 7 {
t.Errorf("line 6 FoldTo = %d, want 7", ls[5].FoldTo)
}
if ls[1].Key != "name" {
t.Errorf("line 2 key = %q, want %q", ls[1].Key, "name")
}
// No anchors outside YAML, and a brace inside a string is not structure.
if ls[1].Anchor != nil || ls[1].Ref != nil {
t.Error("JSON line carries anchor/ref data")
}
}
// Minified JSON has no indentation to fold on; it must degrade to a plain
// view rather than producing bogus ranges.
func TestMinifiedJSONHasNoFolds(t *testing.T) {
const src = `{"a":{"b":[1,2]},"c":3}`
ls := lines(src)
annotateStructure(ls, []byte(src), "json")
if ls[0].FoldTo != 0 {
t.Errorf("FoldTo = %d, want 0", ls[0].FoldTo)
}
}