Sign in

Search issue titles and bodies, change requests and comments. Private repositories you cannot read are not included.

vole
Clone

Read only, no credentials:

https://rodent.software/git/vole.git

Write over SSH with a registered key:

git clone ssh://git@0.0.0.0:2222/vole.git

A free and open-source forge that lives in your Git repo.

Blame · metadata/frontmatter_test.go

Line attribution on main · 200 line(s)

c5902451
Keenan Nemetz
16d ago
1 package metadata
307cbc2a
Keenan Nemetz
17d ago
2
3 import (
4 "strings"
5 "testing"
6 )
7
8 func TestSplit(t *testing.T) {
44530e74
Keenan Nemetz
16d ago
9 t.Parallel()
307cbc2a
Keenan Nemetz
17d ago
10 for _, c := range []struct {
11 name, raw, head, body string
12 wantErr bool
13 }{
14 {
15 name: "fields and body",
16 raw: "---\ntitle: \"x\"\n---\nbody\n",
17 head: "title: \"x\"", body: "body\n",
18 },
19 {
20 name: "body may contain a rule",
21 raw: "---\ntitle: \"x\"\n---\nintro\n\n---\n\nmore\n",
22 head: "title: \"x\"", body: "intro\n\n---\n\nmore\n",
23 },
24 {
25 name: "record with no body",
26 raw: "---\nbase: main\n---\n",
27 head: "base: main", body: "",
28 },
29 {
30 name: "closing fence without a trailing newline",
31 raw: "---\nbase: main\n---",
32 head: "base: main", body: "",
33 },
34 {name: "no opening fence", raw: "title: x\n", wantErr: true},
35 {name: "no closing fence", raw: "---\ntitle: x\n", wantErr: true},
36 } {
37 t.Run(c.name, func(t *testing.T) {
38 head, body, err := Split([]byte(c.raw))
39 if c.wantErr {
40 if err == nil {
41 t.Fatal("expected an error")
42 }
43 return
44 }
45 if err != nil {
46 t.Fatalf("unexpected error: %v", err)
47 }
48 if head != c.head || body != c.body {
49 t.Fatalf("head = %q, body = %q; want %q, %q", head, body, c.head, c.body)
50 }
51 })
52 }
53 }
54
55 func TestDecodeRefusesUnknownFields(t *testing.T) {
44530e74
Keenan Nemetz
16d ago
56 t.Parallel()
307cbc2a
Keenan Nemetz
17d ago
57 var v struct {
58 Title string `yaml:"title"`
59 }
60 if err := Decode("title: \"x\"", &v); err != nil {
61 t.Fatalf("decode: %v", err)
62 }
63 if v.Title != "x" {
64 t.Fatalf("title = %q", v.Title)
65 }
66 if err := Decode("title: \"x\"\nsneaky: 1", &v); err == nil {
67 t.Fatal("an undeclared field was accepted")
68 }
69 // An empty head is not an error: an approval with no fields is still a file.
70 if err := Decode("", &v); err != nil {
71 t.Fatalf("empty head: %v", err)
72 }
73 }
74
75 func TestRoundTrip(t *testing.T) {
44530e74
Keenan Nemetz
16d ago
76 t.Parallel()
307cbc2a
Keenan Nemetz
17d ago
77 for _, c := range []struct {
78 name, head, got string
79 wantErr bool
80 }{
81 {"quoted", `title: "fix #12 crash"`, "fix #12 crash", false},
82 {"bare and unchanged", "title: crash on startup", "crash on startup", false},
83 {"bare and truncated", "title: fix #12 crash", "fix", true},
84 {"bare but read as nothing", "title: null", "", true},
85 {"absent", "state: open", "", false},
86 } {
87 t.Run(c.name, func(t *testing.T) {
88 err := RoundTrip(c.head, "title", c.got)
89 if c.wantErr && err == nil {
90 t.Fatal("expected an error")
91 }
92 if !c.wantErr && err != nil {
93 t.Fatalf("unexpected error: %v", err)
94 }
95 })
96 }
97 }
98
99 func TestText(t *testing.T) {
44530e74
Keenan Nemetz
16d ago
100 t.Parallel()
307cbc2a
Keenan Nemetz
17d ago
101 // A free-text field arrives intact.
102 if err := Text(`title: "fix #12 crash"`, "title", "fix #12 crash"); err != nil {
103 t.Fatalf("unexpected error: %v", err)
104 }
105 // Unquoted and unchanged is fine too.
106 if err := Text("author: keenan", "author", "keenan"); err != nil {
107 t.Fatalf("unexpected error: %v", err)
108 }
109 // Truncated by a comment.
110 if err := Text("title: fix #12 crash", "title", "fix"); err == nil {
111 t.Fatal("a truncated title was accepted")
112 }
113 // Two lines after unescaping.
114 if err := Text(`title: "two\nlines"`, "title", "two\nlines"); err == nil {
115 t.Fatal("a two-line title was accepted")
116 }
117 }
118
119 func TestDoc(t *testing.T) {
44530e74
Keenan Nemetz
16d ago
120 t.Parallel()
c5902451
Keenan Nemetz
16d ago
121 d := NewDoc()
307cbc2a
Keenan Nemetz
17d ago
122 d.Text("title", `fix "quoted" #12`)
123 d.Word("state", "open")
124 d.Word("base", "null")
125 d.List("labels", []string{"bug", "null"})
126 raw, err := d.Bytes("It broke.\n")
127 if err != nil {
128 t.Fatalf("bytes: %v", err)
129 }
130 want := "---\n" +
131 "title: \"fix \\\"quoted\\\" #12\"\n" +
132 "state: open\n" +
133 "base: \"null\"\n" +
134 "labels:\n" +
135 " - bug\n" +
136 " - \"null\"\n" +
137 "---\n" +
138 "It broke.\n"
139 if string(raw) != want {
140 t.Fatalf("rendered:\n%s\nwant:\n%s", raw, want)
141 }
142
143 // One label per line is the point: adding one is an added line.
c5902451
Keenan Nemetz
16d ago
144 grown := NewDoc()
307cbc2a
Keenan Nemetz
17d ago
145 grown.List("labels", []string{"bug", "needs-repro"})
146 grownRaw, err := grown.Bytes("")
147 if err != nil {
148 t.Fatal(err)
149 }
150 if got := strings.Count(string(grownRaw), " - "); got != 2 {
151 t.Fatalf("labels rendered on %d lines, want 2:\n%s", got, grownRaw)
152 }
153 }
154
155 func TestDocRefusesControlCharacters(t *testing.T) {
44530e74
Keenan Nemetz
16d ago
156 t.Parallel()
307cbc2a
Keenan Nemetz
17d ago
157 for _, value := range []string{"two\nlines", "tab\there", "del\x7f"} {
c5902451
Keenan Nemetz
16d ago
158 d := NewDoc()
307cbc2a
Keenan Nemetz
17d ago
159 d.Text("title", value)
160 if _, err := d.Bytes(""); err == nil {
161 t.Fatalf("%q was accepted", value)
162 }
163 }
164 }
165
166 func TestValidText(t *testing.T) {
44530e74
Keenan Nemetz
16d ago
167 t.Parallel()
307cbc2a
Keenan Nemetz
17d ago
168 if err := ValidText("crash on a<b> & c"); err != nil {
169 t.Fatalf("unexpected error: %v", err)
170 }
171 for _, value := range []string{"a\nb", "a\tb", "a\x00b", "a\u2028b"} {
172 if err := ValidText(value); err == nil {
173 t.Fatalf("%q was accepted", value)
174 }
175 }
176 }
177
178 func TestPlain(t *testing.T) {
44530e74
Keenan Nemetz
16d ago
179 t.Parallel()
307cbc2a
Keenan Nemetz
17d ago
180 for _, c := range []struct {
181 in string
182 want bool
183 }{
184 {"bug", true},
185 {"needs-repro", true},
186 {"123", true},
187 {"yes", true},
188 {"null", false},
189 {"~", false},
190 {"", false},
191 {" leading", false},
192 {"two words", true},
193 {"a: b", false},
194 {"why # not", false},
195 } {
196 if got := plain(c.in); got != c.want {
197 t.Fatalf("plain(%q) = %v, want %v", c.in, got, c.want)
198 }
199 }
200 }