1package repository
2
3import (
4 "log"
5 "math/rand"
6 "testing"
7
8 "github.com/stretchr/testify/require"
9
10 "github.com/MichaelMure/git-bug/util/lamport"
11)
12
13// TODO: add tests for RepoBleve
14// TODO: add tests for RepoStorage
15
16func CleanupTestRepos(repos ...Repo) {
17 var firstErr error
18 for _, repo := range repos {
19 if repo, ok := repo.(TestedRepo); ok {
20 err := repo.EraseFromDisk()
21 if err != nil {
22 log.Println(err)
23 if firstErr == nil {
24 firstErr = err
25 }
26 }
27 }
28 }
29
30 if firstErr != nil {
31 log.Fatal(firstErr)
32 }
33}
34
35type RepoCreator func(bare bool) TestedRepo
36type RepoCleaner func(repos ...Repo)
37
38// Test suite for a Repo implementation
39func RepoTest(t *testing.T, creator RepoCreator, cleaner RepoCleaner) {
40 for bare, name := range map[bool]string{
41 false: "Plain",
42 true: "Bare",
43 } {
44 t.Run(name, func(t *testing.T) {
45 repo := creator(bare)
46 defer cleaner(repo)
47
48 t.Run("Data", func(t *testing.T) {
49 RepoDataTest(t, repo)
50 })
51
52 t.Run("Config", func(t *testing.T) {
53 RepoConfigTest(t, repo)
54 })
55
56 t.Run("Clocks", func(t *testing.T) {
57 RepoClockTest(t, repo)
58 })
59 })
60 }
61}
62
63// helper to test a RepoConfig
64func RepoConfigTest(t *testing.T, repo RepoConfig) {
65 testConfig(t, repo.LocalConfig())
66}
67
68// helper to test a RepoData
69func RepoDataTest(t *testing.T, repo RepoData) {
70 // Blob
71
72 data := randomData()
73
74 blobHash1, err := repo.StoreData(data)
75 require.NoError(t, err)
76 require.True(t, blobHash1.IsValid())
77
78 blob1Read, err := repo.ReadData(blobHash1)
79 require.NoError(t, err)
80 require.Equal(t, data, blob1Read)
81
82 // Tree
83
84 blobHash2, err := repo.StoreData(randomData())
85 require.NoError(t, err)
86 blobHash3, err := repo.StoreData(randomData())
87 require.NoError(t, err)
88
89 tree1 := []TreeEntry{
90 {
91 ObjectType: Blob,
92 Hash: blobHash1,
93 Name: "blob1",
94 },
95 {
96 ObjectType: Blob,
97 Hash: blobHash2,
98 Name: "blob2",
99 },
100 }
101
102 treeHash1, err := repo.StoreTree(tree1)
103 require.NoError(t, err)
104 require.True(t, treeHash1.IsValid())
105
106 tree1Read, err := repo.ReadTree(treeHash1)
107 require.NoError(t, err)
108 require.ElementsMatch(t, tree1, tree1Read)
109
110 tree2 := []TreeEntry{
111 {
112 ObjectType: Tree,
113 Hash: treeHash1,
114 Name: "tree1",
115 },
116 {
117 ObjectType: Blob,
118 Hash: blobHash3,
119 Name: "blob3",
120 },
121 }
122
123 treeHash2, err := repo.StoreTree(tree2)
124 require.NoError(t, err)
125 require.True(t, treeHash2.IsValid())
126
127 tree2Read, err := repo.ReadTree(treeHash2)
128 require.NoError(t, err)
129 require.ElementsMatch(t, tree2, tree2Read)
130
131 // Commit
132
133 commit1, err := repo.StoreCommit(treeHash1)
134 require.NoError(t, err)
135 require.True(t, commit1.IsValid())
136
137 treeHash1Read, err := repo.GetTreeHash(commit1)
138 require.NoError(t, err)
139 require.Equal(t, treeHash1, treeHash1Read)
140
141 // commit with a parent
142 commit2, err := repo.StoreCommit(treeHash2, commit1)
143 require.NoError(t, err)
144 require.True(t, commit2.IsValid())
145
146 treeHash2Read, err := repo.GetTreeHash(commit2)
147 require.NoError(t, err)
148 require.Equal(t, treeHash2, treeHash2Read)
149
150 // ReadTree should accept tree and commit hashes
151 tree1read, err := repo.ReadTree(commit1)
152 require.NoError(t, err)
153 require.Equal(t, tree1read, tree1)
154
155 c2, err := repo.ReadCommit(commit2)
156 require.NoError(t, err)
157 c2expected := Commit{Hash: commit2, Parents: []Hash{commit1}, TreeHash: treeHash2}
158 require.Equal(t, c2expected, c2)
159
160 // Ref
161
162 exist1, err := repo.RefExist("refs/bugs/ref1")
163 require.NoError(t, err)
164 require.False(t, exist1)
165
166 err = repo.UpdateRef("refs/bugs/ref1", commit2)
167 require.NoError(t, err)
168
169 exist1, err = repo.RefExist("refs/bugs/ref1")
170 require.NoError(t, err)
171 require.True(t, exist1)
172
173 h, err := repo.ResolveRef("refs/bugs/ref1")
174 require.NoError(t, err)
175 require.Equal(t, commit2, h)
176
177 ls, err := repo.ListRefs("refs/bugs")
178 require.NoError(t, err)
179 require.ElementsMatch(t, []string{"refs/bugs/ref1"}, ls)
180
181 err = repo.CopyRef("refs/bugs/ref1", "refs/bugs/ref2")
182 require.NoError(t, err)
183
184 ls, err = repo.ListRefs("refs/bugs")
185 require.NoError(t, err)
186 require.ElementsMatch(t, []string{"refs/bugs/ref1", "refs/bugs/ref2"}, ls)
187
188 commits, err := repo.ListCommits("refs/bugs/ref2")
189 require.NoError(t, err)
190 require.Equal(t, []Hash{commit1, commit2}, commits)
191
192 // Graph
193
194 commit3, err := repo.StoreCommit(treeHash1, commit1)
195 require.NoError(t, err)
196
197 ancestorHash, err := repo.FindCommonAncestor(commit2, commit3)
198 require.NoError(t, err)
199 require.Equal(t, commit1, ancestorHash)
200
201 err = repo.RemoveRef("refs/bugs/ref1")
202 require.NoError(t, err)
203
204 // TODO: testing for commit's signature
205}
206
207// helper to test a RepoClock
208func RepoClockTest(t *testing.T, repo RepoClock) {
209 allClocks, err := repo.AllClocks()
210 require.NoError(t, err)
211 require.Len(t, allClocks, 0)
212
213 clock, err := repo.GetOrCreateClock("foo")
214 require.NoError(t, err)
215 require.Equal(t, lamport.Time(1), clock.Time())
216
217 time, err := clock.Increment()
218 require.NoError(t, err)
219 require.Equal(t, lamport.Time(2), time)
220 require.Equal(t, lamport.Time(2), clock.Time())
221
222 clock2, err := repo.GetOrCreateClock("foo")
223 require.NoError(t, err)
224 require.Equal(t, lamport.Time(2), clock2.Time())
225
226 clock3, err := repo.GetOrCreateClock("bar")
227 require.NoError(t, err)
228 require.Equal(t, lamport.Time(1), clock3.Time())
229
230 allClocks, err = repo.AllClocks()
231 require.NoError(t, err)
232 require.Equal(t, map[string]lamport.Clock{
233 "foo": clock,
234 "bar": clock3,
235 }, allClocks)
236}
237
238func randomData() []byte {
239 var letterRunes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
240 b := make([]byte, 32)
241 for i := range b {
242 b[i] = letterRunes[rand.Intn(len(letterRunes))]
243 }
244 return b
245}