mock_repo.go

  1package repository
  2
  3import (
  4	"crypto/sha1"
  5	"fmt"
  6	"strings"
  7
  8	"github.com/99designs/keyring"
  9	"github.com/go-git/go-billy/v5"
 10	"github.com/go-git/go-billy/v5/memfs"
 11
 12	"github.com/MichaelMure/git-bug/util/lamport"
 13)
 14
 15var _ ClockedRepo = &mockRepoForTest{}
 16var _ TestedRepo = &mockRepoForTest{}
 17
 18// mockRepoForTest defines an instance of Repo that can be used for testing.
 19type mockRepoForTest struct {
 20	*mockRepoConfig
 21	*mockRepoKeyring
 22	*mockRepoCommon
 23	*mockRepoStorage
 24	*mockRepoData
 25	*mockRepoClock
 26}
 27
 28func NewMockRepoForTest() *mockRepoForTest {
 29	return &mockRepoForTest{
 30		mockRepoConfig:  NewMockRepoConfig(),
 31		mockRepoKeyring: NewMockRepoKeyring(),
 32		mockRepoCommon:  NewMockRepoCommon(),
 33		mockRepoStorage: NewMockRepoStorage(),
 34		mockRepoData:    NewMockRepoData(),
 35		mockRepoClock:   NewMockRepoClock(),
 36	}
 37}
 38
 39var _ RepoConfig = &mockRepoConfig{}
 40
 41type mockRepoConfig struct {
 42	localConfig  *MemConfig
 43	globalConfig *MemConfig
 44}
 45
 46func NewMockRepoConfig() *mockRepoConfig {
 47	return &mockRepoConfig{
 48		localConfig:  NewMemConfig(),
 49		globalConfig: NewMemConfig(),
 50	}
 51}
 52
 53// LocalConfig give access to the repository scoped configuration
 54func (r *mockRepoConfig) LocalConfig() Config {
 55	return r.localConfig
 56}
 57
 58// GlobalConfig give access to the git global configuration
 59func (r *mockRepoConfig) GlobalConfig() Config {
 60	return r.globalConfig
 61}
 62
 63// AnyConfig give access to a merged local/global configuration
 64func (r *mockRepoConfig) AnyConfig() ConfigRead {
 65	return mergeConfig(r.localConfig, r.globalConfig)
 66}
 67
 68var _ RepoKeyring = &mockRepoKeyring{}
 69
 70type mockRepoKeyring struct {
 71	keyring *keyring.ArrayKeyring
 72}
 73
 74func NewMockRepoKeyring() *mockRepoKeyring {
 75	return &mockRepoKeyring{
 76		keyring: keyring.NewArrayKeyring(nil),
 77	}
 78}
 79
 80// Keyring give access to a user-wide storage for secrets
 81func (r *mockRepoKeyring) Keyring() Keyring {
 82	return r.keyring
 83}
 84
 85var _ RepoCommon = &mockRepoCommon{}
 86
 87type mockRepoCommon struct{}
 88
 89func NewMockRepoCommon() *mockRepoCommon {
 90	return &mockRepoCommon{}
 91}
 92
 93func (r *mockRepoCommon) GetUserName() (string, error) {
 94	return "René Descartes", nil
 95}
 96
 97// GetUserEmail returns the email address that the user has used to configure git.
 98func (r *mockRepoCommon) GetUserEmail() (string, error) {
 99	return "user@example.com", nil
100}
101
102// GetCoreEditor returns the name of the editor that the user has used to configure git.
103func (r *mockRepoCommon) GetCoreEditor() (string, error) {
104	return "vi", nil
105}
106
107// GetRemotes returns the configured remotes repositories.
108func (r *mockRepoCommon) GetRemotes() (map[string]string, error) {
109	return map[string]string{
110		"origin": "git://github.com/MichaelMure/git-bug",
111	}, nil
112}
113
114var _ RepoStorage = &mockRepoStorage{}
115
116type mockRepoStorage struct {
117	localFs billy.Filesystem
118}
119
120func NewMockRepoStorage() *mockRepoStorage {
121	return &mockRepoStorage{localFs: memfs.New()}
122}
123
124func (m *mockRepoStorage) LocalStorage() billy.Filesystem {
125	return m.localFs
126}
127
128var _ RepoData = &mockRepoData{}
129
130type commit struct {
131	treeHash Hash
132	parent   Hash
133}
134
135type mockRepoData struct {
136	blobs   map[Hash][]byte
137	trees   map[Hash]string
138	commits map[Hash]commit
139	refs    map[string]Hash
140}
141
142func NewMockRepoData() *mockRepoData {
143	return &mockRepoData{
144		blobs:   make(map[Hash][]byte),
145		trees:   make(map[Hash]string),
146		commits: make(map[Hash]commit),
147		refs:    make(map[string]Hash),
148	}
149}
150
151// PushRefs push git refs to a remote
152func (r *mockRepoData) PushRefs(remote string, refSpec string) (string, error) {
153	return "", nil
154}
155
156func (r *mockRepoData) FetchRefs(remote string, refSpec string) (string, error) {
157	return "", nil
158}
159
160func (r *mockRepoData) StoreData(data []byte) (Hash, error) {
161	rawHash := sha1.Sum(data)
162	hash := Hash(fmt.Sprintf("%x", rawHash))
163	r.blobs[hash] = data
164	return hash, nil
165}
166
167func (r *mockRepoData) ReadData(hash Hash) ([]byte, error) {
168	data, ok := r.blobs[hash]
169
170	if !ok {
171		return nil, fmt.Errorf("unknown hash")
172	}
173
174	return data, nil
175}
176
177func (r *mockRepoData) StoreTree(entries []TreeEntry) (Hash, error) {
178	buffer := prepareTreeEntries(entries)
179	rawHash := sha1.Sum(buffer.Bytes())
180	hash := Hash(fmt.Sprintf("%x", rawHash))
181	r.trees[hash] = buffer.String()
182
183	return hash, nil
184}
185
186func (r *mockRepoData) StoreCommit(treeHash Hash) (Hash, error) {
187	rawHash := sha1.Sum([]byte(treeHash))
188	hash := Hash(fmt.Sprintf("%x", rawHash))
189	r.commits[hash] = commit{
190		treeHash: treeHash,
191	}
192	return hash, nil
193}
194
195func (r *mockRepoData) StoreCommitWithParent(treeHash Hash, parent Hash) (Hash, error) {
196	rawHash := sha1.Sum([]byte(treeHash + parent))
197	hash := Hash(fmt.Sprintf("%x", rawHash))
198	r.commits[hash] = commit{
199		treeHash: treeHash,
200		parent:   parent,
201	}
202	return hash, nil
203}
204
205func (r *mockRepoData) UpdateRef(ref string, hash Hash) error {
206	r.refs[ref] = hash
207	return nil
208}
209
210func (r *mockRepoData) RemoveRef(ref string) error {
211	delete(r.refs, ref)
212	return nil
213}
214
215func (r *mockRepoData) RefExist(ref string) (bool, error) {
216	_, exist := r.refs[ref]
217	return exist, nil
218}
219
220func (r *mockRepoData) CopyRef(source string, dest string) error {
221	hash, exist := r.refs[source]
222
223	if !exist {
224		return fmt.Errorf("Unknown ref")
225	}
226
227	r.refs[dest] = hash
228	return nil
229}
230
231func (r *mockRepoData) ListRefs(refPrefix string) ([]string, error) {
232	var keys []string
233
234	for k := range r.refs {
235		if strings.HasPrefix(k, refPrefix) {
236			keys = append(keys, k)
237		}
238	}
239
240	return keys, nil
241}
242
243func (r *mockRepoData) ListCommits(ref string) ([]Hash, error) {
244	var hashes []Hash
245
246	hash := r.refs[ref]
247
248	for {
249		commit, ok := r.commits[hash]
250
251		if !ok {
252			break
253		}
254
255		hashes = append([]Hash{hash}, hashes...)
256		hash = commit.parent
257	}
258
259	return hashes, nil
260}
261
262func (r *mockRepoData) ReadTree(hash Hash) ([]TreeEntry, error) {
263	var data string
264
265	data, ok := r.trees[hash]
266
267	if !ok {
268		// Git will understand a commit hash to reach a tree
269		commit, ok := r.commits[hash]
270
271		if !ok {
272			return nil, fmt.Errorf("unknown hash")
273		}
274
275		data, ok = r.trees[commit.treeHash]
276
277		if !ok {
278			return nil, fmt.Errorf("unknown hash")
279		}
280	}
281
282	return readTreeEntries(data)
283}
284
285func (r *mockRepoData) FindCommonAncestor(hash1 Hash, hash2 Hash) (Hash, error) {
286	ancestor1 := []Hash{hash1}
287
288	for hash1 != "" {
289		c, ok := r.commits[hash1]
290		if !ok {
291			return "", fmt.Errorf("unknown commit %v", hash1)
292		}
293		ancestor1 = append(ancestor1, c.parent)
294		hash1 = c.parent
295	}
296
297	for {
298		for _, ancestor := range ancestor1 {
299			if ancestor == hash2 {
300				return ancestor, nil
301			}
302		}
303
304		c, ok := r.commits[hash2]
305		if !ok {
306			return "", fmt.Errorf("unknown commit %v", hash1)
307		}
308
309		if c.parent == "" {
310			return "", fmt.Errorf("no ancestor found")
311		}
312
313		hash2 = c.parent
314	}
315}
316
317func (r *mockRepoData) GetTreeHash(commit Hash) (Hash, error) {
318	c, ok := r.commits[commit]
319	if !ok {
320		return "", fmt.Errorf("unknown commit")
321	}
322
323	return c.treeHash, nil
324}
325
326func (r *mockRepoData) AddRemote(name string, url string) error {
327	panic("implement me")
328}
329
330func (m mockRepoForTest) GetLocalRemote() string {
331	panic("implement me")
332}
333
334func (m mockRepoForTest) EraseFromDisk() error {
335	// nothing to do
336	return nil
337}
338
339type mockRepoClock struct {
340	clocks map[string]lamport.Clock
341}
342
343func NewMockRepoClock() *mockRepoClock {
344	return &mockRepoClock{
345		clocks: make(map[string]lamport.Clock),
346	}
347}
348
349func (r *mockRepoClock) GetOrCreateClock(name string) (lamport.Clock, error) {
350	if c, ok := r.clocks[name]; ok {
351		return c, nil
352	}
353
354	c := lamport.NewMemClock()
355	r.clocks[name] = c
356	return c, nil
357}