493 lines
17 KiB
Go
493 lines
17 KiB
Go
package github
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestStore_GitHubWorkspaceSettingsRoundTrip(t *testing.T) {
|
|
store := newTestStore(t)
|
|
ctx := context.Background()
|
|
|
|
input := &WorkspaceSettings{
|
|
WorkspaceID: "ws-1",
|
|
RepoScopeMode: RepoScopeModeRepos,
|
|
RepoScopeOrgs: []string{"kdlbs"},
|
|
RepoScopeRepos: []RepoFilter{
|
|
{Owner: "kdlbs", Name: "kandev"},
|
|
},
|
|
SavedPresets: []byte(`[{"id":"p1","kind":"pr","label":"Mine"}]`),
|
|
DefaultQueryPresets: []byte(`{"pr":[],"issue":[]}`),
|
|
}
|
|
|
|
if err := store.UpsertWorkspaceSettings(ctx, input); err != nil {
|
|
t.Fatalf("upsert workspace settings: %v", err)
|
|
}
|
|
|
|
got, err := store.GetWorkspaceSettings(ctx, "ws-1")
|
|
if err != nil {
|
|
t.Fatalf("get workspace settings: %v", err)
|
|
}
|
|
if got.WorkspaceID != "ws-1" || got.RepoScopeMode != RepoScopeModeRepos {
|
|
t.Fatalf("unexpected settings identity/scope: %+v", got)
|
|
}
|
|
if len(got.RepoScopeRepos) != 1 || got.RepoScopeRepos[0].Owner != "kdlbs" || got.RepoScopeRepos[0].Name != "kandev" {
|
|
t.Fatalf("repo scope lost on round trip: %+v", got.RepoScopeRepos)
|
|
}
|
|
if string(got.SavedPresets) != string(input.SavedPresets) {
|
|
t.Fatalf("saved presets = %s, want %s", got.SavedPresets, input.SavedPresets)
|
|
}
|
|
if string(got.DefaultQueryPresets) != string(input.DefaultQueryPresets) {
|
|
t.Fatalf("default query presets = %s, want %s", got.DefaultQueryPresets, input.DefaultQueryPresets)
|
|
}
|
|
}
|
|
|
|
func TestService_SearchUserPRsPagedForWorkspace_FiltersToSelectedRepos(t *testing.T) {
|
|
client := NewMockClient()
|
|
client.AddPR(&PR{RepoOwner: "kdlbs", RepoName: "kandev", Number: 1, Title: "in scope"})
|
|
client.AddPR(&PR{RepoOwner: "other", RepoName: "repo", Number: 2, Title: "out of scope"})
|
|
store := newTestStore(t)
|
|
svc := NewService(client, AuthMethodPAT, nil, store, nil, testLogger(t))
|
|
ctx := context.Background()
|
|
|
|
if err := svc.UpsertWorkspaceSettings(ctx, &WorkspaceSettings{
|
|
WorkspaceID: "ws-1",
|
|
RepoScopeMode: RepoScopeModeRepos,
|
|
RepoScopeRepos: []RepoFilter{{Owner: "kdlbs", Name: "kandev"}},
|
|
}); err != nil {
|
|
t.Fatalf("save workspace settings: %v", err)
|
|
}
|
|
|
|
page, err := svc.SearchUserPRsPagedForWorkspace(ctx, "ws-1", "", "repo:other/repo is:open", 1, 25)
|
|
if err != nil {
|
|
t.Fatalf("search scoped prs: %v", err)
|
|
}
|
|
if page.TotalCount != 2 || len(page.PRs) != 1 {
|
|
t.Fatalf("expected one scoped PR, got total=%d prs=%+v", page.TotalCount, page.PRs)
|
|
}
|
|
if page.PRs[0].RepoOwner != "kdlbs" || page.PRs[0].RepoName != "kandev" {
|
|
t.Fatalf("custom query escaped workspace scope: %+v", page.PRs[0])
|
|
}
|
|
}
|
|
|
|
func TestService_SearchUserPRsPagedForWorkspace_AppendsScopeToQuery(t *testing.T) {
|
|
client := &capturingSearchClient{MockClient: NewMockClient()}
|
|
client.AddPR(&PR{RepoOwner: "kdlbs", RepoName: "kandev", Number: 1, Title: "in scope"})
|
|
store := newTestStore(t)
|
|
svc := NewService(client, AuthMethodPAT, nil, store, nil, testLogger(t))
|
|
ctx := context.Background()
|
|
|
|
if err := svc.UpsertWorkspaceSettings(ctx, &WorkspaceSettings{
|
|
WorkspaceID: "ws-1",
|
|
RepoScopeMode: RepoScopeModeRepos,
|
|
RepoScopeRepos: []RepoFilter{
|
|
{Owner: "kdlbs", Name: "kandev"},
|
|
{Owner: "kdlbs", Name: "docs"},
|
|
},
|
|
}); err != nil {
|
|
t.Fatalf("save workspace settings: %v", err)
|
|
}
|
|
|
|
page, err := svc.SearchUserPRsPagedForWorkspace(ctx, "ws-1", "", "is:open", 1, 25)
|
|
if err != nil {
|
|
t.Fatalf("search scoped prs: %v", err)
|
|
}
|
|
if page.TotalCount != 1 || len(page.PRs) != 1 {
|
|
t.Fatalf("expected one scoped PR, got total=%d prs=%+v", page.TotalCount, page.PRs)
|
|
}
|
|
if !strings.Contains(client.customQuery, "is:open") ||
|
|
!strings.Contains(client.customQuery, "(repo:kdlbs/kandev OR repo:kdlbs/docs)") {
|
|
t.Fatalf("custom query was not workspace scoped: %q", client.customQuery)
|
|
}
|
|
|
|
if _, err := svc.SearchUserPRsPagedForWorkspace(ctx, "ws-1", "review-requested:@me", "", 1, 25); err != nil {
|
|
t.Fatalf("search scoped prs with preset filter: %v", err)
|
|
}
|
|
if !strings.Contains(client.filter, "review-requested:@me") ||
|
|
!strings.Contains(client.filter, "(repo:kdlbs/kandev OR repo:kdlbs/docs)") {
|
|
t.Fatalf("filter query was not workspace scoped: %q", client.filter)
|
|
}
|
|
}
|
|
|
|
func TestService_SearchUserPRsPagedForWorkspace_OrgScopeIncludesPersonalOwner(t *testing.T) {
|
|
client := &capturingSearchClient{MockClient: NewMockClient()}
|
|
client.AddPR(&PR{RepoOwner: "octo", RepoName: "personal", Number: 1, Title: "in scope"})
|
|
store := newTestStore(t)
|
|
svc := NewService(client, AuthMethodPAT, nil, store, nil, testLogger(t))
|
|
ctx := context.Background()
|
|
|
|
if err := svc.UpsertWorkspaceSettings(ctx, &WorkspaceSettings{
|
|
WorkspaceID: "ws-1",
|
|
RepoScopeMode: RepoScopeModeOrgs,
|
|
RepoScopeOrgs: []string{"octo"},
|
|
}); err != nil {
|
|
t.Fatalf("save workspace settings: %v", err)
|
|
}
|
|
|
|
if _, err := svc.SearchUserPRsPagedForWorkspace(ctx, "ws-1", "", "is:open", 1, 25); err != nil {
|
|
t.Fatalf("search scoped prs: %v", err)
|
|
}
|
|
if !strings.Contains(client.customQuery, "(org:octo OR user:octo)") {
|
|
t.Fatalf("org scope did not include personal-owner qualifier: %q", client.customQuery)
|
|
}
|
|
}
|
|
|
|
func TestService_SearchUserPRsPagedForWorkspace_EmptyRepoScopeFailsClosed(t *testing.T) {
|
|
client := NewMockClient()
|
|
client.AddPR(&PR{RepoOwner: "kdlbs", RepoName: "kandev", Number: 1, Title: "hidden"})
|
|
store := newTestStore(t)
|
|
svc := NewService(client, AuthMethodPAT, nil, store, nil, testLogger(t))
|
|
ctx := context.Background()
|
|
|
|
if err := svc.UpsertWorkspaceSettings(ctx, &WorkspaceSettings{
|
|
WorkspaceID: "ws-1",
|
|
RepoScopeMode: RepoScopeModeRepos,
|
|
RepoScopeRepos: []RepoFilter{},
|
|
}); err != nil {
|
|
t.Fatalf("save workspace settings: %v", err)
|
|
}
|
|
|
|
page, err := svc.SearchUserPRsPagedForWorkspace(ctx, "ws-1", "", "is:open", 1, 25)
|
|
if err != nil {
|
|
t.Fatalf("search scoped prs: %v", err)
|
|
}
|
|
if page.TotalCount != 0 || len(page.PRs) != 0 {
|
|
t.Fatalf("empty repo scope should return no PRs, got total=%d prs=%+v", page.TotalCount, page.PRs)
|
|
}
|
|
}
|
|
|
|
func TestService_UpdateWorkspaceSettings_PartialUpdateAndNullClear(t *testing.T) {
|
|
store := newTestStore(t)
|
|
svc := NewService(NewMockClient(), AuthMethodPAT, nil, store, nil, testLogger(t))
|
|
ctx := context.Background()
|
|
defaults := json.RawMessage(`{"pr":[{"value":"mine","label":"Mine","filter":"is:open","group":"inbox"}],"issue":[]}`)
|
|
saved := json.RawMessage(`[{"id":"p1","kind":"pr","label":"Mine"}]`)
|
|
mode := RepoScopeModeRepos
|
|
repos := []RepoFilter{{Owner: "kdlbs", Name: "kandev"}}
|
|
if _, err := svc.UpdateWorkspaceSettings(ctx, &UpdateWorkspaceSettingsRequest{
|
|
WorkspaceID: "ws-1",
|
|
RepoScopeMode: &mode,
|
|
RepoScopeRepos: &repos,
|
|
SavedPresets: &saved,
|
|
SavedPresetsSet: true,
|
|
DefaultQueryPresets: &defaults,
|
|
DefaultQueriesSet: true,
|
|
}); err != nil {
|
|
t.Fatalf("initial update: %v", err)
|
|
}
|
|
|
|
nextSaved := json.RawMessage(`[{"id":"p2","kind":"issue","label":"Issues"}]`)
|
|
got, err := svc.UpdateWorkspaceSettings(ctx, &UpdateWorkspaceSettingsRequest{
|
|
WorkspaceID: "ws-1",
|
|
SavedPresets: &nextSaved,
|
|
SavedPresetsSet: true,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("partial saved presets update: %v", err)
|
|
}
|
|
if string(got.SavedPresets) != string(nextSaved) {
|
|
t.Fatalf("saved presets = %s, want %s", got.SavedPresets, nextSaved)
|
|
}
|
|
if string(got.DefaultQueryPresets) != string(defaults) {
|
|
t.Fatalf("default presets should be preserved, got %s", got.DefaultQueryPresets)
|
|
}
|
|
|
|
got, err = svc.UpdateWorkspaceSettings(ctx, &UpdateWorkspaceSettingsRequest{
|
|
WorkspaceID: "ws-1",
|
|
DefaultQueriesSet: true,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("clear default query presets: %v", err)
|
|
}
|
|
if got.DefaultQueryPresets != nil {
|
|
t.Fatalf("default presets should be cleared, got %s", got.DefaultQueryPresets)
|
|
}
|
|
if string(got.SavedPresets) != string(nextSaved) {
|
|
t.Fatalf("saved presets should be preserved after clear, got %s", got.SavedPresets)
|
|
}
|
|
|
|
got, err = svc.UpdateWorkspaceSettings(ctx, &UpdateWorkspaceSettingsRequest{
|
|
WorkspaceID: "ws-1",
|
|
SavedPresetsSet: true,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("clear saved presets: %v", err)
|
|
}
|
|
if string(got.SavedPresets) != "[]" {
|
|
t.Fatalf("saved presets should be cleared to empty array, got %s", got.SavedPresets)
|
|
}
|
|
}
|
|
|
|
func TestUpdateWorkspaceSettingsRequest_UnmarshalTracksExplicitNull(t *testing.T) {
|
|
var req UpdateWorkspaceSettingsRequest
|
|
if err := json.Unmarshal([]byte(`{"workspace_id":"ws-1","default_query_presets":null}`), &req); err != nil {
|
|
t.Fatalf("unmarshal request: %v", err)
|
|
}
|
|
if !req.DefaultQueriesSet {
|
|
t.Fatal("expected default query presets to be marked present")
|
|
}
|
|
if req.DefaultQueryPresets != nil {
|
|
t.Fatalf("expected explicit null to leave raw pointer nil, got %s", *req.DefaultQueryPresets)
|
|
}
|
|
}
|
|
|
|
func TestUpdateWorkspaceSettingsRequest_UnmarshalTracksSavedPresetsNull(t *testing.T) {
|
|
var req UpdateWorkspaceSettingsRequest
|
|
if err := json.Unmarshal([]byte(`{"workspace_id":"ws-1","saved_presets":null}`), &req); err != nil {
|
|
t.Fatalf("unmarshal request: %v", err)
|
|
}
|
|
if !req.SavedPresetsSet {
|
|
t.Fatal("expected saved presets to be marked present")
|
|
}
|
|
if req.SavedPresets != nil {
|
|
t.Fatalf("expected explicit null to leave raw pointer nil, got %s", *req.SavedPresets)
|
|
}
|
|
}
|
|
|
|
func TestService_UpdateWorkspaceSettings_RejectsUnknownRepoScopeMode(t *testing.T) {
|
|
store := newTestStore(t)
|
|
svc := NewService(NewMockClient(), AuthMethodPAT, nil, store, nil, testLogger(t))
|
|
mode := "everything"
|
|
|
|
_, err := svc.UpdateWorkspaceSettings(context.Background(), &UpdateWorkspaceSettingsRequest{
|
|
WorkspaceID: "ws-1",
|
|
RepoScopeMode: &mode,
|
|
})
|
|
if !errors.Is(err, ErrWorkspaceSettingsValidation) {
|
|
t.Fatalf("expected validation error, got %v", err)
|
|
}
|
|
}
|
|
|
|
func TestService_UpdateWorkspaceSettings_RejectsConflictingScopePatch(t *testing.T) {
|
|
store := newTestStore(t)
|
|
svc := NewService(NewMockClient(), AuthMethodPAT, nil, store, nil, testLogger(t))
|
|
mode := RepoScopeModeAll
|
|
orgs := []string{"octo"}
|
|
|
|
_, err := svc.UpdateWorkspaceSettings(context.Background(), &UpdateWorkspaceSettingsRequest{
|
|
WorkspaceID: "ws-1",
|
|
RepoScopeMode: &mode,
|
|
RepoScopeOrgs: &orgs,
|
|
})
|
|
if !errors.Is(err, ErrWorkspaceSettingsValidation) {
|
|
t.Fatalf("expected validation error, got %v", err)
|
|
}
|
|
}
|
|
|
|
func TestService_SearchUserIssuesPagedForWorkspace_AllScopePreservesResults(t *testing.T) {
|
|
client := &issueSearchClient{
|
|
issues: []*Issue{
|
|
{RepoOwner: "kdlbs", RepoName: "kandev", Number: 1, Title: "one"},
|
|
{RepoOwner: "other", RepoName: "repo", Number: 2, Title: "two"},
|
|
},
|
|
}
|
|
store := newTestStore(t)
|
|
svc := NewService(client, AuthMethodPAT, nil, store, nil, testLogger(t))
|
|
|
|
page, err := svc.SearchUserIssuesPagedForWorkspace(context.Background(), "ws-1", "", "is:open", 1, 25)
|
|
if err != nil {
|
|
t.Fatalf("search scoped issues: %v", err)
|
|
}
|
|
if page.TotalCount != 2 || len(page.Issues) != 2 {
|
|
t.Fatalf("all scope should preserve results, got total=%d issues=%+v", page.TotalCount, page.Issues)
|
|
}
|
|
}
|
|
|
|
func TestService_CheckReviewWatch_AppliesWorkspaceRepoScope(t *testing.T) {
|
|
client := NewMockClient()
|
|
client.AddPR(&PR{
|
|
RepoOwner: "kdlbs",
|
|
RepoName: "kandev",
|
|
Number: 1,
|
|
Title: "in scope",
|
|
RequestedReviewers: []RequestedReviewer{{Login: "octo", Type: "user"}},
|
|
})
|
|
client.AddPR(&PR{
|
|
RepoOwner: "other",
|
|
RepoName: "repo",
|
|
Number: 2,
|
|
Title: "out of scope",
|
|
RequestedReviewers: []RequestedReviewer{{Login: "octo", Type: "user"}},
|
|
})
|
|
store := newTestStore(t)
|
|
svc := NewService(client, AuthMethodPAT, nil, store, nil, testLogger(t))
|
|
ctx := context.Background()
|
|
|
|
if err := svc.UpsertWorkspaceSettings(ctx, &WorkspaceSettings{
|
|
WorkspaceID: "ws-1",
|
|
RepoScopeMode: RepoScopeModeRepos,
|
|
RepoScopeRepos: []RepoFilter{{Owner: "kdlbs", Name: "kandev"}},
|
|
}); err != nil {
|
|
t.Fatalf("save workspace settings: %v", err)
|
|
}
|
|
|
|
prs, err := svc.CheckReviewWatch(ctx, &ReviewWatch{
|
|
ID: "watch-1",
|
|
WorkspaceID: "ws-1",
|
|
Repos: nil,
|
|
ReviewScope: ReviewScopeUserAndTeams,
|
|
Enabled: true,
|
|
PollIntervalSeconds: 300,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("check review watch: %v", err)
|
|
}
|
|
if len(prs) != 1 || prs[0].RepoOwner != "kdlbs" || prs[0].RepoName != "kandev" {
|
|
t.Fatalf("expected one in-scope PR, got %+v", prs)
|
|
}
|
|
}
|
|
|
|
func TestService_CheckReviewWatch_EmptyWorkspaceScopeSkipsProviderFetch(t *testing.T) {
|
|
client := &countingReviewClient{MockClient: NewMockClient()}
|
|
client.AddPR(&PR{
|
|
RepoOwner: "kdlbs",
|
|
RepoName: "kandev",
|
|
Number: 1,
|
|
Title: "hidden",
|
|
RequestedReviewers: []RequestedReviewer{{Login: "octo", Type: "user"}},
|
|
})
|
|
store := newTestStore(t)
|
|
svc := NewService(client, AuthMethodPAT, nil, store, nil, testLogger(t))
|
|
ctx := context.Background()
|
|
|
|
if err := svc.UpsertWorkspaceSettings(ctx, &WorkspaceSettings{
|
|
WorkspaceID: "ws-1",
|
|
RepoScopeMode: RepoScopeModeRepos,
|
|
RepoScopeRepos: []RepoFilter{},
|
|
}); err != nil {
|
|
t.Fatalf("save workspace settings: %v", err)
|
|
}
|
|
|
|
prs, err := svc.CheckReviewWatch(ctx, &ReviewWatch{
|
|
ID: "watch-1",
|
|
WorkspaceID: "ws-1",
|
|
Repos: nil,
|
|
ReviewScope: ReviewScopeUserAndTeams,
|
|
Enabled: true,
|
|
PollIntervalSeconds: 300,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("check review watch: %v", err)
|
|
}
|
|
if len(prs) != 0 {
|
|
t.Fatalf("empty workspace scope should return no PRs, got %+v", prs)
|
|
}
|
|
if client.listReviewRequestedCalls != 0 {
|
|
t.Fatalf("expected no broad provider fetch, got %d calls", client.listReviewRequestedCalls)
|
|
}
|
|
}
|
|
|
|
func TestService_CheckIssueWatch_AppliesWorkspaceRepoScope(t *testing.T) {
|
|
client := &issueSearchClient{
|
|
issues: []*Issue{
|
|
{RepoOwner: "kdlbs", RepoName: "kandev", Number: 1, Title: "in scope"},
|
|
{RepoOwner: "other", RepoName: "repo", Number: 2, Title: "out of scope"},
|
|
},
|
|
}
|
|
store := newTestStore(t)
|
|
svc := NewService(client, AuthMethodPAT, nil, store, nil, testLogger(t))
|
|
ctx := context.Background()
|
|
|
|
if err := svc.UpsertWorkspaceSettings(ctx, &WorkspaceSettings{
|
|
WorkspaceID: "ws-1",
|
|
RepoScopeMode: RepoScopeModeOrgs,
|
|
RepoScopeOrgs: []string{"kdlbs"},
|
|
}); err != nil {
|
|
t.Fatalf("save workspace settings: %v", err)
|
|
}
|
|
|
|
issues, err := svc.CheckIssueWatch(ctx, &IssueWatch{
|
|
ID: "watch-1",
|
|
WorkspaceID: "ws-1",
|
|
Repos: nil,
|
|
Enabled: true,
|
|
PollIntervalSeconds: 300,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("check issue watch: %v", err)
|
|
}
|
|
if len(issues) != 1 || issues[0].RepoOwner != "kdlbs" || issues[0].RepoName != "kandev" {
|
|
t.Fatalf("expected one in-scope issue, got %+v", issues)
|
|
}
|
|
}
|
|
|
|
func TestService_CheckIssueWatch_EmptyWorkspaceScopeSkipsProviderFetch(t *testing.T) {
|
|
client := &countingWorkspaceIssueClient{MockClient: NewMockClient()}
|
|
store := newTestStore(t)
|
|
svc := NewService(client, AuthMethodPAT, nil, store, nil, testLogger(t))
|
|
ctx := context.Background()
|
|
|
|
if err := svc.UpsertWorkspaceSettings(ctx, &WorkspaceSettings{
|
|
WorkspaceID: "ws-1",
|
|
RepoScopeMode: RepoScopeModeOrgs,
|
|
RepoScopeOrgs: []string{},
|
|
}); err != nil {
|
|
t.Fatalf("save workspace settings: %v", err)
|
|
}
|
|
|
|
issues, err := svc.CheckIssueWatch(ctx, &IssueWatch{
|
|
ID: "watch-1",
|
|
WorkspaceID: "ws-1",
|
|
Repos: nil,
|
|
Enabled: true,
|
|
PollIntervalSeconds: 300,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("check issue watch: %v", err)
|
|
}
|
|
if len(issues) != 0 {
|
|
t.Fatalf("empty workspace scope should return no issues, got %+v", issues)
|
|
}
|
|
if client.listIssuesCalls != 0 {
|
|
t.Fatalf("expected no broad provider fetch, got %d calls", client.listIssuesCalls)
|
|
}
|
|
}
|
|
|
|
type issueSearchClient struct {
|
|
*MockClient
|
|
issues []*Issue
|
|
}
|
|
|
|
type countingReviewClient struct {
|
|
*MockClient
|
|
listReviewRequestedCalls int
|
|
}
|
|
|
|
func (c *countingReviewClient) ListReviewRequestedPRs(ctx context.Context, reviewScope, owner, repo string) ([]*PR, error) {
|
|
c.listReviewRequestedCalls++
|
|
return c.MockClient.ListReviewRequestedPRs(ctx, reviewScope, owner, repo)
|
|
}
|
|
|
|
type countingWorkspaceIssueClient struct {
|
|
*MockClient
|
|
listIssuesCalls int
|
|
}
|
|
|
|
func (c *countingWorkspaceIssueClient) ListIssues(context.Context, string, string) ([]*Issue, error) {
|
|
c.listIssuesCalls++
|
|
return []*Issue{{RepoOwner: "kdlbs", RepoName: "kandev", Number: 1, Title: "hidden"}}, nil
|
|
}
|
|
|
|
type capturingSearchClient struct {
|
|
*MockClient
|
|
filter string
|
|
customQuery string
|
|
}
|
|
|
|
func (c *capturingSearchClient) SearchPRsPaged(ctx context.Context, filter, customQuery string, page, perPage int) (*PRSearchPage, error) {
|
|
c.filter = filter
|
|
c.customQuery = customQuery
|
|
return c.MockClient.SearchPRsPaged(ctx, filter, customQuery, page, perPage)
|
|
}
|
|
|
|
func (c *issueSearchClient) ListIssues(context.Context, string, string) ([]*Issue, error) {
|
|
return c.issues, nil
|
|
}
|
|
|
|
func (c *issueSearchClient) ListIssuesPaged(context.Context, string, string, int, int) (*IssueSearchPage, error) {
|
|
return &IssueSearchPage{Issues: c.issues, TotalCount: len(c.issues), Page: 1, PerPage: 25}, nil
|
|
}
|