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