repo_testing.go

  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}