feat(kvstore): list open merge requests and issues across repositories
- commit
- e17a0e59e503b085f11e3ef2ba82b39e646a580a
- author
- brice · · 2026-10-19 06:19 UTC
- parent
- 68460c1
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
6 files · +278 -0
M internal/adapters/kvstore/issues.go +44 -0
| @@ -185,6 +185,50 @@ func (i *Issues) ListOpen(ctx context.Context, repo domain.RepoName) ([]domain.I | |||
| 185 | 185 | return out, nil | |
| 186 | 186 | } | |
| 187 | 187 | ||
| 188 | + | // ListOpenAll returns the open issues of every repository through the isopen/ | |
| 189 | + | // index: the issues it names are read, no other. | |
| 190 | + | // | |
| 191 | + | //hexcheck:ignore suspicious-business-logic-in-adapter reads the open issues of all repositories through their index: a storage concern, no policy | |
| 192 | + | func (i *Issues) ListOpenAll(ctx context.Context) ([]domain.Issue, error) { | |
| 193 | + | type ref struct { | |
| 194 | + | repo domain.RepoName | |
| 195 | + | n int64 | |
| 196 | + | } | |
| 197 | + | var refs []ref | |
| 198 | + | for key := range i.db.Snapshot(ctx).Prefix(ctx, []byte(issueOpenPrefix)) { | |
| 199 | + | if err := ctx.Err(); err != nil { | |
| 200 | + | return nil, err | |
| 201 | + | } | |
| 202 | + | repoName, num, ok := strings.Cut(strings.TrimPrefix(string(key), issueOpenPrefix), "/") | |
| 203 | + | if !ok { | |
| 204 | + | return nil, fmt.Errorf("index key %q", key) | |
| 205 | + | } | |
| 206 | + | repo, err := domain.ParseRepoName(repoName) | |
| 207 | + | if err != nil { | |
| 208 | + | return nil, fmt.Errorf("index key %q: %w", key, err) | |
| 209 | + | } | |
| 210 | + | n, err := strconv.ParseInt(num, 10, 64) | |
| 211 | + | if err != nil { | |
| 212 | + | return nil, fmt.Errorf("index key %q: %w", key, err) | |
| 213 | + | } | |
| 214 | + | refs = append(refs, ref{repo, n}) | |
| 215 | + | } | |
| 216 | + | var out []domain.Issue | |
| 217 | + | for _, r := range refs { | |
| 218 | + | issue, err := i.Get(ctx, r.repo, r.n) | |
| 219 | + | if errors.Is(err, domain.ErrNotFound) { | |
| 220 | + | continue // closed or deleted since the snapshot of the index | |
| 221 | + | } | |
| 222 | + | if err != nil { | |
| 223 | + | return nil, err | |
| 224 | + | } | |
| 225 | + | if issue.IsOpen() { | |
| 226 | + | out = append(out, issue) | |
| 227 | + | } | |
| 228 | + | } | |
| 229 | + | return out, nil | |
| 230 | + | } | |
| 231 | + | ||
| 188 | 232 | // Update replaces the issue with a version precondition and moves its index key | |
| 189 | 233 | // in the same batch. The repository must exist (see guardRepoKey), so that a | |
| 190 | 234 | // concurrent delete of the repository leaves no index key behind. | |
| @@ -185,6 +185,50 @@ func (i *Issues) ListOpen(ctx context.Context, repo domain.RepoName) ([]domain.I | |||||
| 185 | return out, nil | 185 | return out, nil | ||
| 186 | } | 186 | } | ||
| 187 | 187 | ||||
| 188 | + | // ListOpenAll returns the open issues of every repository through the isopen/ | |||
| 189 | + | // index: the issues it names are read, no other. | |||
| 190 | + | // | |||
| 191 | + | //hexcheck:ignore suspicious-business-logic-in-adapter reads the open issues of all repositories through their index: a storage concern, no policy | |||
| 192 | + | func (i *Issues) ListOpenAll(ctx context.Context) ([]domain.Issue, error) { | |||
| 193 | + | type ref struct { | |||
| 194 | + | repo domain.RepoName | |||
| 195 | + | n int64 | |||
| 196 | + | } | |||
| 197 | + | var refs []ref | |||
| 198 | + | for key := range i.db.Snapshot(ctx).Prefix(ctx, []byte(issueOpenPrefix)) { | |||
| 199 | + | if err := ctx.Err(); err != nil { | |||
| 200 | + | return nil, err | |||
| 201 | + | } | |||
| 202 | + | repoName, num, ok := strings.Cut(strings.TrimPrefix(string(key), issueOpenPrefix), "/") | |||
| 203 | + | if !ok { | |||
| 204 | + | return nil, fmt.Errorf("index key %q", key) | |||
| 205 | + | } | |||
| 206 | + | repo, err := domain.ParseRepoName(repoName) | |||
| 207 | + | if err != nil { | |||
| 208 | + | return nil, fmt.Errorf("index key %q: %w", key, err) | |||
| 209 | + | } | |||
| 210 | + | n, err := strconv.ParseInt(num, 10, 64) | |||
| 211 | + | if err != nil { | |||
| 212 | + | return nil, fmt.Errorf("index key %q: %w", key, err) | |||
| 213 | + | } | |||
| 214 | + | refs = append(refs, ref{repo, n}) | |||
| 215 | + | } | |||
| 216 | + | var out []domain.Issue | |||
| 217 | + | for _, r := range refs { | |||
| 218 | + | issue, err := i.Get(ctx, r.repo, r.n) | |||
| 219 | + | if errors.Is(err, domain.ErrNotFound) { | |||
| 220 | + | continue // closed or deleted since the snapshot of the index | |||
| 221 | + | } | |||
| 222 | + | if err != nil { | |||
| 223 | + | return nil, err | |||
| 224 | + | } | |||
| 225 | + | if issue.IsOpen() { | |||
| 226 | + | out = append(out, issue) | |||
| 227 | + | } | |||
| 228 | + | } | |||
| 229 | + | return out, nil | |||
| 230 | + | } | |||
| 231 | + | ||||
| 188 | // Update replaces the issue with a version precondition and moves its index key | 232 | // Update replaces the issue with a version precondition and moves its index key | ||
| 189 | // in the same batch. The repository must exist (see guardRepoKey), so that a | 233 | // in the same batch. The repository must exist (see guardRepoKey), so that a | ||
| 190 | // concurrent delete of the repository leaves no index key behind. | 234 | // concurrent delete of the repository leaves no index key behind. | ||
M internal/adapters/kvstore/issues_test.go +57 -0
| @@ -4,6 +4,7 @@ package kvstore_test | |||
| 4 | 4 | ||
| 5 | 5 | import ( | |
| 6 | 6 | "context" | |
| 7 | + | "strconv" | |
| 7 | 8 | "sync" | |
| 8 | 9 | "testing" | |
| 9 | 10 | ||
| @@ -368,3 +369,59 @@ func TestDeleteVersusReopenLeavesNoIssueKeys(t *testing.T) { | |||
| 368 | 369 | assert.ErrorIs(t, is.Update(ctx, closed), domain.ErrNotFound) | |
| 369 | 370 | } | |
| 370 | 371 | } | |
| 372 | + | ||
| 373 | + | func TestListOpenAllSpansRepositoriesThroughTheIndex(t *testing.T) { | |
| 374 | + | ctx := context.Background() | |
| 375 | + | s, mrs, n := repoWithMRs(t) | |
| 376 | + | other := name(t, "other") | |
| 377 | + | require.NoError(t, s.Create(ctx, domain.Repo{Name: other, CreatedAt: created, Visibility: domain.VisibilityPublic})) | |
| 378 | + | is := s.Issues() | |
| 379 | + | ||
| 380 | + | i1, err := is.Create(ctx, newIssue(t, n, "one")) | |
| 381 | + | require.NoError(t, err) | |
| 382 | + | i2, err := is.Create(ctx, newIssue(t, other, "two")) | |
| 383 | + | require.NoError(t, err) | |
| 384 | + | i3, err := is.Create(ctx, newIssue(t, other, "three")) | |
| 385 | + | require.NoError(t, err) | |
| 386 | + | i3.State = domain.IssueStateClosed | |
| 387 | + | require.NoError(t, is.Update(ctx, i3)) | |
| 388 | + | _, err = mrs.Create(ctx, newMR(t, n, "mr1")) | |
| 389 | + | require.NoError(t, err) | |
| 390 | + | m2, err := mrs.Create(ctx, newMR(t, other, "mr2")) | |
| 391 | + | require.NoError(t, err) | |
| 392 | + | m3, err := mrs.Create(ctx, newMR(t, other, "mr3")) | |
| 393 | + | require.NoError(t, err) | |
| 394 | + | m3.State = domain.MRStateClosed | |
| 395 | + | require.NoError(t, mrs.Update(ctx, m3)) | |
| 396 | + | ||
| 397 | + | issues, err := is.ListOpenAll(ctx) | |
| 398 | + | require.NoError(t, err) | |
| 399 | + | var got []string | |
| 400 | + | for _, i := range issues { | |
| 401 | + | got = append(got, i.Repo.String()+"#"+strconv.FormatInt(i.Number, 10)) | |
| 402 | + | } | |
| 403 | + | assert.ElementsMatch(t, []string{ | |
| 404 | + | i1.Repo.String() + "#" + strconv.FormatInt(i1.Number, 10), | |
| 405 | + | i2.Repo.String() + "#" + strconv.FormatInt(i2.Number, 10), | |
| 406 | + | }, got) | |
| 407 | + | ||
| 408 | + | open, err := mrs.ListOpenAll(ctx) | |
| 409 | + | require.NoError(t, err) | |
| 410 | + | got = nil | |
| 411 | + | for _, m := range open { | |
| 412 | + | got = append(got, m.Repo.String()+"#"+strconv.FormatInt(m.Number, 10)) | |
| 413 | + | } | |
| 414 | + | assert.ElementsMatch(t, []string{"proj#2", m2.Repo.String() + "#" + strconv.FormatInt(m2.Number, 10)}, got) | |
| 415 | + | assert.Len(t, open, 2) | |
| 416 | + | } | |
| 417 | + | ||
| 418 | + | func TestListOpenAllIsEmptyWithoutOpenItems(t *testing.T) { | |
| 419 | + | ctx := context.Background() | |
| 420 | + | s, mrs, _ := repoWithMRs(t) | |
| 421 | + | issues, err := s.Issues().ListOpenAll(ctx) | |
| 422 | + | require.NoError(t, err) | |
| 423 | + | assert.Empty(t, issues) | |
| 424 | + | open, err := mrs.ListOpenAll(ctx) | |
| 425 | + | require.NoError(t, err) | |
| 426 | + | assert.Empty(t, open) | |
| 427 | + | } | |
| @@ -4,6 +4,7 @@ package kvstore_test | |||||
| 4 | 4 | ||||
| 5 | import ( | 5 | import ( | ||
| 6 | "context" | 6 | "context" | ||
| 7 | + | "strconv" | |||
| 7 | "sync" | 8 | "sync" | ||
| 8 | "testing" | 9 | "testing" | ||
| 9 | 10 | ||||
| @@ -368,3 +369,59 @@ func TestDeleteVersusReopenLeavesNoIssueKeys(t *testing.T) { | |||||
| 368 | assert.ErrorIs(t, is.Update(ctx, closed), domain.ErrNotFound) | 369 | assert.ErrorIs(t, is.Update(ctx, closed), domain.ErrNotFound) | ||
| 369 | } | 370 | } | ||
| 370 | } | 371 | } | ||
| 372 | + | ||||
| 373 | + | func TestListOpenAllSpansRepositoriesThroughTheIndex(t *testing.T) { | |||
| 374 | + | ctx := context.Background() | |||
| 375 | + | s, mrs, n := repoWithMRs(t) | |||
| 376 | + | other := name(t, "other") | |||
| 377 | + | require.NoError(t, s.Create(ctx, domain.Repo{Name: other, CreatedAt: created, Visibility: domain.VisibilityPublic})) | |||
| 378 | + | is := s.Issues() | |||
| 379 | + | ||||
| 380 | + | i1, err := is.Create(ctx, newIssue(t, n, "one")) | |||
| 381 | + | require.NoError(t, err) | |||
| 382 | + | i2, err := is.Create(ctx, newIssue(t, other, "two")) | |||
| 383 | + | require.NoError(t, err) | |||
| 384 | + | i3, err := is.Create(ctx, newIssue(t, other, "three")) | |||
| 385 | + | require.NoError(t, err) | |||
| 386 | + | i3.State = domain.IssueStateClosed | |||
| 387 | + | require.NoError(t, is.Update(ctx, i3)) | |||
| 388 | + | _, err = mrs.Create(ctx, newMR(t, n, "mr1")) | |||
| 389 | + | require.NoError(t, err) | |||
| 390 | + | m2, err := mrs.Create(ctx, newMR(t, other, "mr2")) | |||
| 391 | + | require.NoError(t, err) | |||
| 392 | + | m3, err := mrs.Create(ctx, newMR(t, other, "mr3")) | |||
| 393 | + | require.NoError(t, err) | |||
| 394 | + | m3.State = domain.MRStateClosed | |||
| 395 | + | require.NoError(t, mrs.Update(ctx, m3)) | |||
| 396 | + | ||||
| 397 | + | issues, err := is.ListOpenAll(ctx) | |||
| 398 | + | require.NoError(t, err) | |||
| 399 | + | var got []string | |||
| 400 | + | for _, i := range issues { | |||
| 401 | + | got = append(got, i.Repo.String()+"#"+strconv.FormatInt(i.Number, 10)) | |||
| 402 | + | } | |||
| 403 | + | assert.ElementsMatch(t, []string{ | |||
| 404 | + | i1.Repo.String() + "#" + strconv.FormatInt(i1.Number, 10), | |||
| 405 | + | i2.Repo.String() + "#" + strconv.FormatInt(i2.Number, 10), | |||
| 406 | + | }, got) | |||
| 407 | + | ||||
| 408 | + | open, err := mrs.ListOpenAll(ctx) | |||
| 409 | + | require.NoError(t, err) | |||
| 410 | + | got = nil | |||
| 411 | + | for _, m := range open { | |||
| 412 | + | got = append(got, m.Repo.String()+"#"+strconv.FormatInt(m.Number, 10)) | |||
| 413 | + | } | |||
| 414 | + | assert.ElementsMatch(t, []string{"proj#2", m2.Repo.String() + "#" + strconv.FormatInt(m2.Number, 10)}, got) | |||
| 415 | + | assert.Len(t, open, 2) | |||
| 416 | + | } | |||
| 417 | + | ||||
| 418 | + | func TestListOpenAllIsEmptyWithoutOpenItems(t *testing.T) { | |||
| 419 | + | ctx := context.Background() | |||
| 420 | + | s, mrs, _ := repoWithMRs(t) | |||
| 421 | + | issues, err := s.Issues().ListOpenAll(ctx) | |||
| 422 | + | require.NoError(t, err) | |||
| 423 | + | assert.Empty(t, issues) | |||
| 424 | + | open, err := mrs.ListOpenAll(ctx) | |||
| 425 | + | require.NoError(t, err) | |||
| 426 | + | assert.Empty(t, open) | |||
| 427 | + | } | |||
M internal/adapters/kvstore/mergerequests.go +46 -0
| @@ -380,6 +380,52 @@ func (m *MergeRequests) ListOpen(ctx context.Context, repo domain.RepoName) ([]d | |||
| 380 | 380 | return out, nil | |
| 381 | 381 | } | |
| 382 | 382 | ||
| 383 | + | // ListOpenAll returns the open merge requests of every repository, repository | |
| 384 | + | // by repository and highest number first, through the mropen/ index: the | |
| 385 | + | // requests it names are read, no other. | |
| 386 | + | // | |
| 387 | + | //hexcheck:ignore suspicious-business-logic-in-adapter reads the open merge requests of all repositories through their index: a storage concern, no policy | |
| 388 | + | func (m *MergeRequests) ListOpenAll(ctx context.Context) ([]domain.MergeRequest, error) { | |
| 389 | + | type ref struct { | |
| 390 | + | repo domain.RepoName | |
| 391 | + | n int64 | |
| 392 | + | } | |
| 393 | + | var refs []ref | |
| 394 | + | for key := range m.db.Snapshot(ctx).Prefix(ctx, []byte(mrOpenPrefix)) { | |
| 395 | + | if err := ctx.Err(); err != nil { | |
| 396 | + | return nil, err | |
| 397 | + | } | |
| 398 | + | rest := strings.TrimPrefix(string(key), mrOpenPrefix) | |
| 399 | + | repoName, _, ok := strings.Cut(rest, "/") | |
| 400 | + | if !ok { | |
| 401 | + | return nil, fmt.Errorf("index key %q", key) | |
| 402 | + | } | |
| 403 | + | repo, err := domain.ParseRepoName(repoName) | |
| 404 | + | if err != nil { | |
| 405 | + | return nil, fmt.Errorf("index key %q: %w", key, err) | |
| 406 | + | } | |
| 407 | + | n, err := strconv.ParseInt(rest[strings.LastIndexByte(rest, '/')+1:], 10, 64) | |
| 408 | + | if err != nil { | |
| 409 | + | return nil, fmt.Errorf("index key %q: %w", key, err) | |
| 410 | + | } | |
| 411 | + | refs = append(refs, ref{repo, n}) | |
| 412 | + | } | |
| 413 | + | var out []domain.MergeRequest | |
| 414 | + | for _, r := range refs { | |
| 415 | + | mr, err := m.Get(ctx, r.repo, r.n) | |
| 416 | + | if errors.Is(err, domain.ErrNotFound) { | |
| 417 | + | continue // closed or deleted since the snapshot of the index | |
| 418 | + | } | |
| 419 | + | if err != nil { | |
| 420 | + | return nil, err | |
| 421 | + | } | |
| 422 | + | if mr.IsOpen() { | |
| 423 | + | out = append(out, mr) | |
| 424 | + | } | |
| 425 | + | } | |
| 426 | + | return out, nil | |
| 427 | + | } | |
| 428 | + | ||
| 383 | 429 | // CountOpen counts the open merge requests of the repository from the keys of | |
| 384 | 430 | // the index: no request is decoded. | |
| 385 | 431 | func (m *MergeRequests) CountOpen(ctx context.Context, repo domain.RepoName) (int, error) { | |
| @@ -380,6 +380,52 @@ func (m *MergeRequests) ListOpen(ctx context.Context, repo domain.RepoName) ([]d | |||||
| 380 | return out, nil | 380 | return out, nil | ||
| 381 | } | 381 | } | ||
| 382 | 382 | ||||
| 383 | + | // ListOpenAll returns the open merge requests of every repository, repository | |||
| 384 | + | // by repository and highest number first, through the mropen/ index: the | |||
| 385 | + | // requests it names are read, no other. | |||
| 386 | + | // | |||
| 387 | + | //hexcheck:ignore suspicious-business-logic-in-adapter reads the open merge requests of all repositories through their index: a storage concern, no policy | |||
| 388 | + | func (m *MergeRequests) ListOpenAll(ctx context.Context) ([]domain.MergeRequest, error) { | |||
| 389 | + | type ref struct { | |||
| 390 | + | repo domain.RepoName | |||
| 391 | + | n int64 | |||
| 392 | + | } | |||
| 393 | + | var refs []ref | |||
| 394 | + | for key := range m.db.Snapshot(ctx).Prefix(ctx, []byte(mrOpenPrefix)) { | |||
| 395 | + | if err := ctx.Err(); err != nil { | |||
| 396 | + | return nil, err | |||
| 397 | + | } | |||
| 398 | + | rest := strings.TrimPrefix(string(key), mrOpenPrefix) | |||
| 399 | + | repoName, _, ok := strings.Cut(rest, "/") | |||
| 400 | + | if !ok { | |||
| 401 | + | return nil, fmt.Errorf("index key %q", key) | |||
| 402 | + | } | |||
| 403 | + | repo, err := domain.ParseRepoName(repoName) | |||
| 404 | + | if err != nil { | |||
| 405 | + | return nil, fmt.Errorf("index key %q: %w", key, err) | |||
| 406 | + | } | |||
| 407 | + | n, err := strconv.ParseInt(rest[strings.LastIndexByte(rest, '/')+1:], 10, 64) | |||
| 408 | + | if err != nil { | |||
| 409 | + | return nil, fmt.Errorf("index key %q: %w", key, err) | |||
| 410 | + | } | |||
| 411 | + | refs = append(refs, ref{repo, n}) | |||
| 412 | + | } | |||
| 413 | + | var out []domain.MergeRequest | |||
| 414 | + | for _, r := range refs { | |||
| 415 | + | mr, err := m.Get(ctx, r.repo, r.n) | |||
| 416 | + | if errors.Is(err, domain.ErrNotFound) { | |||
| 417 | + | continue // closed or deleted since the snapshot of the index | |||
| 418 | + | } | |||
| 419 | + | if err != nil { | |||
| 420 | + | return nil, err | |||
| 421 | + | } | |||
| 422 | + | if mr.IsOpen() { | |||
| 423 | + | out = append(out, mr) | |||
| 424 | + | } | |||
| 425 | + | } | |||
| 426 | + | return out, nil | |||
| 427 | + | } | |||
| 428 | + | ||||
| 383 | // CountOpen counts the open merge requests of the repository from the keys of | 429 | // CountOpen counts the open merge requests of the repository from the keys of | ||
| 384 | // the index: no request is decoded. | 430 | // the index: no request is decoded. | ||
| 385 | func (m *MergeRequests) CountOpen(ctx context.Context, repo domain.RepoName) (int, error) { | 431 | func (m *MergeRequests) CountOpen(ctx context.Context, repo domain.RepoName) (int, error) { | ||
M internal/ports/mocks/IssueStore.go +62 -0
| @@ -400,6 +400,68 @@ func (_c *IssueStore_ListOpen_Call) RunAndReturn(run func(ctx context.Context, r | |||
| 400 | 400 | return _c | |
| 401 | 401 | } | |
| 402 | 402 | ||
| 403 | + | // ListOpenAll provides a mock function for the type IssueStore | |
| 404 | + | func (_mock *IssueStore) ListOpenAll(ctx context.Context) ([]domain.Issue, error) { | |
| 405 | + | ret := _mock.Called(ctx) | |
| 406 | + | ||
| 407 | + | if len(ret) == 0 { | |
| 408 | + | panic("no return value specified for ListOpenAll") | |
| 409 | + | } | |
| 410 | + | ||
| 411 | + | var r0 []domain.Issue | |
| 412 | + | var r1 error | |
| 413 | + | if returnFunc, ok := ret.Get(0).(func(context.Context) ([]domain.Issue, error)); ok { | |
| 414 | + | return returnFunc(ctx) | |
| 415 | + | } | |
| 416 | + | if returnFunc, ok := ret.Get(0).(func(context.Context) []domain.Issue); ok { | |
| 417 | + | r0 = returnFunc(ctx) | |
| 418 | + | } else { | |
| 419 | + | if ret.Get(0) != nil { | |
| 420 | + | r0 = ret.Get(0).([]domain.Issue) | |
| 421 | + | } | |
| 422 | + | } | |
| 423 | + | if returnFunc, ok := ret.Get(1).(func(context.Context) error); ok { | |
| 424 | + | r1 = returnFunc(ctx) | |
| 425 | + | } else { | |
| 426 | + | r1 = ret.Error(1) | |
| 427 | + | } | |
| 428 | + | return r0, r1 | |
| 429 | + | } | |
| 430 | + | ||
| 431 | + | // IssueStore_ListOpenAll_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListOpenAll' | |
| 432 | + | type IssueStore_ListOpenAll_Call struct { | |
| 433 | + | *mock.Call | |
| 434 | + | } | |
| 435 | + | ||
| 436 | + | // ListOpenAll is a helper method to define mock.On call | |
| 437 | + | // - ctx context.Context | |
| 438 | + | func (_e *IssueStore_Expecter) ListOpenAll(ctx any) *IssueStore_ListOpenAll_Call { | |
| 439 | + | return &IssueStore_ListOpenAll_Call{Call: _e.mock.On("ListOpenAll", ctx)} | |
| 440 | + | } | |
| 441 | + | ||
| 442 | + | func (_c *IssueStore_ListOpenAll_Call) Run(run func(ctx context.Context)) *IssueStore_ListOpenAll_Call { | |
| 443 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 444 | + | var arg0 context.Context | |
| 445 | + | if args[0] != nil { | |
| 446 | + | arg0 = args[0].(context.Context) | |
| 447 | + | } | |
| 448 | + | run( | |
| 449 | + | arg0, | |
| 450 | + | ) | |
| 451 | + | }) | |
| 452 | + | return _c | |
| 453 | + | } | |
| 454 | + | ||
| 455 | + | func (_c *IssueStore_ListOpenAll_Call) Return(issues []domain.Issue, err error) *IssueStore_ListOpenAll_Call { | |
| 456 | + | _c.Call.Return(issues, err) | |
| 457 | + | return _c | |
| 458 | + | } | |
| 459 | + | ||
| 460 | + | func (_c *IssueStore_ListOpenAll_Call) RunAndReturn(run func(ctx context.Context) ([]domain.Issue, error)) *IssueStore_ListOpenAll_Call { | |
| 461 | + | _c.Call.Return(run) | |
| 462 | + | return _c | |
| 463 | + | } | |
| 464 | + | ||
| 403 | 465 | // SaveComment provides a mock function for the type IssueStore | |
| 404 | 466 | func (_mock *IssueStore) SaveComment(ctx context.Context, issue domain.Issue, c domain.IssueComment) error { | |
| 405 | 467 | ret := _mock.Called(ctx, issue, c) | |
| @@ -400,6 +400,68 @@ func (_c *IssueStore_ListOpen_Call) RunAndReturn(run func(ctx context.Context, r | |||||
| 400 | return _c | 400 | return _c | ||
| 401 | } | 401 | } | ||
| 402 | 402 | ||||
| 403 | + | // ListOpenAll provides a mock function for the type IssueStore | |||
| 404 | + | func (_mock *IssueStore) ListOpenAll(ctx context.Context) ([]domain.Issue, error) { | |||
| 405 | + | ret := _mock.Called(ctx) | |||
| 406 | + | ||||
| 407 | + | if len(ret) == 0 { | |||
| 408 | + | panic("no return value specified for ListOpenAll") | |||
| 409 | + | } | |||
| 410 | + | ||||
| 411 | + | var r0 []domain.Issue | |||
| 412 | + | var r1 error | |||
| 413 | + | if returnFunc, ok := ret.Get(0).(func(context.Context) ([]domain.Issue, error)); ok { | |||
| 414 | + | return returnFunc(ctx) | |||
| 415 | + | } | |||
| 416 | + | if returnFunc, ok := ret.Get(0).(func(context.Context) []domain.Issue); ok { | |||
| 417 | + | r0 = returnFunc(ctx) | |||
| 418 | + | } else { | |||
| 419 | + | if ret.Get(0) != nil { | |||
| 420 | + | r0 = ret.Get(0).([]domain.Issue) | |||
| 421 | + | } | |||
| 422 | + | } | |||
| 423 | + | if returnFunc, ok := ret.Get(1).(func(context.Context) error); ok { | |||
| 424 | + | r1 = returnFunc(ctx) | |||
| 425 | + | } else { | |||
| 426 | + | r1 = ret.Error(1) | |||
| 427 | + | } | |||
| 428 | + | return r0, r1 | |||
| 429 | + | } | |||
| 430 | + | ||||
| 431 | + | // IssueStore_ListOpenAll_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListOpenAll' | |||
| 432 | + | type IssueStore_ListOpenAll_Call struct { | |||
| 433 | + | *mock.Call | |||
| 434 | + | } | |||
| 435 | + | ||||
| 436 | + | // ListOpenAll is a helper method to define mock.On call | |||
| 437 | + | // - ctx context.Context | |||
| 438 | + | func (_e *IssueStore_Expecter) ListOpenAll(ctx any) *IssueStore_ListOpenAll_Call { | |||
| 439 | + | return &IssueStore_ListOpenAll_Call{Call: _e.mock.On("ListOpenAll", ctx)} | |||
| 440 | + | } | |||
| 441 | + | ||||
| 442 | + | func (_c *IssueStore_ListOpenAll_Call) Run(run func(ctx context.Context)) *IssueStore_ListOpenAll_Call { | |||
| 443 | + | _c.Call.Run(func(args mock.Arguments) { | |||
| 444 | + | var arg0 context.Context | |||
| 445 | + | if args[0] != nil { | |||
| 446 | + | arg0 = args[0].(context.Context) | |||
| 447 | + | } | |||
| 448 | + | run( | |||
| 449 | + | arg0, | |||
| 450 | + | ) | |||
| 451 | + | }) | |||
| 452 | + | return _c | |||
| 453 | + | } | |||
| 454 | + | ||||
| 455 | + | func (_c *IssueStore_ListOpenAll_Call) Return(issues []domain.Issue, err error) *IssueStore_ListOpenAll_Call { | |||
| 456 | + | _c.Call.Return(issues, err) | |||
| 457 | + | return _c | |||
| 458 | + | } | |||
| 459 | + | ||||
| 460 | + | func (_c *IssueStore_ListOpenAll_Call) RunAndReturn(run func(ctx context.Context) ([]domain.Issue, error)) *IssueStore_ListOpenAll_Call { | |||
| 461 | + | _c.Call.Return(run) | |||
| 462 | + | return _c | |||
| 463 | + | } | |||
| 464 | + | ||||
| 403 | // SaveComment provides a mock function for the type IssueStore | 465 | // SaveComment provides a mock function for the type IssueStore | ||
| 404 | func (_mock *IssueStore) SaveComment(ctx context.Context, issue domain.Issue, c domain.IssueComment) error { | 466 | func (_mock *IssueStore) SaveComment(ctx context.Context, issue domain.Issue, c domain.IssueComment) error { | ||
| 405 | ret := _mock.Called(ctx, issue, c) | 467 | ret := _mock.Called(ctx, issue, c) | ||
M internal/ports/mocks/MergeRequestStore.go +62 -0
| @@ -611,6 +611,68 @@ func (_c *MergeRequestStore_ListOpen_Call) RunAndReturn(run func(ctx context.Con | |||
| 611 | 611 | return _c | |
| 612 | 612 | } | |
| 613 | 613 | ||
| 614 | + | // ListOpenAll provides a mock function for the type MergeRequestStore | |
| 615 | + | func (_mock *MergeRequestStore) ListOpenAll(ctx context.Context) ([]domain.MergeRequest, error) { | |
| 616 | + | ret := _mock.Called(ctx) | |
| 617 | + | ||
| 618 | + | if len(ret) == 0 { | |
| 619 | + | panic("no return value specified for ListOpenAll") | |
| 620 | + | } | |
| 621 | + | ||
| 622 | + | var r0 []domain.MergeRequest | |
| 623 | + | var r1 error | |
| 624 | + | if returnFunc, ok := ret.Get(0).(func(context.Context) ([]domain.MergeRequest, error)); ok { | |
| 625 | + | return returnFunc(ctx) | |
| 626 | + | } | |
| 627 | + | if returnFunc, ok := ret.Get(0).(func(context.Context) []domain.MergeRequest); ok { | |
| 628 | + | r0 = returnFunc(ctx) | |
| 629 | + | } else { | |
| 630 | + | if ret.Get(0) != nil { | |
| 631 | + | r0 = ret.Get(0).([]domain.MergeRequest) | |
| 632 | + | } | |
| 633 | + | } | |
| 634 | + | if returnFunc, ok := ret.Get(1).(func(context.Context) error); ok { | |
| 635 | + | r1 = returnFunc(ctx) | |
| 636 | + | } else { | |
| 637 | + | r1 = ret.Error(1) | |
| 638 | + | } | |
| 639 | + | return r0, r1 | |
| 640 | + | } | |
| 641 | + | ||
| 642 | + | // MergeRequestStore_ListOpenAll_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListOpenAll' | |
| 643 | + | type MergeRequestStore_ListOpenAll_Call struct { | |
| 644 | + | *mock.Call | |
| 645 | + | } | |
| 646 | + | ||
| 647 | + | // ListOpenAll is a helper method to define mock.On call | |
| 648 | + | // - ctx context.Context | |
| 649 | + | func (_e *MergeRequestStore_Expecter) ListOpenAll(ctx any) *MergeRequestStore_ListOpenAll_Call { | |
| 650 | + | return &MergeRequestStore_ListOpenAll_Call{Call: _e.mock.On("ListOpenAll", ctx)} | |
| 651 | + | } | |
| 652 | + | ||
| 653 | + | func (_c *MergeRequestStore_ListOpenAll_Call) Run(run func(ctx context.Context)) *MergeRequestStore_ListOpenAll_Call { | |
| 654 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 655 | + | var arg0 context.Context | |
| 656 | + | if args[0] != nil { | |
| 657 | + | arg0 = args[0].(context.Context) | |
| 658 | + | } | |
| 659 | + | run( | |
| 660 | + | arg0, | |
| 661 | + | ) | |
| 662 | + | }) | |
| 663 | + | return _c | |
| 664 | + | } | |
| 665 | + | ||
| 666 | + | func (_c *MergeRequestStore_ListOpenAll_Call) Return(mergeRequests []domain.MergeRequest, err error) *MergeRequestStore_ListOpenAll_Call { | |
| 667 | + | _c.Call.Return(mergeRequests, err) | |
| 668 | + | return _c | |
| 669 | + | } | |
| 670 | + | ||
| 671 | + | func (_c *MergeRequestStore_ListOpenAll_Call) RunAndReturn(run func(ctx context.Context) ([]domain.MergeRequest, error)) *MergeRequestStore_ListOpenAll_Call { | |
| 672 | + | _c.Call.Return(run) | |
| 673 | + | return _c | |
| 674 | + | } | |
| 675 | + | ||
| 614 | 676 | // OpenBySource provides a mock function for the type MergeRequestStore | |
| 615 | 677 | func (_mock *MergeRequestStore) OpenBySource(ctx context.Context, repo domain.RepoName, branches []string, limit int) ([]int64, bool, error) { | |
| 616 | 678 | ret := _mock.Called(ctx, repo, branches, limit) | |
| @@ -611,6 +611,68 @@ func (_c *MergeRequestStore_ListOpen_Call) RunAndReturn(run func(ctx context.Con | |||||
| 611 | return _c | 611 | return _c | ||
| 612 | } | 612 | } | ||
| 613 | 613 | ||||
| 614 | + | // ListOpenAll provides a mock function for the type MergeRequestStore | |||
| 615 | + | func (_mock *MergeRequestStore) ListOpenAll(ctx context.Context) ([]domain.MergeRequest, error) { | |||
| 616 | + | ret := _mock.Called(ctx) | |||
| 617 | + | ||||
| 618 | + | if len(ret) == 0 { | |||
| 619 | + | panic("no return value specified for ListOpenAll") | |||
| 620 | + | } | |||
| 621 | + | ||||
| 622 | + | var r0 []domain.MergeRequest | |||
| 623 | + | var r1 error | |||
| 624 | + | if returnFunc, ok := ret.Get(0).(func(context.Context) ([]domain.MergeRequest, error)); ok { | |||
| 625 | + | return returnFunc(ctx) | |||
| 626 | + | } | |||
| 627 | + | if returnFunc, ok := ret.Get(0).(func(context.Context) []domain.MergeRequest); ok { | |||
| 628 | + | r0 = returnFunc(ctx) | |||
| 629 | + | } else { | |||
| 630 | + | if ret.Get(0) != nil { | |||
| 631 | + | r0 = ret.Get(0).([]domain.MergeRequest) | |||
| 632 | + | } | |||
| 633 | + | } | |||
| 634 | + | if returnFunc, ok := ret.Get(1).(func(context.Context) error); ok { | |||
| 635 | + | r1 = returnFunc(ctx) | |||
| 636 | + | } else { | |||
| 637 | + | r1 = ret.Error(1) | |||
| 638 | + | } | |||
| 639 | + | return r0, r1 | |||
| 640 | + | } | |||
| 641 | + | ||||
| 642 | + | // MergeRequestStore_ListOpenAll_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListOpenAll' | |||
| 643 | + | type MergeRequestStore_ListOpenAll_Call struct { | |||
| 644 | + | *mock.Call | |||
| 645 | + | } | |||
| 646 | + | ||||
| 647 | + | // ListOpenAll is a helper method to define mock.On call | |||
| 648 | + | // - ctx context.Context | |||
| 649 | + | func (_e *MergeRequestStore_Expecter) ListOpenAll(ctx any) *MergeRequestStore_ListOpenAll_Call { | |||
| 650 | + | return &MergeRequestStore_ListOpenAll_Call{Call: _e.mock.On("ListOpenAll", ctx)} | |||
| 651 | + | } | |||
| 652 | + | ||||
| 653 | + | func (_c *MergeRequestStore_ListOpenAll_Call) Run(run func(ctx context.Context)) *MergeRequestStore_ListOpenAll_Call { | |||
| 654 | + | _c.Call.Run(func(args mock.Arguments) { | |||
| 655 | + | var arg0 context.Context | |||
| 656 | + | if args[0] != nil { | |||
| 657 | + | arg0 = args[0].(context.Context) | |||
| 658 | + | } | |||
| 659 | + | run( | |||
| 660 | + | arg0, | |||
| 661 | + | ) | |||
| 662 | + | }) | |||
| 663 | + | return _c | |||
| 664 | + | } | |||
| 665 | + | ||||
| 666 | + | func (_c *MergeRequestStore_ListOpenAll_Call) Return(mergeRequests []domain.MergeRequest, err error) *MergeRequestStore_ListOpenAll_Call { | |||
| 667 | + | _c.Call.Return(mergeRequests, err) | |||
| 668 | + | return _c | |||
| 669 | + | } | |||
| 670 | + | ||||
| 671 | + | func (_c *MergeRequestStore_ListOpenAll_Call) RunAndReturn(run func(ctx context.Context) ([]domain.MergeRequest, error)) *MergeRequestStore_ListOpenAll_Call { | |||
| 672 | + | _c.Call.Return(run) | |||
| 673 | + | return _c | |||
| 674 | + | } | |||
| 675 | + | ||||
| 614 | // OpenBySource provides a mock function for the type MergeRequestStore | 676 | // OpenBySource provides a mock function for the type MergeRequestStore | ||
| 615 | func (_mock *MergeRequestStore) OpenBySource(ctx context.Context, repo domain.RepoName, branches []string, limit int) ([]int64, bool, error) { | 677 | func (_mock *MergeRequestStore) OpenBySource(ctx context.Context, repo domain.RepoName, branches []string, limit int) ([]int64, bool, error) { | ||
| 616 | ret := _mock.Called(ctx, repo, branches, limit) | 678 | ret := _mock.Called(ctx, repo, branches, limit) | ||
M internal/ports/ports.go +7 -0
| @@ -261,6 +261,9 @@ type IssueStore interface { | |||
| 261 | 261 | // ListOpen returns the open issues of the repository, highest number | |
| 262 | 262 | // first, reading only the open ones (through an index). | |
| 263 | 263 | ListOpen(ctx context.Context, repo domain.RepoName) ([]domain.Issue, error) | |
| 264 | + | // ListOpenAll returns the open issues of every repository, reading only | |
| 265 | + | // the open ones (through the same index). The order is unspecified. | |
| 266 | + | ListOpenAll(ctx context.Context) ([]domain.Issue, error) | |
| 264 | 267 | // Update replaces the stored issue and keeps the index of the open issues | |
| 265 | 268 | // in step. It returns domain.ErrNotFound if there is none and | |
| 266 | 269 | // domain.ErrConflict if another write changed it since issue was read. | |
| @@ -294,6 +297,10 @@ type MergeRequestStore interface { | |||
| 294 | 297 | // ListOpen returns the open merge requests of the repository, highest | |
| 295 | 298 | // number first, reading only the open ones (through an index). | |
| 296 | 299 | ListOpen(ctx context.Context, repo domain.RepoName) ([]domain.MergeRequest, error) | |
| 300 | + | // ListOpenAll returns the open merge requests of every repository, | |
| 301 | + | // reading only the open ones (through the same index). The order is | |
| 302 | + | // unspecified. | |
| 303 | + | ListOpenAll(ctx context.Context) ([]domain.MergeRequest, error) | |
| 297 | 304 | // CountOpen returns the number of open merge requests of the repository | |
| 298 | 305 | // from the index of keys: no request is read. | |
| 299 | 306 | CountOpen(ctx context.Context, repo domain.RepoName) (int, error) | |
| @@ -261,6 +261,9 @@ type IssueStore interface { | |||||
| 261 | // ListOpen returns the open issues of the repository, highest number | 261 | // ListOpen returns the open issues of the repository, highest number | ||
| 262 | // first, reading only the open ones (through an index). | 262 | // first, reading only the open ones (through an index). | ||
| 263 | ListOpen(ctx context.Context, repo domain.RepoName) ([]domain.Issue, error) | 263 | ListOpen(ctx context.Context, repo domain.RepoName) ([]domain.Issue, error) | ||
| 264 | + | // ListOpenAll returns the open issues of every repository, reading only | |||
| 265 | + | // the open ones (through the same index). The order is unspecified. | |||
| 266 | + | ListOpenAll(ctx context.Context) ([]domain.Issue, error) | |||
| 264 | // Update replaces the stored issue and keeps the index of the open issues | 267 | // Update replaces the stored issue and keeps the index of the open issues | ||
| 265 | // in step. It returns domain.ErrNotFound if there is none and | 268 | // in step. It returns domain.ErrNotFound if there is none and | ||
| 266 | // domain.ErrConflict if another write changed it since issue was read. | 269 | // domain.ErrConflict if another write changed it since issue was read. | ||
| @@ -294,6 +297,10 @@ type MergeRequestStore interface { | |||||
| 294 | // ListOpen returns the open merge requests of the repository, highest | 297 | // ListOpen returns the open merge requests of the repository, highest | ||
| 295 | // number first, reading only the open ones (through an index). | 298 | // number first, reading only the open ones (through an index). | ||
| 296 | ListOpen(ctx context.Context, repo domain.RepoName) ([]domain.MergeRequest, error) | 299 | ListOpen(ctx context.Context, repo domain.RepoName) ([]domain.MergeRequest, error) | ||
| 300 | + | // ListOpenAll returns the open merge requests of every repository, | |||
| 301 | + | // reading only the open ones (through the same index). The order is | |||
| 302 | + | // unspecified. | |||
| 303 | + | ListOpenAll(ctx context.Context) ([]domain.MergeRequest, error) | |||
| 297 | // CountOpen returns the number of open merge requests of the repository | 304 | // CountOpen returns the number of open merge requests of the repository | ||
| 298 | // from the index of keys: no request is read. | 305 | // from the index of keys: no request is read. | ||
| 299 | CountOpen(ctx context.Context, repo domain.RepoName) (int, error) | 306 | CountOpen(ctx context.Context, repo domain.RepoName) (int, error) | ||