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