vole
Clone
Read only, no credentials:
https://rodent.software/vole.git
Write over SSH with a registered key:
git clone ssh://git@rodent.software:2222/vole.git
A free and open-source forge that lives in your Git repo.
refactor
c5902451905acdaa478f8476844ccd4a510d9ca9 · Keenan Nemetz · 17d ago · parent ec2aba4b
internal/issues/issues_test.go → metadata/issues_test.go +18−40
| @@ -1,12 +1,11 @@ | |||
| 1 | package issues | ||
| 1 | package metadata | ||
| 2 | 2 | ||
| 3 | 3 | import ( | |
| 4 | 4 | "strings" | |
| 5 | 5 | "testing" | |
| 6 | 6 | "time" | |
| 7 | 7 | ||
| 8 | "github.com/rodent-software/vole/internal/frontmatter" | ||
| 9 | "github.com/rodent-software/vole/internal/identity" | ||
| 8 | "github.com/rodent-software/vole/forge/identity" | ||
| 10 | 9 | ) | |
| 11 | 10 | ||
| 12 | 11 | const ( | |
| @@ -34,7 +33,7 @@ func validContents() map[string]string { | |||
| 34 | 33 | ||
| 35 | 34 | func TestParseValid(t *testing.T) { | |
| 36 | 35 | t.Parallel() | |
| 37 | list, err := Parse(validContents()) | ||
| 36 | list, err := ParseIssues(validContents()) | ||
| 38 | 37 | if err != nil { | |
| 39 | 38 | t.Fatalf("unexpected error: %v", err) | |
| 40 | 39 | } | |
| @@ -67,7 +66,7 @@ func TestParseIgnoresUnrelatedFiles(t *testing.T) { | |||
| 67 | 66 | t.Parallel() | |
| 68 | 67 | c := validContents() | |
| 69 | 68 | c["README.md"] = "notes about the issues branch\n" | |
| 70 | if _, err := Parse(c); err != nil { | ||
| 69 | if _, err := ParseIssues(c); err != nil { | ||
| 71 | 70 | t.Fatalf("unrelated files should be ignored: %v", err) | |
| 72 | 71 | } | |
| 73 | 72 | } | |
| @@ -107,7 +106,7 @@ func TestRenderParseRoundTrip(t *testing.T) { | |||
| 107 | 106 | if err != nil { | |
| 108 | 107 | t.Fatalf("%q: render: %v", title, err) | |
| 109 | 108 | } | |
| 110 | got, err := Parse(map[string]string{"issues/" + idA + "/issue.md": string(raw)}) | ||
| 109 | got, err := ParseIssues(map[string]string{"issues/" + idA + "/issue.md": string(raw)}) | ||
| 111 | 110 | if err != nil { | |
| 112 | 111 | t.Fatalf("%q: parse:\n%s\nerror: %v", title, raw, err) | |
| 113 | 112 | } | |
| @@ -131,7 +130,7 @@ func TestAssigneesAreAccountNames(t *testing.T) { | |||
| 131 | 130 | // Build the files the way a writer would, so the validator is what refuses | |
| 132 | 131 | // rather than YAML refusing to parse the line first. | |
| 133 | 132 | mk := func(assignees ...string) map[string]string { | |
| 134 | d := frontmatter.New() | ||
| 133 | d := NewDoc() | ||
| 135 | 134 | d.Text("title", "assigned") | |
| 136 | 135 | d.Word("state", "open") | |
| 137 | 136 | d.Word("author", "keenan") | |
| @@ -146,7 +145,7 @@ func TestAssigneesAreAccountNames(t *testing.T) { | |||
| 146 | 145 | return c | |
| 147 | 146 | } | |
| 148 | 147 | ||
| 149 | list, err := Parse(mk("wilbur", "keenan")) | ||
| 148 | list, err := ParseIssues(mk("wilbur", "keenan")) | ||
| 150 | 149 | if err != nil { | |
| 151 | 150 | t.Fatalf("valid assignees refused: %v", err) | |
| 152 | 151 | } | |
| @@ -155,7 +154,7 @@ func TestAssigneesAreAccountNames(t *testing.T) { | |||
| 155 | 154 | } | |
| 156 | 155 | ||
| 157 | 156 | for _, bad := range []string{"Wilbur", "wil.bur", "@wilbur", "w", strings.Repeat("a", 33), "wil bur"} { | |
| 158 | _, err := Parse(mk(bad)) | ||
| 157 | _, err := ParseIssues(mk(bad)) | ||
| 159 | 158 | ve, ok := err.(*ValidationError) | |
| 160 | 159 | if !ok { | |
| 161 | 160 | t.Fatalf("%q was accepted: %v", bad, err) | |
| @@ -200,7 +199,7 @@ func TestAssigneeRoundTrip(t *testing.T) { | |||
| 200 | 199 | if !strings.Contains(string(raw), "assignees:\n - keenan\n - wilbur\n") { | |
| 201 | 200 | t.Fatalf("assignees not written sorted, one per line:\n%s", raw) | |
| 202 | 201 | } | |
| 203 | got, err := Parse(map[string]string{"issues/" + idA + "/issue.md": string(raw)}) | ||
| 202 | got, err := ParseIssues(map[string]string{"issues/" + idA + "/issue.md": string(raw)}) | ||
| 204 | 203 | if err != nil { | |
| 205 | 204 | t.Fatalf("parse: %v\n%s", raw, err) | |
| 206 | 205 | } | |
| @@ -219,7 +218,7 @@ func TestAnchorRoundTrips(t *testing.T) { | |||
| 219 | 218 | Created: time.Date(2026, 10, 23, 9, 0, 0, 0, time.UTC), | |
| 220 | 219 | Body: "this line is wrong\n", | |
| 221 | 220 | Anchor: &Anchor{ | |
| 222 | Path: "internal/hook/hook.go", | ||
| 221 | Path: "hook/hook.go", | ||
| 223 | 222 | Side: "new", | |
| 224 | 223 | Line: 412, | |
| 225 | 224 | Blob: "0123456789abcdef0123456789abcdef01234567", | |
| @@ -301,17 +300,17 @@ func TestAuthorOfReadsAnyMetadataFile(t *testing.T) { | |||
| 301 | 300 | // about where a comment lives, or an edit commits to a path nobody reads. | |
| 302 | 301 | func TestCommentPathMatchesTheReader(t *testing.T) { | |
| 303 | 302 | t.Parallel() | |
| 304 | path := CommentPath(idA, idB) | ||
| 303 | path := IssueCommentPath(idA, idB) | ||
| 305 | 304 | if path != "issues/"+idA+"/comments/"+idB+".md" { | |
| 306 | 305 | t.Fatalf("path = %q", path) | |
| 307 | 306 | } | |
| 308 | if !IsCommentPath(path) { | ||
| 307 | if !IsIssueCommentPath(path) { | ||
| 309 | 308 | t.Fatal("a comment path was not recognised") | |
| 310 | 309 | } | |
| 311 | if IsCommentPath("issues/" + idA + "/issue.md") { | ||
| 310 | if IsIssueCommentPath("issues/" + idA + "/issue.md") { | ||
| 312 | 311 | t.Fatal("an issue file was mistaken for a comment") | |
| 313 | 312 | } | |
| 314 | if IsCommentPath("requests/" + idA + "/request.md") { | ||
| 313 | if IsIssueCommentPath("requests/" + idA + "/request.md") { | ||
| 315 | 314 | t.Fatal("a request file was mistaken for a comment") | |
| 316 | 315 | } | |
| 317 | 316 | } | |
| @@ -323,7 +322,7 @@ func TestUnquotedTitleIsRefused(t *testing.T) { | |||
| 323 | 322 | c := validContents() | |
| 324 | 323 | c["issues/"+idA+"/issue.md"] = "---\ntitle: fix #12 crash\nstate: open\nauthor: keenan\n" + | |
| 325 | 324 | "created: 2026-10-22T21:00:00Z\n---\nbody\n" | |
| 326 | _, err := Parse(c) | ||
| 325 | _, err := ParseIssues(c) | ||
| 327 | 326 | ve, ok := err.(*ValidationError) | |
| 328 | 327 | if !ok { | |
| 329 | 328 | t.Fatalf("error is %T, want *ValidationError: %v", err, err) | |
| @@ -340,7 +339,7 @@ func TestTitleOnOneLine(t *testing.T) { | |||
| 340 | 339 | c := validContents() | |
| 341 | 340 | c["issues/"+idA+"/issue.md"] = "---\ntitle: \"two\\nlines\"\nstate: open\nauthor: keenan\n" + | |
| 342 | 341 | "created: 2026-10-22T21:00:00Z\n---\nbody\n" | |
| 343 | if _, err := Parse(c); err == nil { | ||
| 342 | if _, err := ParseIssues(c); err == nil { | ||
| 344 | 343 | t.Fatal("a title with a newline was accepted") | |
| 345 | 344 | } | |
| 346 | 345 | } | |
| @@ -400,7 +399,7 @@ func TestValidationFailures(t *testing.T) { | |||
| 400 | 399 | t.Run(name, func(t *testing.T) { | |
| 401 | 400 | c := validContents() | |
| 402 | 401 | mutate(c) | |
| 403 | if _, err := Parse(c); err == nil { | ||
| 402 | if _, err := ParseIssues(c); err == nil { | ||
| 404 | 403 | t.Fatal("expected a validation error") | |
| 405 | 404 | } else if _, ok := err.(*ValidationError); !ok { | |
| 406 | 405 | t.Fatalf("error is %T, want *ValidationError: %v", err, err) | |
| @@ -413,7 +412,7 @@ func TestAllProblemsReported(t *testing.T) { | |||
| 413 | 412 | t.Parallel() | |
| 414 | 413 | c := validContents() | |
| 415 | 414 | c["issues/"+idA+"/issue.md"] = "---\ntitle: \"\"\nstate: nope\nauthor: \"\"\ncreated: \"\"\n---\n" | |
| 416 | _, err := Parse(c) | ||
| 415 | _, err := ParseIssues(c) | ||
| 417 | 416 | ve, ok := err.(*ValidationError) | |
| 418 | 417 | if !ok { | |
| 419 | 418 | t.Fatalf("error is %T: %v", err, err) | |
| @@ -422,24 +421,3 @@ func TestAllProblemsReported(t *testing.T) { | |||
| 422 | 421 | t.Fatalf("want several problems, got %d: %v", len(ve.Problems), ve.Problems) | |
| 423 | 422 | } | |
| 424 | 423 | } | |
| 425 | |||
| 426 | func TestULIDAndOrdering(t *testing.T) { | ||
| 427 | t.Parallel() | ||
| 428 | a, err := NewULID(time.Unix(1700000000, 0)) | ||
| 429 | if err != nil { | ||
| 430 | t.Fatal(err) | ||
| 431 | } | ||
| 432 | b, err := NewULID(time.Unix(1700000001, 0)) | ||
| 433 | if err != nil { | ||
| 434 | t.Fatal(err) | ||
| 435 | } | ||
| 436 | if !ValidULID(a) || !ValidULID(b) { | ||
| 437 | t.Fatalf("invalid ulids: %q %q", a, b) | ||
| 438 | } | ||
| 439 | if a >= b { | ||
| 440 | t.Fatalf("ulids do not sort by time: %q >= %q", a, b) | ||
| 441 | } | ||
| 442 | if ValidULID("lowercase0123456789ABCDEFGH") { | ||
| 443 | t.Fatal("lowercase ulid accepted") | ||
| 444 | } | ||
| 445 | } | ||