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}