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 · core/lfs/lfs_test.go

Line attribution on main · 168 line(s)

64ac5071
Keenan Nemetz
17d ago
1 package lfs
2
3 import (
4 "bytes"
5 "crypto/sha256"
6 "encoding/hex"
7 "errors"
8 "io"
9 "os"
10 "path/filepath"
11 "strings"
12 "testing"
13 )
14
15 func oidOf(b []byte) string {
16 sum := sha256.Sum256(b)
17 return hex.EncodeToString(sum[:])
18 }
19
20 func TestPointerRoundTrip(t *testing.T) {
44530e74
Keenan Nemetz
16d ago
21 t.Parallel()
64ac5071
Keenan Nemetz
17d ago
22 data := bytes.Repeat([]byte("x"), 1234)
23 p := Pointer{OID: oidOf(data), Size: int64(len(data))}
24 got, ok := ParsePointer(p.Encode())
25 if !ok {
26 t.Fatalf("could not parse what Encode wrote: %q", p.Encode())
27 }
28 if got != p {
29 t.Fatalf("round trip changed the pointer: %+v != %+v", got, p)
30 }
31 }
32
33 func TestPointerIsStrictAboutWhatItNames(t *testing.T) {
44530e74
Keenan Nemetz
16d ago
34 t.Parallel()
64ac5071
Keenan Nemetz
17d ago
35 good := string(Pointer{OID: strings.Repeat("a", 64), Size: 5}.Encode())
36 cases := map[string]string{
37 "wrong version": strings.Replace(good, PointerVersion, "https://example.com/v1", 1),
38 "sha1 oid": strings.Replace(good, "sha256:", "sha1:", 1),
39 "short oid": strings.Replace(good, strings.Repeat("a", 64), "abc", 1),
40 "negative size": strings.Replace(good, "size 5", "size -5", 1),
41 "missing size": strings.Replace(good, "size 5\n", "", 1),
42 "not a pointer": "title: a note, not an LFS pointer\n",
43 "empty": "",
44 "binary garbage": "\x00\x01\x02",
45 }
46 for name, text := range cases {
47 if _, ok := ParsePointer([]byte(text)); ok {
48 t.Errorf("%s: parsed as a pointer, should not have", name)
49 }
50 }
51 // Unknown keys are fine: git-lfs writes ext- lines for its extensions.
52 withExt := good + "ext-0-foo sha256:" + strings.Repeat("b", 64) + "\n"
53 if _, ok := ParsePointer([]byte(withExt)); !ok {
54 t.Errorf("an unknown key should not make a pointer unreadable")
55 }
56 }
57
58 func TestStorePutOpenDedupe(t *testing.T) {
44530e74
Keenan Nemetz
16d ago
59 t.Parallel()
64ac5071
Keenan Nemetz
17d ago
60 s := New(t.TempDir(), 1<<20)
61 data := []byte("some bytes worth storing")
62 oid := oidOf(data)
63
65b88b3b
Keenan Nemetz
16d ago
64 n, created, err := s.Put("demo", oid, int64(len(data)), bytes.NewReader(data))
64ac5071
Keenan Nemetz
17d ago
65 if err != nil || n != int64(len(data)) || !created {
66 t.Fatalf("Put = %d, %v, %v; want %d, true, nil", n, created, err, len(data))
67 }
65b88b3b
Keenan Nemetz
16d ago
68 if size, err := s.Stat("demo", oid); err != nil || size != int64(len(data)) {
64ac5071
Keenan Nemetz
17d ago
69 t.Fatalf("Stat = %d, %v", size, err)
70 }
65b88b3b
Keenan Nemetz
16d ago
71 f, size, err := s.Open("demo", oid)
64ac5071
Keenan Nemetz
17d ago
72 if err != nil {
73 t.Fatal(err)
74 }
75 got, _ := io.ReadAll(f)
76 f.Close()
77 if !bytes.Equal(got, data) || size != int64(len(data)) {
78 t.Fatalf("Open returned %q (%d bytes)", got, size)
79 }
80 // The same content twice is one object.
65b88b3b
Keenan Nemetz
16d ago
81 if _, created, err := s.Put("demo", oid, int64(len(data)), bytes.NewReader(data)); err != nil || created {
64ac5071
Keenan Nemetz
17d ago
82 t.Fatalf("second Put created=%v err=%v; want deduplicated", created, err)
83 }
84
85 objects, bytesUsed, err := s.Usage()
86 if err != nil || objects != 1 || bytesUsed != int64(len(data)) {
87 t.Fatalf("Usage = %d, %d, %v", objects, bytesUsed, err)
88 }
89 if err := s.Remove(oid); err != nil {
90 t.Fatal(err)
91 }
65b88b3b
Keenan Nemetz
16d ago
92 if s.Has("demo", oid) {
64ac5071
Keenan Nemetz
17d ago
93 t.Fatal("object still present after Remove")
94 }
95 if err := s.Remove(oid); err != nil {
96 t.Fatalf("Remove of a missing object should be quiet: %v", err)
97 }
98 }
99
100 func TestStoreRefusesWhatDoesNotMatchItsKey(t *testing.T) {
44530e74
Keenan Nemetz
16d ago
101 t.Parallel()
64ac5071
Keenan Nemetz
17d ago
102 s := New(t.TempDir(), 1<<20)
103 data := []byte("real content")
104 oid := oidOf([]byte("different content"))
105
65b88b3b
Keenan Nemetz
16d ago
106 if _, _, err := s.Put("demo", oid, int64(len(data)), bytes.NewReader(data)); !errors.Is(err, ErrMismatch) {
64ac5071
Keenan Nemetz
17d ago
107 t.Fatalf("Put of mismatched content = %v; want ErrMismatch", err)
108 }
65b88b3b
Keenan Nemetz
16d ago
109 if s.Has("demo", oid) {
64ac5071
Keenan Nemetz
17d ago
110 t.Fatal("mismatched content was stored under the wrong id")
111 }
112
113 tight := New(t.TempDir(), 1024)
114 big := bytes.Repeat([]byte("x"), 2048)
65b88b3b
Keenan Nemetz
16d ago
115 if _, _, err := tight.Put("demo", oidOf(big), 0, bytes.NewReader(big)); !errors.Is(err, ErrTooLarge) {
64ac5071
Keenan Nemetz
17d ago
116 t.Fatalf("oversized Put = %v; want ErrTooLarge", err)
117 }
118
119 small := []byte("declared size lies")
65b88b3b
Keenan Nemetz
16d ago
120 if _, _, err := s.Put("demo", oidOf(small), 9999, bytes.NewReader(small)); !errors.Is(err, ErrMismatch) {
64ac5071
Keenan Nemetz
17d ago
121 t.Fatalf("Put with a lying size = %v; want ErrMismatch", err)
122 }
123
65b88b3b
Keenan Nemetz
16d ago
124 if _, err := s.Stat("demo", "not-an-oid"); !errors.Is(err, ErrBadOID) {
64ac5071
Keenan Nemetz
17d ago
125 t.Fatalf("Stat of a malformed id = %v", err)
126 }
65b88b3b
Keenan Nemetz
16d ago
127 if _, err := s.Stat("demo", strings.Repeat("f", 64)); !errors.Is(err, ErrNotFound) {
64ac5071
Keenan Nemetz
17d ago
128 t.Fatalf("Stat of an absent object = %v", err)
129 }
130 }
131
132 func TestStoreWalkFindsShardedObjects(t *testing.T) {
44530e74
Keenan Nemetz
16d ago
133 t.Parallel()
64ac5071
Keenan Nemetz
17d ago
134 root := t.TempDir()
135 s := New(root, 1<<20)
136 var want []string
137 for i := 0; i < 3; i++ {
138 data := []byte(strings.Repeat("ab", i+1))
139 oid := oidOf(data)
65b88b3b
Keenan Nemetz
16d ago
140 if _, _, err := s.Put("demo", oid, int64(len(data)), bytes.NewReader(data)); err != nil {
64ac5071
Keenan Nemetz
17d ago
141 t.Fatal(err)
142 }
143 want = append(want, oid)
144 }
145 var seen []string
146 if err := s.Walk(func(oid string, size int64) error {
147 seen = append(seen, oid)
148 return nil
149 }); err != nil {
150 t.Fatal(err)
151 }
152 if len(seen) != len(want) {
153 t.Fatalf("Walk saw %d objects, want %d (%v)", len(seen), len(want), seen)
154 }
155 for _, oid := range want {
65b88b3b
Keenan Nemetz
16d ago
156 if !s.Has("demo", oid) {
64ac5071
Keenan Nemetz
17d ago
157 t.Fatalf("Walk did not see %s, which is at %s", oid, s.ObjectPath(oid))
158 }
159 }
160 // The path really is where the file is: a sweep that cannot stat objects
161 // silently deletes nothing and reports success.
162 if _, err := os.Stat(s.ObjectPath(want[0])); err != nil {
163 t.Fatalf("ObjectPath does not exist: %v", err)
164 }
165 if filepath.Base(s.ObjectPath(want[0])) != want[0] {
166 t.Fatalf("ObjectPath basename %q is not the object id", filepath.Base(s.ObjectPath(want[0])))
167 }
168 }