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.
|
34a404b3
Keenan Nemetz
10d ago
|
1 | package metadata |
| 2 | ||
| 3 | import ( | |
| 4 | "strings" | |
| 5 | "testing" | |
| 6 | "time" | |
| 7 | ||
|
d37d301f
Keenan Nemetz
15h ago
|
8 | "rodent.software/vole/core/git" |
|
34a404b3
Keenan Nemetz
10d ago
|
9 | ) |
| 10 | ||
| 11 | func TestLoadIssueAtReadsOnlyTheTarget(t *testing.T) { | |
| 12 | t.Parallel() | |
| 13 | root := t.TempDir() | |
| 14 | if err := git.InitBare(root, "main"); err != nil { | |
| 15 | t.Fatal(err) | |
| 16 | } | |
| 17 | g := git.Open(root) | |
| 18 | tree, err := g.MkTree(nil) | |
| 19 | if err != nil { | |
| 20 | t.Fatal(err) | |
| 21 | } | |
| 22 | seed, err := g.CommitTree(tree, "", "seed") | |
| 23 | if err != nil { | |
| 24 | t.Fatal(err) | |
| 25 | } | |
| 26 | if err := g.UpdateRef(Ref, seed); err != nil { | |
| 27 | t.Fatal(err) | |
| 28 | } | |
| 29 | ||
| 30 | now := time.Date(2026, 10, 30, 12, 0, 0, 0, time.UTC) | |
| 31 | target, err := NewIssue("Target", "target body", "keenan", []string{"bug"}, nil, now) | |
| 32 | if err != nil { | |
| 33 | t.Fatal(err) | |
| 34 | } | |
| 35 | if err := CreateIssue(g, target, "keenan"); err != nil { | |
| 36 | t.Fatal(err) | |
| 37 | } | |
| 38 | other, err := NewIssue("Other", "other body", "keenan", nil, nil, now.Add(time.Minute)) | |
| 39 | if err != nil { | |
| 40 | t.Fatal(err) | |
| 41 | } | |
| 42 | if err := CreateIssue(g, other, "keenan"); err != nil { | |
| 43 | t.Fatal(err) | |
| 44 | } | |
| 45 | c, err := NewIssueComment("One comment", "wilbur", now.Add(2*time.Minute)) | |
| 46 | if err != nil { | |
| 47 | t.Fatal(err) | |
| 48 | } | |
| 49 | if err := AddIssueComment(g, target.ID, c, "wilbur"); err != nil { | |
| 50 | t.Fatal(err) | |
| 51 | } | |
| 52 | ||
| 53 | got, found, err := LoadIssueAt(g, Ref, target.ID) | |
| 54 | if err != nil { | |
| 55 | t.Fatal(err) | |
| 56 | } | |
| 57 | if !found { | |
| 58 | t.Fatal("target issue was not found") | |
| 59 | } | |
| 60 | if got.ID != target.ID || got.Title != "Target" || got.Description != "target body\n" { | |
| 61 | t.Fatalf("loaded %+v, want the target issue", got) | |
| 62 | } | |
| 63 | if len(got.Comments) != 1 || strings.TrimSpace(got.Comments[0].Body) != "One comment" { | |
| 64 | t.Fatalf("comments = %+v, want the one comment", got.Comments) | |
| 65 | } | |
| 66 | if got.ID == other.ID { | |
| 67 | t.Fatal("LoadIssueAt returned the wrong issue") | |
| 68 | } | |
| 69 | ||
| 70 | _, found, err = LoadIssueAt(g, Ref, "01M5JR2JK29KKMAA1T9MDNTPB0") | |
| 71 | if err != nil { | |
| 72 | t.Fatal(err) | |
| 73 | } | |
| 74 | if found { | |
| 75 | t.Fatal("a missing issue was reported as found") | |
| 76 | } | |
| 77 | // An id is used as a path segment, so anything that is not a ULID is simply | |
| 78 | // not an issue rather than a chance to read outside issues/. | |
| 79 | _, found, err = LoadIssueAt(g, Ref, "../../etc/passwd") | |
| 80 | if err != nil { | |
| 81 | t.Fatal(err) | |
| 82 | } | |
| 83 | if found { | |
| 84 | t.Fatal("a non-ULID was read as an issue") | |
| 85 | } | |
| 86 | } |