1package cache
2
3import (
4 "strings"
5 "testing"
6 "time"
7
8 "github.com/stretchr/testify/assert"
9 "github.com/stretchr/testify/require"
10
11 "github.com/MichaelMure/git-bug/entities/bug"
12 "github.com/MichaelMure/git-bug/query"
13 "github.com/MichaelMure/git-bug/repository"
14)
15
16func TestCache(t *testing.T) {
17 repo := repository.CreateGoGitTestRepo(t, false)
18 cache, stderr := NewTestRepoCache(t, repo)
19
20 // Create, set and get user identity
21 iden1, err := cache.NewIdentity("René Descartes", "rene@descartes.fr")
22 require.NoError(t, err)
23 err = cache.SetUserIdentity(iden1)
24 require.NoError(t, err)
25 userIden, err := cache.GetUserIdentity()
26 require.NoError(t, err)
27 require.Equal(t, iden1.Id(), userIden.Id())
28
29 // it's possible to create two identical identities
30 iden2, err := cache.NewIdentity("René Descartes", "rene@descartes.fr")
31 require.NoError(t, err)
32
33 // Two identical identities yield a different id
34 require.NotEqual(t, iden1.Id(), iden2.Id())
35
36 // There is now two identities in the cache
37 require.Len(t, cache.AllIdentityIds(), 2)
38 require.Len(t, cache.identitiesExcerpts, 2)
39 require.Len(t, cache.identities, 2)
40
41 // Create a bug
42 bug1, _, err := cache.NewBug("title", "message")
43 require.NoError(t, err)
44
45 // It's possible to create two identical bugs
46 bug2, _, err := cache.NewBug("title", "message")
47 require.NoError(t, err)
48
49 // two identical bugs yield a different id
50 require.NotEqual(t, bug1.Id(), bug2.Id())
51
52 // There is now two bugs in the cache
53 require.Len(t, cache.AllBugsIds(), 2)
54 require.Len(t, cache.bugExcerpts, 2)
55 require.Len(t, cache.bugs, 2)
56
57 // Resolving
58 _, err = cache.ResolveIdentity(iden1.Id())
59 require.NoError(t, err)
60 _, err = cache.ResolveIdentityExcerpt(iden1.Id())
61 require.NoError(t, err)
62 _, err = cache.ResolveIdentityPrefix(iden1.Id().String()[:10])
63 require.NoError(t, err)
64
65 _, err = cache.ResolveBug(bug1.Id())
66 require.NoError(t, err)
67 _, err = cache.ResolveBugExcerpt(bug1.Id())
68 require.NoError(t, err)
69 _, err = cache.ResolveBugPrefix(bug1.Id().String()[:10])
70 require.NoError(t, err)
71
72 // Querying
73 q, err := query.Parse("status:open author:descartes sort:edit-asc")
74 require.NoError(t, err)
75 res, err := cache.QueryBugs(q)
76 require.NoError(t, err)
77 require.Len(t, res, 2)
78
79 // Close
80 require.NoError(t, cache.Close())
81 require.Empty(t, cache.bugs)
82 require.Empty(t, cache.bugExcerpts)
83 require.Empty(t, cache.identities)
84 require.Empty(t, cache.identitiesExcerpts)
85
86 // There should be no output to stderr
87 require.Empty(t, stderr.String())
88
89 // Reload, only excerpt are loaded, but as we need to load the identities used in the bugs
90 // to check the signatures, we also load the identity used above
91 cache, stderr = NewTestRepoCache(t, repo)
92 require.Empty(t, cache.bugs)
93 require.Len(t, cache.identities, 1)
94 require.Len(t, cache.bugExcerpts, 2)
95 require.Len(t, cache.identitiesExcerpts, 2)
96
97 // Resolving load from the disk
98 _, err = cache.ResolveIdentity(iden1.Id())
99 require.NoError(t, err)
100 _, err = cache.ResolveIdentityExcerpt(iden1.Id())
101 require.NoError(t, err)
102 _, err = cache.ResolveIdentityPrefix(iden1.Id().String()[:10])
103 require.NoError(t, err)
104
105 _, err = cache.ResolveBug(bug1.Id())
106 require.NoError(t, err)
107 _, err = cache.ResolveBugExcerpt(bug1.Id())
108 require.NoError(t, err)
109 _, err = cache.ResolveBugPrefix(bug1.Id().String()[:10])
110 require.NoError(t, err)
111
112 require.Empty(t, stderr.String())
113}
114
115func TestCachePushPull(t *testing.T) {
116 repoA, repoB, _ := repository.SetupGoGitReposAndRemote(t)
117 cacheA, stderrA := NewTestRepoCache(t, repoA)
118 cacheB, stderrB := NewTestRepoCache(t, repoB)
119
120 // Create, set and get user identity
121 reneA, err := cacheA.NewIdentity("René Descartes", "rene@descartes.fr")
122 require.NoError(t, err)
123 err = cacheA.SetUserIdentity(reneA)
124 require.NoError(t, err)
125 isaacB, err := cacheB.NewIdentity("Isaac Newton", "isaac@newton.uk")
126 require.NoError(t, err)
127 err = cacheB.SetUserIdentity(isaacB)
128 require.NoError(t, err)
129
130 // distribute the identity
131 _, err = cacheA.Push("origin")
132 require.NoError(t, err)
133 err = cacheB.Pull("origin")
134 require.NoError(t, err)
135
136 // Create a bug in A
137 _, _, err = cacheA.NewBug("bug1", "message")
138 require.NoError(t, err)
139
140 // A --> remote --> B
141 _, err = cacheA.Push("origin")
142 require.NoError(t, err)
143
144 err = cacheB.Pull("origin")
145 require.NoError(t, err)
146
147 require.Len(t, cacheB.AllBugsIds(), 1)
148
149 // retrieve and set identity
150 reneB, err := cacheB.ResolveIdentity(reneA.Id())
151 require.NoError(t, err)
152
153 err = cacheB.SetUserIdentity(reneB)
154 require.NoError(t, err)
155
156 // B --> remote --> A
157 _, _, err = cacheB.NewBug("bug2", "message")
158 require.NoError(t, err)
159
160 _, err = cacheB.Push("origin")
161 require.NoError(t, err)
162
163 err = cacheA.Pull("origin")
164 require.NoError(t, err)
165
166 require.Len(t, cacheA.AllBugsIds(), 2)
167
168 require.Empty(t, stderrA.String())
169 require.Empty(t, stderrB.String())
170}
171
172func TestRemove(t *testing.T) {
173 repo := repository.CreateGoGitTestRepo(t, false)
174 remoteA := repository.CreateGoGitTestRepo(t, true)
175 remoteB := repository.CreateGoGitTestRepo(t, true)
176
177 err := repo.AddRemote("remoteA", remoteA.GetLocalRemote())
178 require.NoError(t, err)
179
180 err = repo.AddRemote("remoteB", remoteB.GetLocalRemote())
181 require.NoError(t, err)
182
183 repoCache, stderr := NewTestRepoCache(t, repo)
184
185 rene, err := repoCache.NewIdentity("René Descartes", "rene@descartes.fr")
186 require.NoError(t, err)
187
188 err = repoCache.SetUserIdentity(rene)
189 require.NoError(t, err)
190
191 _, _, err = repoCache.NewBug("title", "message")
192 require.NoError(t, err)
193
194 // and one more for testing
195 b1, _, err := repoCache.NewBug("title", "message")
196 require.NoError(t, err)
197
198 _, err = repoCache.Push("remoteA")
199 require.NoError(t, err)
200
201 _, err = repoCache.Push("remoteB")
202 require.NoError(t, err)
203
204 _, err = repoCache.Fetch("remoteA")
205 require.NoError(t, err)
206
207 _, err = repoCache.Fetch("remoteB")
208 require.NoError(t, err)
209
210 err = repoCache.RemoveBug(b1.Id().String())
211 require.NoError(t, err)
212 assert.Equal(t, 1, len(repoCache.bugs))
213 assert.Equal(t, 1, len(repoCache.bugExcerpts))
214
215 _, err = repoCache.ResolveBug(b1.Id())
216 assert.Error(t, bug.ErrBugNotExist, err)
217
218 require.Empty(t, stderr.String())
219}
220
221func TestCacheEviction(t *testing.T) {
222 repo := repository.CreateGoGitTestRepo(t, false)
223 repoCache, stderr := NewTestRepoCache(t, repo)
224 repoCache.setCacheSize(2)
225
226 require.Equal(t, 2, repoCache.maxLoadedBugs)
227 require.Equal(t, 0, repoCache.loadedBugs.Len())
228 require.Equal(t, 0, len(repoCache.bugs))
229
230 // Generating some bugs
231 rene, err := repoCache.NewIdentity("René Descartes", "rene@descartes.fr")
232 require.NoError(t, err)
233 err = repoCache.SetUserIdentity(rene)
234 require.NoError(t, err)
235
236 bug1, _, err := repoCache.NewBug("title", "message")
237 require.NoError(t, err)
238
239 checkBugPresence(t, repoCache, bug1, true)
240 require.Equal(t, 1, repoCache.loadedBugs.Len())
241 require.Equal(t, 1, len(repoCache.bugs))
242
243 bug2, _, err := repoCache.NewBug("title", "message")
244 require.NoError(t, err)
245
246 checkBugPresence(t, repoCache, bug1, true)
247 checkBugPresence(t, repoCache, bug2, true)
248 require.Equal(t, 2, repoCache.loadedBugs.Len())
249 require.Equal(t, 2, len(repoCache.bugs))
250
251 // Number of bugs should not exceed max size of lruCache, oldest one should be evicted
252 bug3, _, err := repoCache.NewBug("title", "message")
253 require.NoError(t, err)
254
255 require.Equal(t, 2, repoCache.loadedBugs.Len())
256 require.Equal(t, 2, len(repoCache.bugs))
257 checkBugPresence(t, repoCache, bug1, false)
258 checkBugPresence(t, repoCache, bug2, true)
259 checkBugPresence(t, repoCache, bug3, true)
260
261 // Accessing bug should update position in lruCache and therefore it should not be evicted
262 repoCache.loadedBugs.Get(bug2.Id())
263 oldestId, _ := repoCache.loadedBugs.GetOldest()
264 require.Equal(t, bug3.Id(), oldestId)
265
266 checkBugPresence(t, repoCache, bug1, false)
267 checkBugPresence(t, repoCache, bug2, true)
268 checkBugPresence(t, repoCache, bug3, true)
269 require.Equal(t, 2, repoCache.loadedBugs.Len())
270 require.Equal(t, 2, len(repoCache.bugs))
271
272 require.Empty(t, stderr.String())
273}
274
275func checkBugPresence(t *testing.T, cache *RepoCache, bug *BugCache, presence bool) {
276 id := bug.Id()
277 require.Equal(t, presence, cache.loadedBugs.Contains(id))
278 b, ok := cache.bugs[id]
279 require.Equal(t, presence, ok)
280 if ok {
281 require.Equal(t, bug, b)
282 }
283}
284
285func TestLongDescription(t *testing.T) {
286 // See https://github.com/MichaelMure/git-bug/issues/606
287
288 text := strings.Repeat("x", 65536)
289
290 repo := repository.CreateGoGitTestRepo(t, false)
291
292 backend, stderr := NewTestRepoCache(t, repo)
293
294 i, err := backend.NewIdentity("René Descartes", "rene@descartes.fr")
295 require.NoError(t, err)
296
297 _, _, err = backend.NewBugRaw(i, time.Now().Unix(), text, text, nil, nil)
298 require.NoError(t, err)
299
300 require.Empty(t, stderr.String())
301}