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