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