repo_testing.go

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