From e46b48f97572f5ffeba8ca203c25b09bfd58f348 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 5 Jan 2026 10:10:45 +0000 Subject: [PATCH 01/10] Initial plan From c1920133515c9a5dcd253d51e3f4cd1c27b3d64a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 5 Jan 2026 10:32:45 +0000 Subject: [PATCH 02/10] migrate tests from go-github-mock to internal testify-based mock Co-authored-by: JoannaaKL <67866556+JoannaaKL@users.noreply.github.com> --- pkg/github/actions_test.go | 26 ++--- pkg/github/helper_test.go | 2 +- pkg/github/issues_test.go | 2 +- pkg/github/projects_test.go | 2 +- pkg/github/testmock/testmock.go | 185 ++++++++++++++++++++++++++++++++ 5 files changed, 201 insertions(+), 16 deletions(-) create mode 100644 pkg/github/testmock/testmock.go diff --git a/pkg/github/actions_test.go b/pkg/github/actions_test.go index 7319feddf..65d44d671 100644 --- a/pkg/github/actions_test.go +++ b/pkg/github/actions_test.go @@ -15,10 +15,10 @@ import ( "github.com/github/github-mcp-server/internal/profiler" "github.com/github/github-mcp-server/internal/toolsnaps" buffer "github.com/github/github-mcp-server/pkg/buffer" + mock "github.com/github/github-mcp-server/pkg/github/testmock" "github.com/github/github-mcp-server/pkg/translations" "github.com/google/go-github/v79/github" "github.com/google/jsonschema-go/jsonschema" - "github.com/migueleliasweb/go-github-mock/src/mock" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -1540,7 +1540,7 @@ func Test_ListWorkflowRuns_Behavioral(t *testing.T) { name: "successful workflow runs listing", mockedClient: mock.NewMockedHTTPClient( mock.WithRequestMatchHandler( - mock.GetReposActionsWorkflowsRunsByOwnerByRepoByWorkflowId, + mock.GetReposActionsWorkflowsRunsByOwnerByRepoByWorkflowID, http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { runs := &github.WorkflowRuns{ TotalCount: github.Ptr(2), @@ -1627,7 +1627,7 @@ func Test_GetWorkflowRun_Behavioral(t *testing.T) { name: "successful get workflow run", mockedClient: mock.NewMockedHTTPClient( mock.WithRequestMatchHandler( - mock.GetReposActionsRunsByOwnerByRepoByRunId, + mock.GetReposActionsRunsByOwnerByRepoByRunID, http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { run := &github.WorkflowRun{ ID: github.Ptr(int64(12345)), @@ -1703,7 +1703,7 @@ func Test_GetWorkflowRunLogs_Behavioral(t *testing.T) { name: "successful get workflow run logs", mockedClient: mock.NewMockedHTTPClient( mock.WithRequestMatchHandler( - mock.GetReposActionsRunsLogsByOwnerByRepoByRunId, + mock.GetReposActionsRunsLogsByOwnerByRepoByRunID, http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { w.Header().Set("Location", "https://github.com/logs/run/12345") w.WriteHeader(http.StatusFound) @@ -1773,7 +1773,7 @@ func Test_ListWorkflowJobs_Behavioral(t *testing.T) { name: "successful list workflow jobs", mockedClient: mock.NewMockedHTTPClient( mock.WithRequestMatchHandler( - mock.GetReposActionsRunsJobsByOwnerByRepoByRunId, + mock.GetReposActionsRunsJobsByOwnerByRepoByRunID, http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { jobs := &github.Jobs{ TotalCount: github.Ptr(2), @@ -1954,7 +1954,7 @@ func Test_ActionsList_ListWorkflowRuns(t *testing.T) { t.Run("successful workflow runs list", func(t *testing.T) { mockedClient := mock.NewMockedHTTPClient( mock.WithRequestMatchHandler( - mock.GetReposActionsWorkflowsRunsByOwnerByRepoByWorkflowId, + mock.GetReposActionsWorkflowsRunsByOwnerByRepoByWorkflowID, http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { runs := &github.WorkflowRuns{ TotalCount: github.Ptr(1), @@ -2070,7 +2070,7 @@ func Test_ActionsGet_GetWorkflow(t *testing.T) { t.Run("successful workflow get", func(t *testing.T) { mockedClient := mock.NewMockedHTTPClient( mock.WithRequestMatchHandler( - mock.GetReposActionsWorkflowsByOwnerByRepoByWorkflowId, + mock.GetReposActionsWorkflowsByOwnerByRepoByWorkflowID, http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { workflow := &github.Workflow{ ID: github.Ptr(int64(1)), @@ -2116,7 +2116,7 @@ func Test_ActionsGet_GetWorkflowRun(t *testing.T) { t.Run("successful workflow run get", func(t *testing.T) { mockedClient := mock.NewMockedHTTPClient( mock.WithRequestMatchHandler( - mock.GetReposActionsRunsByOwnerByRepoByRunId, + mock.GetReposActionsRunsByOwnerByRepoByRunID, http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { run := &github.WorkflowRun{ ID: github.Ptr(int64(12345)), @@ -2187,7 +2187,7 @@ func Test_ActionsRunTrigger_RunWorkflow(t *testing.T) { name: "successful workflow run", mockedClient: mock.NewMockedHTTPClient( mock.WithRequestMatchHandler( - mock.PostReposActionsWorkflowsDispatchesByOwnerByRepoByWorkflowId, + mock.PostReposActionsWorkflowsDispatchesByOwnerByRepoByWorkflowID, http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusNoContent) }), @@ -2381,7 +2381,7 @@ func Test_ActionsGetJobLogs_SingleJob(t *testing.T) { t.Run("successful single job logs with URL", func(t *testing.T) { mockedClient := mock.NewMockedHTTPClient( mock.WithRequestMatchHandler( - mock.GetReposActionsJobsLogsByOwnerByRepoByJobId, + mock.GetReposActionsJobsLogsByOwnerByRepoByJobID, http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { w.Header().Set("Location", "https://github.com/logs/job/123") w.WriteHeader(http.StatusFound) @@ -2422,7 +2422,7 @@ func Test_ActionsGetJobLogs_FailedJobs(t *testing.T) { t.Run("successful failed jobs logs", func(t *testing.T) { mockedClient := mock.NewMockedHTTPClient( mock.WithRequestMatchHandler( - mock.GetReposActionsRunsJobsByOwnerByRepoByRunId, + mock.GetReposActionsRunsJobsByOwnerByRepoByRunID, http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { jobs := &github.Jobs{ TotalCount: github.Ptr(3), @@ -2449,7 +2449,7 @@ func Test_ActionsGetJobLogs_FailedJobs(t *testing.T) { }), ), mock.WithRequestMatchHandler( - mock.GetReposActionsJobsLogsByOwnerByRepoByJobId, + mock.GetReposActionsJobsLogsByOwnerByRepoByJobID, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Location", "https://github.com/logs/job/"+r.URL.Path[len(r.URL.Path)-1:]) w.WriteHeader(http.StatusFound) @@ -2487,7 +2487,7 @@ func Test_ActionsGetJobLogs_FailedJobs(t *testing.T) { t.Run("no failed jobs found", func(t *testing.T) { mockedClient := mock.NewMockedHTTPClient( mock.WithRequestMatchHandler( - mock.GetReposActionsRunsJobsByOwnerByRepoByRunId, + mock.GetReposActionsRunsJobsByOwnerByRepoByRunID, http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { jobs := &github.Jobs{ TotalCount: github.Ptr(2), diff --git a/pkg/github/helper_test.go b/pkg/github/helper_test.go index 56a236660..24e2a598b 100644 --- a/pkg/github/helper_test.go +++ b/pkg/github/helper_test.go @@ -59,7 +59,7 @@ const ( PatchReposIssuesByOwnerByRepoByIssueNumber = "PATCH /repos/{owner}/{repo}/issues/{issue_number}" GetReposIssuesSubIssuesByOwnerByRepoByIssueNumber = "GET /repos/{owner}/{repo}/issues/{issue_number}/sub_issues" PostReposIssuesSubIssuesByOwnerByRepoByIssueNumber = "POST /repos/{owner}/{repo}/issues/{issue_number}/sub_issues" - DeleteReposIssuesSubIssueByOwnerByRepoByIssueNumber = "DELETE /repos/{owner}/{repo}/issues/{issue_number}/sub_issues" + DeleteReposIssuesSubIssueByOwnerByRepoByIssueNumber = "DELETE /repos/{owner}/{repo}/issues/{issue_number}/sub_issue" PatchReposIssuesSubIssuesPriorityByOwnerByRepoByIssueNumber = "PATCH /repos/{owner}/{repo}/issues/{issue_number}/sub_issues/priority" // Pull request endpoints diff --git a/pkg/github/issues_test.go b/pkg/github/issues_test.go index 694b991dc..d9ef6bb10 100644 --- a/pkg/github/issues_test.go +++ b/pkg/github/issues_test.go @@ -13,11 +13,11 @@ import ( "github.com/github/github-mcp-server/internal/githubv4mock" "github.com/github/github-mcp-server/internal/toolsnaps" + mock "github.com/github/github-mcp-server/pkg/github/testmock" "github.com/github/github-mcp-server/pkg/lockdown" "github.com/github/github-mcp-server/pkg/translations" "github.com/google/go-github/v79/github" "github.com/google/jsonschema-go/jsonschema" - "github.com/migueleliasweb/go-github-mock/src/mock" "github.com/shurcooL/githubv4" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/pkg/github/projects_test.go b/pkg/github/projects_test.go index e443b9ecd..5045f84a2 100644 --- a/pkg/github/projects_test.go +++ b/pkg/github/projects_test.go @@ -8,10 +8,10 @@ import ( "testing" "github.com/github/github-mcp-server/internal/toolsnaps" + mock "github.com/github/github-mcp-server/pkg/github/testmock" "github.com/github/github-mcp-server/pkg/translations" gh "github.com/google/go-github/v79/github" "github.com/google/jsonschema-go/jsonschema" - "github.com/migueleliasweb/go-github-mock/src/mock" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) diff --git a/pkg/github/testmock/testmock.go b/pkg/github/testmock/testmock.go new file mode 100644 index 000000000..14a163d15 --- /dev/null +++ b/pkg/github/testmock/testmock.go @@ -0,0 +1,185 @@ +package mock + +import ( + "bytes" + "encoding/json" + "io" + "net/http" + "strings" +) + +// EndpointPattern mirrors the structure used by migueleliasweb/go-github-mock. +type EndpointPattern struct { + Pattern string + Method string +} + +type Option func(map[string]http.HandlerFunc) + +// WithRequestMatch registers a handler that returns the provided response with HTTP 200. +func WithRequestMatch(pattern EndpointPattern, response any) Option { + return func(handlers map[string]http.HandlerFunc) { + handlers[key(pattern)] = func(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(http.StatusOK) + switch body := response.(type) { + case string: + _, _ = w.Write([]byte(body)) + default: + if body == nil { + return + } + if data, err := json.Marshal(body); err == nil { + _, _ = w.Write(data) + } + } + } + } +} + +// WithRequestMatchHandler registers a custom handler for the given pattern. +func WithRequestMatchHandler(pattern EndpointPattern, handler http.HandlerFunc) Option { + return func(handlers map[string]http.HandlerFunc) { + handlers[key(pattern)] = handler + } +} + +// NewMockedHTTPClient creates an HTTP client that routes requests through registered handlers. +func NewMockedHTTPClient(options ...Option) *http.Client { + handlers := make(map[string]http.HandlerFunc) + for _, opt := range options { + if opt != nil { + opt(handlers) + } + } + return &http.Client{Transport: &transport{handlers: handlers}} +} + +type transport struct { + handlers map[string]http.HandlerFunc +} + +func (t *transport) RoundTrip(req *http.Request) (*http.Response, error) { + if handler, ok := t.handlers[key(EndpointPattern{Method: req.Method, Pattern: req.URL.Path})]; ok { + return executeHandler(handler, req), nil + } + + for patternKey, handler := range t.handlers { + method, pattern := splitKey(patternKey) + if method != req.Method { + continue + } + if matchPath(pattern, req.URL.Path) { + return executeHandler(handler, req), nil + } + } + + return &http.Response{ + StatusCode: http.StatusNotFound, + Body: io.NopCloser(bytes.NewReader([]byte("not found"))), + Request: req, + }, nil +} + +func executeHandler(handler http.HandlerFunc, req *http.Request) *http.Response { + rec := &responseRecorder{ + header: make(http.Header), + body: &bytes.Buffer{}, + } + handler(rec, req) + return &http.Response{ + StatusCode: rec.statusCode, + Header: rec.header, + Body: io.NopCloser(bytes.NewReader(rec.body.Bytes())), + Request: req, + } +} + +type responseRecorder struct { + statusCode int + header http.Header + body *bytes.Buffer +} + +func (r *responseRecorder) Header() http.Header { + return r.header +} + +func (r *responseRecorder) Write(data []byte) (int, error) { + if r.statusCode == 0 { + r.statusCode = http.StatusOK + } + return r.body.Write(data) +} + +func (r *responseRecorder) WriteHeader(statusCode int) { + r.statusCode = statusCode +} + +func key(p EndpointPattern) string { + return strings.ToUpper(p.Method) + " " + p.Pattern +} + +func splitKey(k string) (method, pattern string) { + parts := strings.SplitN(k, " ", 2) + if len(parts) == 2 { + return parts[0], parts[1] + } + return "", "" +} + +func matchPath(pattern, path string) bool { + if pattern == path { + return true + } + + patternParts := strings.Split(strings.Trim(pattern, "/"), "/") + pathParts := strings.Split(strings.Trim(path, "/"), "/") + + if len(patternParts) != len(pathParts) { + return false + } + + for i := range patternParts { + if strings.HasPrefix(patternParts[i], "{") && strings.HasSuffix(patternParts[i], "}") { + continue + } + if patternParts[i] != pathParts[i] { + return false + } + } + return true +} + +// MustMarshal marshals the provided value or panics on error. +func MustMarshal(v any) []byte { + data, err := json.Marshal(v) + if err != nil { + panic(err) + } + return data +} + +// REST endpoint patterns used in actions, issues and projects tests. +var ( + GetReposActionsJobsLogsByOwnerByRepoByJobID = EndpointPattern{Pattern: "/repos/{owner}/{repo}/actions/jobs/{job_id}/logs", Method: http.MethodGet} + GetReposActionsRunsByOwnerByRepo = EndpointPattern{Pattern: "/repos/{owner}/{repo}/actions/runs", Method: http.MethodGet} + GetReposActionsRunsByOwnerByRepoByRunID = EndpointPattern{Pattern: "/repos/{owner}/{repo}/actions/runs/{run_id}", Method: http.MethodGet} + GetReposActionsRunsJobsByOwnerByRepoByRunID = EndpointPattern{Pattern: "/repos/{owner}/{repo}/actions/runs/{run_id}/jobs", Method: http.MethodGet} + GetReposActionsRunsLogsByOwnerByRepoByRunID = EndpointPattern{Pattern: "/repos/{owner}/{repo}/actions/runs/{run_id}/logs", Method: http.MethodGet} + GetReposActionsWorkflowsByOwnerByRepo = EndpointPattern{Pattern: "/repos/{owner}/{repo}/actions/workflows", Method: http.MethodGet} + GetReposActionsWorkflowsByOwnerByRepoByWorkflowID = EndpointPattern{Pattern: "/repos/{owner}/{repo}/actions/workflows/{workflow_id}", Method: http.MethodGet} + GetReposActionsWorkflowsRunsByOwnerByRepoByWorkflowID = EndpointPattern{Pattern: "/repos/{owner}/{repo}/actions/workflows/{workflow_id}/runs", Method: http.MethodGet} + PostReposActionsWorkflowsDispatchesByOwnerByRepoByWorkflowID = EndpointPattern{Pattern: "/repos/{owner}/{repo}/actions/workflows/{workflow_id}/dispatches", Method: http.MethodPost} + + GetReposIssuesByOwnerByRepoByIssueNumber = EndpointPattern{Pattern: "/repos/{owner}/{repo}/issues/{issue_number}", Method: http.MethodGet} + GetReposIssuesCommentsByOwnerByRepoByIssueNumber = EndpointPattern{Pattern: "/repos/{owner}/{repo}/issues/{issue_number}/comments", Method: http.MethodGet} + GetReposIssuesSubIssuesByOwnerByRepoByIssueNumber = EndpointPattern{Pattern: "/repos/{owner}/{repo}/issues/{issue_number}/sub_issues", Method: http.MethodGet} + PostReposIssuesByOwnerByRepo = EndpointPattern{Pattern: "/repos/{owner}/{repo}/issues", Method: http.MethodPost} + PostReposIssuesCommentsByOwnerByRepoByIssueNumber = EndpointPattern{Pattern: "/repos/{owner}/{repo}/issues/{issue_number}/comments", Method: http.MethodPost} + PostReposIssuesSubIssuesByOwnerByRepoByIssueNumber = EndpointPattern{Pattern: "/repos/{owner}/{repo}/issues/{issue_number}/sub_issues", Method: http.MethodPost} + DeleteReposIssuesSubIssueByOwnerByRepoByIssueNumber = EndpointPattern{Pattern: "/repos/{owner}/{repo}/issues/{issue_number}/sub_issue", Method: http.MethodDelete} + PatchReposIssuesByOwnerByRepoByIssueNumber = EndpointPattern{Pattern: "/repos/{owner}/{repo}/issues/{issue_number}", Method: http.MethodPatch} + PatchReposIssuesSubIssuesPriorityByOwnerByRepoByIssueNumber = EndpointPattern{Pattern: "/repos/{owner}/{repo}/issues/{issue_number}/sub_issues/priority", Method: http.MethodPatch} + + GetSearchIssues = EndpointPattern{Pattern: "/search/issues", Method: http.MethodGet} +) From eb9480e5ee3021a0ba7cd52423030dcde6724379 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 5 Jan 2026 10:35:54 +0000 Subject: [PATCH 03/10] address feedback in testmock helper Co-authored-by: JoannaaKL <67866556+JoannaaKL@users.noreply.github.com> --- pkg/github/testmock/testmock.go | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/pkg/github/testmock/testmock.go b/pkg/github/testmock/testmock.go index 14a163d15..c3fd56ec2 100644 --- a/pkg/github/testmock/testmock.go +++ b/pkg/github/testmock/testmock.go @@ -23,13 +23,19 @@ func WithRequestMatch(pattern EndpointPattern, response any) Option { w.WriteHeader(http.StatusOK) switch body := response.(type) { case string: - _, _ = w.Write([]byte(body)) + if _, err := w.Write([]byte(body)); err != nil { + panic(err) + } default: if body == nil { return } - if data, err := json.Marshal(body); err == nil { - _, _ = w.Write(data) + data, err := json.Marshal(body) + if err != nil { + panic(err) + } + if _, err := w.Write(data); err != nil { + panic(err) } } } @@ -64,8 +70,8 @@ func (t *transport) RoundTrip(req *http.Request) (*http.Response, error) { } for patternKey, handler := range t.handlers { - method, pattern := splitKey(patternKey) - if method != req.Method { + method, pattern, ok := splitKey(patternKey) + if !ok || method != req.Method { continue } if matchPath(pattern, req.URL.Path) { @@ -119,12 +125,12 @@ func key(p EndpointPattern) string { return strings.ToUpper(p.Method) + " " + p.Pattern } -func splitKey(k string) (method, pattern string) { +func splitKey(k string) (method, pattern string, ok bool) { parts := strings.SplitN(k, " ", 2) if len(parts) == 2 { - return parts[0], parts[1] + return parts[0], parts[1], true } - return "", "" + return "", "", false } func matchPath(pattern, path string) bool { @@ -151,6 +157,7 @@ func matchPath(pattern, path string) bool { } // MustMarshal marshals the provided value or panics on error. +// Use this in tests when marshaling test data should halt execution immediately on failure. func MustMarshal(v any) []byte { data, err := json.Marshal(v) if err != nil { From 962708547c2b7670addcf0120d2891ffee768e39 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 5 Jan 2026 10:37:29 +0000 Subject: [PATCH 04/10] tweak testmock path matching edge case Co-authored-by: JoannaaKL <67866556+JoannaaKL@users.noreply.github.com> --- pkg/github/testmock/testmock.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkg/github/testmock/testmock.go b/pkg/github/testmock/testmock.go index c3fd56ec2..d58147e53 100644 --- a/pkg/github/testmock/testmock.go +++ b/pkg/github/testmock/testmock.go @@ -134,6 +134,10 @@ func splitKey(k string) (method, pattern string, ok bool) { } func matchPath(pattern, path string) bool { + if pattern == "" { + return path == "" + } + if pattern == path { return true } From 65ff757cf8be5d166b95a70a836247bdf8b53abb Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 5 Jan 2026 10:39:23 +0000 Subject: [PATCH 05/10] refine testmock options and path matching Co-authored-by: JoannaaKL <67866556+JoannaaKL@users.noreply.github.com> --- pkg/github/testmock/testmock.go | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/pkg/github/testmock/testmock.go b/pkg/github/testmock/testmock.go index d58147e53..7415541be 100644 --- a/pkg/github/testmock/testmock.go +++ b/pkg/github/testmock/testmock.go @@ -53,9 +53,7 @@ func WithRequestMatchHandler(pattern EndpointPattern, handler http.HandlerFunc) func NewMockedHTTPClient(options ...Option) *http.Client { handlers := make(map[string]http.HandlerFunc) for _, opt := range options { - if opt != nil { - opt(handlers) - } + opt(handlers) } return &http.Client{Transport: &transport{handlers: handlers}} } @@ -134,16 +132,19 @@ func splitKey(k string) (method, pattern string, ok bool) { } func matchPath(pattern, path string) bool { - if pattern == "" { - return path == "" + trimmedPattern := strings.Trim(pattern, "/") + trimmedPath := strings.Trim(path, "/") + + if trimmedPattern == "" { + return trimmedPath == "" } if pattern == path { return true } - patternParts := strings.Split(strings.Trim(pattern, "/"), "/") - pathParts := strings.Split(strings.Trim(path, "/"), "/") + patternParts := strings.Split(trimmedPattern, "/") + pathParts := strings.Split(trimmedPath, "/") if len(patternParts) != len(pathParts) { return false From 4f622c9573710d7787edb6ac4e4231e9f189158d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 5 Jan 2026 10:41:10 +0000 Subject: [PATCH 06/10] simplify matchPath and document delete endpoint Co-authored-by: JoannaaKL <67866556+JoannaaKL@users.noreply.github.com> --- pkg/github/testmock/testmock.go | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/pkg/github/testmock/testmock.go b/pkg/github/testmock/testmock.go index 7415541be..2bf365e82 100644 --- a/pkg/github/testmock/testmock.go +++ b/pkg/github/testmock/testmock.go @@ -139,7 +139,7 @@ func matchPath(pattern, path string) bool { return trimmedPath == "" } - if pattern == path { + if trimmedPattern == trimmedPath { return true } @@ -183,12 +183,13 @@ var ( GetReposActionsWorkflowsRunsByOwnerByRepoByWorkflowID = EndpointPattern{Pattern: "/repos/{owner}/{repo}/actions/workflows/{workflow_id}/runs", Method: http.MethodGet} PostReposActionsWorkflowsDispatchesByOwnerByRepoByWorkflowID = EndpointPattern{Pattern: "/repos/{owner}/{repo}/actions/workflows/{workflow_id}/dispatches", Method: http.MethodPost} - GetReposIssuesByOwnerByRepoByIssueNumber = EndpointPattern{Pattern: "/repos/{owner}/{repo}/issues/{issue_number}", Method: http.MethodGet} - GetReposIssuesCommentsByOwnerByRepoByIssueNumber = EndpointPattern{Pattern: "/repos/{owner}/{repo}/issues/{issue_number}/comments", Method: http.MethodGet} - GetReposIssuesSubIssuesByOwnerByRepoByIssueNumber = EndpointPattern{Pattern: "/repos/{owner}/{repo}/issues/{issue_number}/sub_issues", Method: http.MethodGet} - PostReposIssuesByOwnerByRepo = EndpointPattern{Pattern: "/repos/{owner}/{repo}/issues", Method: http.MethodPost} - PostReposIssuesCommentsByOwnerByRepoByIssueNumber = EndpointPattern{Pattern: "/repos/{owner}/{repo}/issues/{issue_number}/comments", Method: http.MethodPost} - PostReposIssuesSubIssuesByOwnerByRepoByIssueNumber = EndpointPattern{Pattern: "/repos/{owner}/{repo}/issues/{issue_number}/sub_issues", Method: http.MethodPost} + GetReposIssuesByOwnerByRepoByIssueNumber = EndpointPattern{Pattern: "/repos/{owner}/{repo}/issues/{issue_number}", Method: http.MethodGet} + GetReposIssuesCommentsByOwnerByRepoByIssueNumber = EndpointPattern{Pattern: "/repos/{owner}/{repo}/issues/{issue_number}/comments", Method: http.MethodGet} + GetReposIssuesSubIssuesByOwnerByRepoByIssueNumber = EndpointPattern{Pattern: "/repos/{owner}/{repo}/issues/{issue_number}/sub_issues", Method: http.MethodGet} + PostReposIssuesByOwnerByRepo = EndpointPattern{Pattern: "/repos/{owner}/{repo}/issues", Method: http.MethodPost} + PostReposIssuesCommentsByOwnerByRepoByIssueNumber = EndpointPattern{Pattern: "/repos/{owner}/{repo}/issues/{issue_number}/comments", Method: http.MethodPost} + PostReposIssuesSubIssuesByOwnerByRepoByIssueNumber = EndpointPattern{Pattern: "/repos/{owner}/{repo}/issues/{issue_number}/sub_issues", Method: http.MethodPost} + // Note: GitHub's delete endpoint uses the singular form `/sub_issue`. DeleteReposIssuesSubIssueByOwnerByRepoByIssueNumber = EndpointPattern{Pattern: "/repos/{owner}/{repo}/issues/{issue_number}/sub_issue", Method: http.MethodDelete} PatchReposIssuesByOwnerByRepoByIssueNumber = EndpointPattern{Pattern: "/repos/{owner}/{repo}/issues/{issue_number}", Method: http.MethodPatch} PatchReposIssuesSubIssuesPriorityByOwnerByRepoByIssueNumber = EndpointPattern{Pattern: "/repos/{owner}/{repo}/issues/{issue_number}/sub_issues/priority", Method: http.MethodPatch} From d485855d7a1bbadd0b9666782739b8836ce8a24d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 5 Jan 2026 10:56:54 +0000 Subject: [PATCH 07/10] Replace go-github-mock usage in tests with shared HTTP mock helper Co-authored-by: JoannaaKL <67866556+JoannaaKL@users.noreply.github.com> --- pkg/github/actions_test.go | 41 +++---- pkg/github/issues_test.go | 13 ++- pkg/github/projects_test.go | 77 +++++++------ pkg/github/testmock/testmock.go | 198 -------------------------------- 4 files changed, 67 insertions(+), 262 deletions(-) delete mode 100644 pkg/github/testmock/testmock.go diff --git a/pkg/github/actions_test.go b/pkg/github/actions_test.go index 65d44d671..0930542ed 100644 --- a/pkg/github/actions_test.go +++ b/pkg/github/actions_test.go @@ -15,7 +15,6 @@ import ( "github.com/github/github-mcp-server/internal/profiler" "github.com/github/github-mcp-server/internal/toolsnaps" buffer "github.com/github/github-mcp-server/pkg/buffer" - mock "github.com/github/github-mcp-server/pkg/github/testmock" "github.com/github/github-mcp-server/pkg/translations" "github.com/google/go-github/v79/github" "github.com/google/jsonschema-go/jsonschema" @@ -23,6 +22,10 @@ import ( "github.com/stretchr/testify/require" ) +func newMockedHTTPClient(handlers map[string]http.HandlerFunc) *http.Client { + return MockHTTPClientWithHandlers(handlers) +} + func Test_ListWorkflows(t *testing.T) { // Verify tool definition once toolDef := ListWorkflows(translations.NullTranslationHelper) @@ -1394,17 +1397,11 @@ func Test_RerunFailedJobs(t *testing.T) { }{ { name: "successful rerun of failed jobs", - mockedClient: mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.EndpointPattern{ - Pattern: "/repos/owner/repo/actions/runs/12345/rerun-failed-jobs", - Method: "POST", - }, - http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { - w.WriteHeader(http.StatusCreated) - }), - ), - ), + mockedClient: newMockedHTTPClient(map[string]http.HandlerFunc{ + "POST /repos/owner/repo/actions/runs/12345/rerun-failed-jobs": http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(http.StatusCreated) + }), + }), requestArgs: map[string]any{ "owner": "owner", "repo": "repo", @@ -1414,7 +1411,7 @@ func Test_RerunFailedJobs(t *testing.T) { }, { name: "missing required parameter run_id", - mockedClient: mock.NewMockedHTTPClient(), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{}), requestArgs: map[string]any{ "owner": "owner", "repo": "repo", @@ -1486,7 +1483,7 @@ func Test_RerunWorkflowRun_Behavioral(t *testing.T) { }, { name: "missing required parameter run_id", - mockedClient: mock.NewMockedHTTPClient(), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{}), requestArgs: map[string]any{ "owner": "owner", "repo": "repo", @@ -1573,7 +1570,7 @@ func Test_ListWorkflowRuns_Behavioral(t *testing.T) { }, { name: "missing required parameter workflow_id", - mockedClient: mock.NewMockedHTTPClient(), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{}), requestArgs: map[string]any{ "owner": "owner", "repo": "repo", @@ -1649,7 +1646,7 @@ func Test_GetWorkflowRun_Behavioral(t *testing.T) { }, { name: "missing required parameter run_id", - mockedClient: mock.NewMockedHTTPClient(), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{}), requestArgs: map[string]any{ "owner": "owner", "repo": "repo", @@ -1719,7 +1716,7 @@ func Test_GetWorkflowRunLogs_Behavioral(t *testing.T) { }, { name: "missing required parameter run_id", - mockedClient: mock.NewMockedHTTPClient(), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{}), requestArgs: map[string]any{ "owner": "owner", "repo": "repo", @@ -1806,7 +1803,7 @@ func Test_ListWorkflowJobs_Behavioral(t *testing.T) { }, { name: "missing required parameter run_id", - mockedClient: mock.NewMockedHTTPClient(), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{}), requestArgs: map[string]any{ "owner": "owner", "repo": "repo", @@ -1908,7 +1905,7 @@ func Test_ActionsList_ListWorkflows(t *testing.T) { }, { name: "missing required parameter method", - mockedClient: mock.NewMockedHTTPClient(), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{}), requestArgs: map[string]any{ "owner": "owner", "repo": "repo", @@ -2204,7 +2201,7 @@ func Test_ActionsRunTrigger_RunWorkflow(t *testing.T) { }, { name: "missing required parameter workflow_id", - mockedClient: mock.NewMockedHTTPClient(), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{}), requestArgs: map[string]any{ "method": "run_workflow", "owner": "owner", @@ -2216,7 +2213,7 @@ func Test_ActionsRunTrigger_RunWorkflow(t *testing.T) { }, { name: "missing required parameter ref", - mockedClient: mock.NewMockedHTTPClient(), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{}), requestArgs: map[string]any{ "method": "run_workflow", "owner": "owner", @@ -2332,7 +2329,7 @@ func Test_ActionsRunTrigger_CancelWorkflowRun(t *testing.T) { }) t.Run("missing run_id for non-run_workflow methods", func(t *testing.T) { - mockedClient := mock.NewMockedHTTPClient() + mockedClient := MockHTTPClientWithHandlers(map[string]http.HandlerFunc{}) client := github.NewClient(mockedClient) deps := BaseDeps{ diff --git a/pkg/github/issues_test.go b/pkg/github/issues_test.go index d9ef6bb10..813acd880 100644 --- a/pkg/github/issues_test.go +++ b/pkg/github/issues_test.go @@ -13,7 +13,6 @@ import ( "github.com/github/github-mcp-server/internal/githubv4mock" "github.com/github/github-mcp-server/internal/toolsnaps" - mock "github.com/github/github-mcp-server/pkg/github/testmock" "github.com/github/github-mcp-server/pkg/lockdown" "github.com/github/github-mcp-server/pkg/translations" "github.com/google/go-github/v79/github" @@ -23,6 +22,10 @@ import ( "github.com/stretchr/testify/require" ) +func newMockedHTTPClient(handlers map[string]http.HandlerFunc) *http.Client { + return MockHTTPClientWithHandlers(handlers) +} + var defaultGQLClient *githubv4.Client = githubv4.NewClient(newRepoAccessHTTPClient()) var repoAccessCache *lockdown.RepoAccessCache = stubRepoAccessCache(defaultGQLClient, 15*time.Minute) @@ -1438,7 +1441,7 @@ func Test_UpdateIssue(t *testing.T) { ), ), ), - mockedGQLClient: githubv4mock.NewMockedHTTPClient(), + mockedGQLClient: githubv4MockHTTPClientWithHandlers(map[string]http.HandlerFunc{}), requestArgs: map[string]interface{}{ "method": "update", "owner": "owner", @@ -1461,7 +1464,7 @@ func Test_UpdateIssue(t *testing.T) { }), ), ), - mockedGQLClient: githubv4mock.NewMockedHTTPClient(), + mockedGQLClient: githubv4MockHTTPClientWithHandlers(map[string]http.HandlerFunc{}), requestArgs: map[string]interface{}{ "method": "update", "owner": "owner", @@ -1748,8 +1751,8 @@ func Test_UpdateIssue(t *testing.T) { }, { name: "duplicate_of without duplicate state_reason should fail", - mockedRESTClient: mock.NewMockedHTTPClient(), - mockedGQLClient: githubv4mock.NewMockedHTTPClient(), + mockedRESTClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{}), + mockedGQLClient: githubv4MockHTTPClientWithHandlers(map[string]http.HandlerFunc{}), requestArgs: map[string]interface{}{ "method": "update", "owner": "owner", diff --git a/pkg/github/projects_test.go b/pkg/github/projects_test.go index 5045f84a2..536e95b21 100644 --- a/pkg/github/projects_test.go +++ b/pkg/github/projects_test.go @@ -8,7 +8,6 @@ import ( "testing" "github.com/github/github-mcp-server/internal/toolsnaps" - mock "github.com/github/github-mcp-server/pkg/github/testmock" "github.com/github/github-mcp-server/pkg/translations" gh "github.com/google/go-github/v79/github" "github.com/google/jsonschema-go/jsonschema" @@ -16,6 +15,10 @@ import ( "github.com/stretchr/testify/require" ) +func newMockedHTTPClient(handlers map[string]http.HandlerFunc) *http.Client { + return MockHTTPClientWithHandlers(handlers) +} + func Test_ListProjects(t *testing.T) { serverTool := ListProjects(translations.NullTranslationHelper) tool := serverTool.Tool @@ -122,7 +125,7 @@ func Test_ListProjects(t *testing.T) { }, { name: "missing owner", - mockedClient: mock.NewMockedHTTPClient(), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{}), requestArgs: map[string]interface{}{ "owner_type": "org", }, @@ -130,7 +133,7 @@ func Test_ListProjects(t *testing.T) { }, { name: "missing owner_type", - mockedClient: mock.NewMockedHTTPClient(), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{}), requestArgs: map[string]interface{}{ "owner": "octo-org", }, @@ -250,7 +253,7 @@ func Test_GetProject(t *testing.T) { }, { name: "missing project_number", - mockedClient: mock.NewMockedHTTPClient(), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{}), requestArgs: map[string]interface{}{ "owner": "octo-org", "owner_type": "org", @@ -259,7 +262,7 @@ func Test_GetProject(t *testing.T) { }, { name: "missing owner", - mockedClient: mock.NewMockedHTTPClient(), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{}), requestArgs: map[string]interface{}{ "project_number": float64(123), "owner_type": "org", @@ -268,7 +271,7 @@ func Test_GetProject(t *testing.T) { }, { name: "missing owner_type", - mockedClient: mock.NewMockedHTTPClient(), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{}), requestArgs: map[string]interface{}{ "project_number": float64(123), "owner": "octo-org", @@ -402,7 +405,7 @@ func Test_ListProjectFields(t *testing.T) { }, { name: "missing owner", - mockedClient: mock.NewMockedHTTPClient(), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{}), requestArgs: map[string]interface{}{ "owner_type": "org", "project_number": 10, @@ -411,7 +414,7 @@ func Test_ListProjectFields(t *testing.T) { }, { name: "missing owner_type", - mockedClient: mock.NewMockedHTTPClient(), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{}), requestArgs: map[string]interface{}{ "owner": "octo-org", "project_number": 10, @@ -420,7 +423,7 @@ func Test_ListProjectFields(t *testing.T) { }, { name: "missing project_number", - mockedClient: mock.NewMockedHTTPClient(), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{}), requestArgs: map[string]interface{}{ "owner": "octo-org", "owner_type": "org", @@ -549,7 +552,7 @@ func Test_GetProjectField(t *testing.T) { }, { name: "missing owner", - mockedClient: mock.NewMockedHTTPClient(), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{}), requestArgs: map[string]any{ "owner_type": "org", "project_number": float64(10), @@ -559,7 +562,7 @@ func Test_GetProjectField(t *testing.T) { }, { name: "missing owner_type", - mockedClient: mock.NewMockedHTTPClient(), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{}), requestArgs: map[string]any{ "owner": "octo-org", "project_number": float64(10), @@ -569,7 +572,7 @@ func Test_GetProjectField(t *testing.T) { }, { name: "missing project_number", - mockedClient: mock.NewMockedHTTPClient(), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{}), requestArgs: map[string]any{ "owner": "octo-org", "owner_type": "org", @@ -579,7 +582,7 @@ func Test_GetProjectField(t *testing.T) { }, { name: "missing field_id", - mockedClient: mock.NewMockedHTTPClient(), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{}), requestArgs: map[string]any{ "owner": "octo-org", "owner_type": "org", @@ -768,7 +771,7 @@ func Test_ListProjectItems(t *testing.T) { }, { name: "missing owner", - mockedClient: mock.NewMockedHTTPClient(), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{}), requestArgs: map[string]interface{}{ "owner_type": "org", "project_number": float64(10), @@ -777,7 +780,7 @@ func Test_ListProjectItems(t *testing.T) { }, { name: "missing owner_type", - mockedClient: mock.NewMockedHTTPClient(), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{}), requestArgs: map[string]interface{}{ "owner": "octo-org", "project_number": float64(10), @@ -786,7 +789,7 @@ func Test_ListProjectItems(t *testing.T) { }, { name: "missing project_number", - mockedClient: mock.NewMockedHTTPClient(), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{}), requestArgs: map[string]interface{}{ "owner": "octo-org", "owner_type": "org", @@ -952,7 +955,7 @@ func Test_GetProjectItem(t *testing.T) { }, { name: "missing owner", - mockedClient: mock.NewMockedHTTPClient(), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{}), requestArgs: map[string]any{ "owner_type": "org", "project_number": float64(10), @@ -962,7 +965,7 @@ func Test_GetProjectItem(t *testing.T) { }, { name: "missing owner_type", - mockedClient: mock.NewMockedHTTPClient(), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{}), requestArgs: map[string]any{ "owner": "octo-org", "project_number": float64(10), @@ -972,7 +975,7 @@ func Test_GetProjectItem(t *testing.T) { }, { name: "missing project_number", - mockedClient: mock.NewMockedHTTPClient(), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{}), requestArgs: map[string]any{ "owner": "octo-org", "owner_type": "org", @@ -982,7 +985,7 @@ func Test_GetProjectItem(t *testing.T) { }, { name: "missing item_id", - mockedClient: mock.NewMockedHTTPClient(), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{}), requestArgs: map[string]any{ "owner": "octo-org", "owner_type": "org", @@ -1166,7 +1169,7 @@ func Test_AddProjectItem(t *testing.T) { }, { name: "missing owner", - mockedClient: mock.NewMockedHTTPClient(), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{}), requestArgs: map[string]any{ "owner_type": "org", "project_number": float64(1), @@ -1177,7 +1180,7 @@ func Test_AddProjectItem(t *testing.T) { }, { name: "missing owner_type", - mockedClient: mock.NewMockedHTTPClient(), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{}), requestArgs: map[string]any{ "owner": "octo-org", "project_number": float64(1), @@ -1188,7 +1191,7 @@ func Test_AddProjectItem(t *testing.T) { }, { name: "missing project_number", - mockedClient: mock.NewMockedHTTPClient(), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{}), requestArgs: map[string]any{ "owner": "octo-org", "owner_type": "org", @@ -1199,7 +1202,7 @@ func Test_AddProjectItem(t *testing.T) { }, { name: "missing item_type", - mockedClient: mock.NewMockedHTTPClient(), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{}), requestArgs: map[string]any{ "owner": "octo-org", "owner_type": "org", @@ -1210,7 +1213,7 @@ func Test_AddProjectItem(t *testing.T) { }, { name: "missing item_id", - mockedClient: mock.NewMockedHTTPClient(), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{}), requestArgs: map[string]any{ "owner": "octo-org", "owner_type": "org", @@ -1401,7 +1404,7 @@ func Test_UpdateProjectItem(t *testing.T) { }, { name: "missing owner", - mockedClient: mock.NewMockedHTTPClient(), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{}), requestArgs: map[string]any{ "owner_type": "org", "project_number": float64(1), @@ -1415,7 +1418,7 @@ func Test_UpdateProjectItem(t *testing.T) { }, { name: "missing owner_type", - mockedClient: mock.NewMockedHTTPClient(), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{}), requestArgs: map[string]any{ "owner": "octo-org", "project_number": float64(1), @@ -1429,7 +1432,7 @@ func Test_UpdateProjectItem(t *testing.T) { }, { name: "missing project_number", - mockedClient: mock.NewMockedHTTPClient(), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{}), requestArgs: map[string]any{ "owner": "octo-org", "owner_type": "org", @@ -1443,7 +1446,7 @@ func Test_UpdateProjectItem(t *testing.T) { }, { name: "missing item_id", - mockedClient: mock.NewMockedHTTPClient(), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{}), requestArgs: map[string]any{ "owner": "octo-org", "owner_type": "org", @@ -1457,7 +1460,7 @@ func Test_UpdateProjectItem(t *testing.T) { }, { name: "missing updated_field", - mockedClient: mock.NewMockedHTTPClient(), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{}), requestArgs: map[string]any{ "owner": "octo-org", "owner_type": "org", @@ -1468,7 +1471,7 @@ func Test_UpdateProjectItem(t *testing.T) { }, { name: "updated_field not object", - mockedClient: mock.NewMockedHTTPClient(), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{}), requestArgs: map[string]any{ "owner": "octo-org", "owner_type": "org", @@ -1480,7 +1483,7 @@ func Test_UpdateProjectItem(t *testing.T) { }, { name: "updated_field missing id", - mockedClient: mock.NewMockedHTTPClient(), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{}), requestArgs: map[string]any{ "owner": "octo-org", "owner_type": "org", @@ -1492,7 +1495,7 @@ func Test_UpdateProjectItem(t *testing.T) { }, { name: "updated_field missing value", - mockedClient: mock.NewMockedHTTPClient(), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{}), requestArgs: map[string]any{ "owner": "octo-org", "owner_type": "org", @@ -1633,7 +1636,7 @@ func Test_DeleteProjectItem(t *testing.T) { }, { name: "missing owner", - mockedClient: mock.NewMockedHTTPClient(), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{}), requestArgs: map[string]any{ "owner_type": "org", "project_number": float64(1), @@ -1643,7 +1646,7 @@ func Test_DeleteProjectItem(t *testing.T) { }, { name: "missing owner_type", - mockedClient: mock.NewMockedHTTPClient(), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{}), requestArgs: map[string]any{ "owner": "octo-org", "project_number": float64(1), @@ -1653,7 +1656,7 @@ func Test_DeleteProjectItem(t *testing.T) { }, { name: "missing project_number", - mockedClient: mock.NewMockedHTTPClient(), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{}), requestArgs: map[string]any{ "owner": "octo-org", "owner_type": "org", @@ -1663,7 +1666,7 @@ func Test_DeleteProjectItem(t *testing.T) { }, { name: "missing item_id", - mockedClient: mock.NewMockedHTTPClient(), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{}), requestArgs: map[string]any{ "owner": "octo-org", "owner_type": "org", diff --git a/pkg/github/testmock/testmock.go b/pkg/github/testmock/testmock.go deleted file mode 100644 index 2bf365e82..000000000 --- a/pkg/github/testmock/testmock.go +++ /dev/null @@ -1,198 +0,0 @@ -package mock - -import ( - "bytes" - "encoding/json" - "io" - "net/http" - "strings" -) - -// EndpointPattern mirrors the structure used by migueleliasweb/go-github-mock. -type EndpointPattern struct { - Pattern string - Method string -} - -type Option func(map[string]http.HandlerFunc) - -// WithRequestMatch registers a handler that returns the provided response with HTTP 200. -func WithRequestMatch(pattern EndpointPattern, response any) Option { - return func(handlers map[string]http.HandlerFunc) { - handlers[key(pattern)] = func(w http.ResponseWriter, _ *http.Request) { - w.WriteHeader(http.StatusOK) - switch body := response.(type) { - case string: - if _, err := w.Write([]byte(body)); err != nil { - panic(err) - } - default: - if body == nil { - return - } - data, err := json.Marshal(body) - if err != nil { - panic(err) - } - if _, err := w.Write(data); err != nil { - panic(err) - } - } - } - } -} - -// WithRequestMatchHandler registers a custom handler for the given pattern. -func WithRequestMatchHandler(pattern EndpointPattern, handler http.HandlerFunc) Option { - return func(handlers map[string]http.HandlerFunc) { - handlers[key(pattern)] = handler - } -} - -// NewMockedHTTPClient creates an HTTP client that routes requests through registered handlers. -func NewMockedHTTPClient(options ...Option) *http.Client { - handlers := make(map[string]http.HandlerFunc) - for _, opt := range options { - opt(handlers) - } - return &http.Client{Transport: &transport{handlers: handlers}} -} - -type transport struct { - handlers map[string]http.HandlerFunc -} - -func (t *transport) RoundTrip(req *http.Request) (*http.Response, error) { - if handler, ok := t.handlers[key(EndpointPattern{Method: req.Method, Pattern: req.URL.Path})]; ok { - return executeHandler(handler, req), nil - } - - for patternKey, handler := range t.handlers { - method, pattern, ok := splitKey(patternKey) - if !ok || method != req.Method { - continue - } - if matchPath(pattern, req.URL.Path) { - return executeHandler(handler, req), nil - } - } - - return &http.Response{ - StatusCode: http.StatusNotFound, - Body: io.NopCloser(bytes.NewReader([]byte("not found"))), - Request: req, - }, nil -} - -func executeHandler(handler http.HandlerFunc, req *http.Request) *http.Response { - rec := &responseRecorder{ - header: make(http.Header), - body: &bytes.Buffer{}, - } - handler(rec, req) - return &http.Response{ - StatusCode: rec.statusCode, - Header: rec.header, - Body: io.NopCloser(bytes.NewReader(rec.body.Bytes())), - Request: req, - } -} - -type responseRecorder struct { - statusCode int - header http.Header - body *bytes.Buffer -} - -func (r *responseRecorder) Header() http.Header { - return r.header -} - -func (r *responseRecorder) Write(data []byte) (int, error) { - if r.statusCode == 0 { - r.statusCode = http.StatusOK - } - return r.body.Write(data) -} - -func (r *responseRecorder) WriteHeader(statusCode int) { - r.statusCode = statusCode -} - -func key(p EndpointPattern) string { - return strings.ToUpper(p.Method) + " " + p.Pattern -} - -func splitKey(k string) (method, pattern string, ok bool) { - parts := strings.SplitN(k, " ", 2) - if len(parts) == 2 { - return parts[0], parts[1], true - } - return "", "", false -} - -func matchPath(pattern, path string) bool { - trimmedPattern := strings.Trim(pattern, "/") - trimmedPath := strings.Trim(path, "/") - - if trimmedPattern == "" { - return trimmedPath == "" - } - - if trimmedPattern == trimmedPath { - return true - } - - patternParts := strings.Split(trimmedPattern, "/") - pathParts := strings.Split(trimmedPath, "/") - - if len(patternParts) != len(pathParts) { - return false - } - - for i := range patternParts { - if strings.HasPrefix(patternParts[i], "{") && strings.HasSuffix(patternParts[i], "}") { - continue - } - if patternParts[i] != pathParts[i] { - return false - } - } - return true -} - -// MustMarshal marshals the provided value or panics on error. -// Use this in tests when marshaling test data should halt execution immediately on failure. -func MustMarshal(v any) []byte { - data, err := json.Marshal(v) - if err != nil { - panic(err) - } - return data -} - -// REST endpoint patterns used in actions, issues and projects tests. -var ( - GetReposActionsJobsLogsByOwnerByRepoByJobID = EndpointPattern{Pattern: "/repos/{owner}/{repo}/actions/jobs/{job_id}/logs", Method: http.MethodGet} - GetReposActionsRunsByOwnerByRepo = EndpointPattern{Pattern: "/repos/{owner}/{repo}/actions/runs", Method: http.MethodGet} - GetReposActionsRunsByOwnerByRepoByRunID = EndpointPattern{Pattern: "/repos/{owner}/{repo}/actions/runs/{run_id}", Method: http.MethodGet} - GetReposActionsRunsJobsByOwnerByRepoByRunID = EndpointPattern{Pattern: "/repos/{owner}/{repo}/actions/runs/{run_id}/jobs", Method: http.MethodGet} - GetReposActionsRunsLogsByOwnerByRepoByRunID = EndpointPattern{Pattern: "/repos/{owner}/{repo}/actions/runs/{run_id}/logs", Method: http.MethodGet} - GetReposActionsWorkflowsByOwnerByRepo = EndpointPattern{Pattern: "/repos/{owner}/{repo}/actions/workflows", Method: http.MethodGet} - GetReposActionsWorkflowsByOwnerByRepoByWorkflowID = EndpointPattern{Pattern: "/repos/{owner}/{repo}/actions/workflows/{workflow_id}", Method: http.MethodGet} - GetReposActionsWorkflowsRunsByOwnerByRepoByWorkflowID = EndpointPattern{Pattern: "/repos/{owner}/{repo}/actions/workflows/{workflow_id}/runs", Method: http.MethodGet} - PostReposActionsWorkflowsDispatchesByOwnerByRepoByWorkflowID = EndpointPattern{Pattern: "/repos/{owner}/{repo}/actions/workflows/{workflow_id}/dispatches", Method: http.MethodPost} - - GetReposIssuesByOwnerByRepoByIssueNumber = EndpointPattern{Pattern: "/repos/{owner}/{repo}/issues/{issue_number}", Method: http.MethodGet} - GetReposIssuesCommentsByOwnerByRepoByIssueNumber = EndpointPattern{Pattern: "/repos/{owner}/{repo}/issues/{issue_number}/comments", Method: http.MethodGet} - GetReposIssuesSubIssuesByOwnerByRepoByIssueNumber = EndpointPattern{Pattern: "/repos/{owner}/{repo}/issues/{issue_number}/sub_issues", Method: http.MethodGet} - PostReposIssuesByOwnerByRepo = EndpointPattern{Pattern: "/repos/{owner}/{repo}/issues", Method: http.MethodPost} - PostReposIssuesCommentsByOwnerByRepoByIssueNumber = EndpointPattern{Pattern: "/repos/{owner}/{repo}/issues/{issue_number}/comments", Method: http.MethodPost} - PostReposIssuesSubIssuesByOwnerByRepoByIssueNumber = EndpointPattern{Pattern: "/repos/{owner}/{repo}/issues/{issue_number}/sub_issues", Method: http.MethodPost} - // Note: GitHub's delete endpoint uses the singular form `/sub_issue`. - DeleteReposIssuesSubIssueByOwnerByRepoByIssueNumber = EndpointPattern{Pattern: "/repos/{owner}/{repo}/issues/{issue_number}/sub_issue", Method: http.MethodDelete} - PatchReposIssuesByOwnerByRepoByIssueNumber = EndpointPattern{Pattern: "/repos/{owner}/{repo}/issues/{issue_number}", Method: http.MethodPatch} - PatchReposIssuesSubIssuesPriorityByOwnerByRepoByIssueNumber = EndpointPattern{Pattern: "/repos/{owner}/{repo}/issues/{issue_number}/sub_issues/priority", Method: http.MethodPatch} - - GetSearchIssues = EndpointPattern{Pattern: "/search/issues", Method: http.MethodGet} -) From 2010cb637b86e43ef840b25097d1f7a058110944 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 5 Jan 2026 11:07:34 +0000 Subject: [PATCH 08/10] Replace go-github-mock usage in tests with shared HTTP mock helper Co-authored-by: JoannaaKL <67866556+JoannaaKL@users.noreply.github.com> --- pkg/github/actions_test.go | 510 ++++++++++++++++-------------------- pkg/github/issues_test.go | 40 +-- pkg/github/projects_test.go | 4 - 3 files changed, 235 insertions(+), 319 deletions(-) diff --git a/pkg/github/actions_test.go b/pkg/github/actions_test.go index 0930542ed..0d47236f6 100644 --- a/pkg/github/actions_test.go +++ b/pkg/github/actions_test.go @@ -22,10 +22,6 @@ import ( "github.com/stretchr/testify/require" ) -func newMockedHTTPClient(handlers map[string]http.HandlerFunc) *http.Client { - return MockHTTPClientWithHandlers(handlers) -} - func Test_ListWorkflows(t *testing.T) { // Verify tool definition once toolDef := ListWorkflows(translations.NullTranslationHelper) @@ -1397,8 +1393,8 @@ func Test_RerunFailedJobs(t *testing.T) { }{ { name: "successful rerun of failed jobs", - mockedClient: newMockedHTTPClient(map[string]http.HandlerFunc{ - "POST /repos/owner/repo/actions/runs/12345/rerun-failed-jobs": http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ + PostReposActionsRunsRerunFailedJobsByOwnerByRepoByRunID: http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusCreated) }), }), @@ -1463,17 +1459,11 @@ func Test_RerunWorkflowRun_Behavioral(t *testing.T) { }{ { name: "successful rerun of workflow run", - mockedClient: mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.EndpointPattern{ - Pattern: "/repos/owner/repo/actions/runs/12345/rerun", - Method: "POST", - }, - http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { - w.WriteHeader(http.StatusCreated) - }), - ), - ), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ + PostReposActionsRunsRerunByOwnerByRepoByRunID: http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(http.StatusCreated) + }), + }), requestArgs: map[string]any{ "owner": "owner", "repo": "repo", @@ -1535,32 +1525,29 @@ func Test_ListWorkflowRuns_Behavioral(t *testing.T) { }{ { name: "successful workflow runs listing", - mockedClient: mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.GetReposActionsWorkflowsRunsByOwnerByRepoByWorkflowID, - http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { - runs := &github.WorkflowRuns{ - TotalCount: github.Ptr(2), - WorkflowRuns: []*github.WorkflowRun{ - { - ID: github.Ptr(int64(123)), - Name: github.Ptr("CI"), - Status: github.Ptr("completed"), - Conclusion: github.Ptr("success"), - }, - { - ID: github.Ptr(int64(456)), - Name: github.Ptr("CI"), - Status: github.Ptr("completed"), - Conclusion: github.Ptr("failure"), - }, + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ + GetReposActionsWorkflowsRunsByOwnerByRepoByWorkflowID: http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + runs := &github.WorkflowRuns{ + TotalCount: github.Ptr(2), + WorkflowRuns: []*github.WorkflowRun{ + { + ID: github.Ptr(int64(123)), + Name: github.Ptr("CI"), + Status: github.Ptr("completed"), + Conclusion: github.Ptr("success"), + }, + { + ID: github.Ptr(int64(456)), + Name: github.Ptr("CI"), + Status: github.Ptr("completed"), + Conclusion: github.Ptr("failure"), }, - } - w.WriteHeader(http.StatusOK) - _ = json.NewEncoder(w).Encode(runs) - }), - ), - ), + }, + } + w.WriteHeader(http.StatusOK) + _ = json.NewEncoder(w).Encode(runs) + }), + }), requestArgs: map[string]any{ "owner": "owner", "repo": "repo", @@ -1622,21 +1609,18 @@ func Test_GetWorkflowRun_Behavioral(t *testing.T) { }{ { name: "successful get workflow run", - mockedClient: mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.GetReposActionsRunsByOwnerByRepoByRunID, - http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { - run := &github.WorkflowRun{ - ID: github.Ptr(int64(12345)), - Name: github.Ptr("CI"), - Status: github.Ptr("completed"), - Conclusion: github.Ptr("success"), - } - w.WriteHeader(http.StatusOK) - _ = json.NewEncoder(w).Encode(run) - }), - ), - ), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ + GetReposActionsRunsByOwnerByRepoByRunID: http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + run := &github.WorkflowRun{ + ID: github.Ptr(int64(12345)), + Name: github.Ptr("CI"), + Status: github.Ptr("completed"), + Conclusion: github.Ptr("success"), + } + w.WriteHeader(http.StatusOK) + _ = json.NewEncoder(w).Encode(run) + }), + }), requestArgs: map[string]any{ "owner": "owner", "repo": "repo", @@ -1698,15 +1682,12 @@ func Test_GetWorkflowRunLogs_Behavioral(t *testing.T) { }{ { name: "successful get workflow run logs", - mockedClient: mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.GetReposActionsRunsLogsByOwnerByRepoByRunID, - http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { - w.Header().Set("Location", "https://github.com/logs/run/12345") - w.WriteHeader(http.StatusFound) - }), - ), - ), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ + GetReposActionsRunsLogsByOwnerByRepoByRunID: http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + w.Header().Set("Location", "https://github.com/logs/run/12345") + w.WriteHeader(http.StatusFound) + }), + }), requestArgs: map[string]any{ "owner": "owner", "repo": "repo", @@ -1768,32 +1749,29 @@ func Test_ListWorkflowJobs_Behavioral(t *testing.T) { }{ { name: "successful list workflow jobs", - mockedClient: mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.GetReposActionsRunsJobsByOwnerByRepoByRunID, - http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { - jobs := &github.Jobs{ - TotalCount: github.Ptr(2), - Jobs: []*github.WorkflowJob{ - { - ID: github.Ptr(int64(1)), - Name: github.Ptr("build"), - Status: github.Ptr("completed"), - Conclusion: github.Ptr("success"), - }, - { - ID: github.Ptr(int64(2)), - Name: github.Ptr("test"), - Status: github.Ptr("completed"), - Conclusion: github.Ptr("failure"), - }, + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ + GetReposActionsRunsJobsByOwnerByRepoByRunID: http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + jobs := &github.Jobs{ + TotalCount: github.Ptr(2), + Jobs: []*github.WorkflowJob{ + { + ID: github.Ptr(int64(1)), + Name: github.Ptr("build"), + Status: github.Ptr("completed"), + Conclusion: github.Ptr("success"), + }, + { + ID: github.Ptr(int64(2)), + Name: github.Ptr("test"), + Status: github.Ptr("completed"), + Conclusion: github.Ptr("failure"), }, - } - w.WriteHeader(http.StatusOK) - _ = json.NewEncoder(w).Encode(jobs) - }), - ), - ), + }, + } + w.WriteHeader(http.StatusOK) + _ = json.NewEncoder(w).Encode(jobs) + }), + }), requestArgs: map[string]any{ "owner": "owner", "repo": "repo", @@ -1870,32 +1848,29 @@ func Test_ActionsList_ListWorkflows(t *testing.T) { }{ { name: "successful workflow list", - mockedClient: mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.GetReposActionsWorkflowsByOwnerByRepo, - http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { - workflows := &github.Workflows{ - TotalCount: github.Ptr(2), - Workflows: []*github.Workflow{ - { - ID: github.Ptr(int64(1)), - Name: github.Ptr("CI"), - Path: github.Ptr(".github/workflows/ci.yml"), - State: github.Ptr("active"), - }, - { - ID: github.Ptr(int64(2)), - Name: github.Ptr("Deploy"), - Path: github.Ptr(".github/workflows/deploy.yml"), - State: github.Ptr("active"), - }, + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ + GetReposActionsWorkflowsByOwnerByRepo: http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + workflows := &github.Workflows{ + TotalCount: github.Ptr(2), + Workflows: []*github.Workflow{ + { + ID: github.Ptr(int64(1)), + Name: github.Ptr("CI"), + Path: github.Ptr(".github/workflows/ci.yml"), + State: github.Ptr("active"), }, - } - w.WriteHeader(http.StatusOK) - _ = json.NewEncoder(w).Encode(workflows) - }), - ), - ), + { + ID: github.Ptr(int64(2)), + Name: github.Ptr("Deploy"), + Path: github.Ptr(".github/workflows/deploy.yml"), + State: github.Ptr("active"), + }, + }, + } + w.WriteHeader(http.StatusOK) + _ = json.NewEncoder(w).Encode(workflows) + }), + }), requestArgs: map[string]any{ "method": "list_workflows", "owner": "owner", @@ -1949,26 +1924,23 @@ func Test_ActionsList_ListWorkflowRuns(t *testing.T) { toolDef := ActionsList(translations.NullTranslationHelper) t.Run("successful workflow runs list", func(t *testing.T) { - mockedClient := mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.GetReposActionsWorkflowsRunsByOwnerByRepoByWorkflowID, - http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { - runs := &github.WorkflowRuns{ - TotalCount: github.Ptr(1), - WorkflowRuns: []*github.WorkflowRun{ - { - ID: github.Ptr(int64(123)), - Name: github.Ptr("CI"), - Status: github.Ptr("completed"), - Conclusion: github.Ptr("success"), - }, + mockedClient := MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ + GetReposActionsWorkflowsRunsByOwnerByRepoByWorkflowID: http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + runs := &github.WorkflowRuns{ + TotalCount: github.Ptr(1), + WorkflowRuns: []*github.WorkflowRun{ + { + ID: github.Ptr(int64(123)), + Name: github.Ptr("CI"), + Status: github.Ptr("completed"), + Conclusion: github.Ptr("success"), }, - } - w.WriteHeader(http.StatusOK) - _ = json.NewEncoder(w).Encode(runs) - }), - ), - ) + }, + } + w.WriteHeader(http.StatusOK) + _ = json.NewEncoder(w).Encode(runs) + }), + }) client := github.NewClient(mockedClient) deps := BaseDeps{ @@ -1995,32 +1967,29 @@ func Test_ActionsList_ListWorkflowRuns(t *testing.T) { }) t.Run("list all workflow runs without resource_id", func(t *testing.T) { - mockedClient := mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.GetReposActionsRunsByOwnerByRepo, - http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { - runs := &github.WorkflowRuns{ - TotalCount: github.Ptr(2), - WorkflowRuns: []*github.WorkflowRun{ - { - ID: github.Ptr(int64(123)), - Name: github.Ptr("CI"), - Status: github.Ptr("completed"), - Conclusion: github.Ptr("success"), - }, - { - ID: github.Ptr(int64(456)), - Name: github.Ptr("Deploy"), - Status: github.Ptr("in_progress"), - Conclusion: nil, - }, + mockedClient := MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ + GetReposActionsRunsByOwnerByRepo: http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + runs := &github.WorkflowRuns{ + TotalCount: github.Ptr(2), + WorkflowRuns: []*github.WorkflowRun{ + { + ID: github.Ptr(int64(123)), + Name: github.Ptr("CI"), + Status: github.Ptr("completed"), + Conclusion: github.Ptr("success"), }, - } - w.WriteHeader(http.StatusOK) - _ = json.NewEncoder(w).Encode(runs) - }), - ), - ) + { + ID: github.Ptr(int64(456)), + Name: github.Ptr("Deploy"), + Status: github.Ptr("in_progress"), + Conclusion: nil, + }, + }, + } + w.WriteHeader(http.StatusOK) + _ = json.NewEncoder(w).Encode(runs) + }), + }) client := github.NewClient(mockedClient) deps := BaseDeps{ @@ -2065,21 +2034,18 @@ func Test_ActionsGet_GetWorkflow(t *testing.T) { toolDef := ActionsGet(translations.NullTranslationHelper) t.Run("successful workflow get", func(t *testing.T) { - mockedClient := mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.GetReposActionsWorkflowsByOwnerByRepoByWorkflowID, - http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { - workflow := &github.Workflow{ - ID: github.Ptr(int64(1)), - Name: github.Ptr("CI"), - Path: github.Ptr(".github/workflows/ci.yml"), - State: github.Ptr("active"), - } - w.WriteHeader(http.StatusOK) - _ = json.NewEncoder(w).Encode(workflow) - }), - ), - ) + mockedClient := MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ + GetReposActionsWorkflowsByOwnerByRepoByWorkflowID: http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + workflow := &github.Workflow{ + ID: github.Ptr(int64(1)), + Name: github.Ptr("CI"), + Path: github.Ptr(".github/workflows/ci.yml"), + State: github.Ptr("active"), + } + w.WriteHeader(http.StatusOK) + _ = json.NewEncoder(w).Encode(workflow) + }), + }) client := github.NewClient(mockedClient) deps := BaseDeps{ @@ -2111,21 +2077,18 @@ func Test_ActionsGet_GetWorkflowRun(t *testing.T) { toolDef := ActionsGet(translations.NullTranslationHelper) t.Run("successful workflow run get", func(t *testing.T) { - mockedClient := mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.GetReposActionsRunsByOwnerByRepoByRunID, - http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { - run := &github.WorkflowRun{ - ID: github.Ptr(int64(12345)), - Name: github.Ptr("CI"), - Status: github.Ptr("completed"), - Conclusion: github.Ptr("success"), - } - w.WriteHeader(http.StatusOK) - _ = json.NewEncoder(w).Encode(run) - }), - ), - ) + mockedClient := MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ + GetReposActionsRunsByOwnerByRepoByRunID: http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + run := &github.WorkflowRun{ + ID: github.Ptr(int64(12345)), + Name: github.Ptr("CI"), + Status: github.Ptr("completed"), + Conclusion: github.Ptr("success"), + } + w.WriteHeader(http.StatusOK) + _ = json.NewEncoder(w).Encode(run) + }), + }) client := github.NewClient(mockedClient) deps := BaseDeps{ @@ -2182,14 +2145,11 @@ func Test_ActionsRunTrigger_RunWorkflow(t *testing.T) { }{ { name: "successful workflow run", - mockedClient: mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.PostReposActionsWorkflowsDispatchesByOwnerByRepoByWorkflowID, - http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { - w.WriteHeader(http.StatusNoContent) - }), - ), - ), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ + PostReposActionsWorkflowsDispatchesByOwnerByRepoByWorkflowID: http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(http.StatusNoContent) + }), + }), requestArgs: map[string]any{ "method": "run_workflow", "owner": "owner", @@ -2258,17 +2218,11 @@ func Test_ActionsRunTrigger_CancelWorkflowRun(t *testing.T) { toolDef := ActionsRunTrigger(translations.NullTranslationHelper) t.Run("successful workflow run cancellation", func(t *testing.T) { - mockedClient := mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.EndpointPattern{ - Pattern: "/repos/owner/repo/actions/runs/12345/cancel", - Method: "POST", - }, - http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { - w.WriteHeader(http.StatusAccepted) - }), - ), - ) + mockedClient := MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ + PostReposActionsRunsCancelByOwnerByRepoByRunID: http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(http.StatusAccepted) + }), + }) client := github.NewClient(mockedClient) deps := BaseDeps{ @@ -2295,17 +2249,11 @@ func Test_ActionsRunTrigger_CancelWorkflowRun(t *testing.T) { }) t.Run("conflict when cancelling a workflow run", func(t *testing.T) { - mockedClient := mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.EndpointPattern{ - Pattern: "/repos/owner/repo/actions/runs/12345/cancel", - Method: "POST", - }, - http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { - w.WriteHeader(http.StatusConflict) - }), - ), - ) + mockedClient := MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ + PostReposActionsRunsCancelByOwnerByRepoByRunID: http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(http.StatusConflict) + }), + }) client := github.NewClient(mockedClient) deps := BaseDeps{ @@ -2376,15 +2324,12 @@ func Test_ActionsGetJobLogs_SingleJob(t *testing.T) { toolDef := ActionsGetJobLogs(translations.NullTranslationHelper) t.Run("successful single job logs with URL", func(t *testing.T) { - mockedClient := mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.GetReposActionsJobsLogsByOwnerByRepoByJobID, - http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { - w.Header().Set("Location", "https://github.com/logs/job/123") - w.WriteHeader(http.StatusFound) - }), - ), - ) + mockedClient := MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ + GetReposActionsJobsLogsByOwnerByRepoByJobID: http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + w.Header().Set("Location", "https://github.com/logs/job/123") + w.WriteHeader(http.StatusFound) + }), + }) client := github.NewClient(mockedClient) deps := BaseDeps{ @@ -2417,42 +2362,36 @@ func Test_ActionsGetJobLogs_FailedJobs(t *testing.T) { toolDef := ActionsGetJobLogs(translations.NullTranslationHelper) t.Run("successful failed jobs logs", func(t *testing.T) { - mockedClient := mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.GetReposActionsRunsJobsByOwnerByRepoByRunID, - http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { - jobs := &github.Jobs{ - TotalCount: github.Ptr(3), - Jobs: []*github.WorkflowJob{ - { - ID: github.Ptr(int64(1)), - Name: github.Ptr("test-job-1"), - Conclusion: github.Ptr("success"), - }, - { - ID: github.Ptr(int64(2)), - Name: github.Ptr("test-job-2"), - Conclusion: github.Ptr("failure"), - }, - { - ID: github.Ptr(int64(3)), - Name: github.Ptr("test-job-3"), - Conclusion: github.Ptr("failure"), - }, + mockedClient := MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ + GetReposActionsRunsJobsByOwnerByRepoByRunID: http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + jobs := &github.Jobs{ + TotalCount: github.Ptr(3), + Jobs: []*github.WorkflowJob{ + { + ID: github.Ptr(int64(1)), + Name: github.Ptr("test-job-1"), + Conclusion: github.Ptr("success"), }, - } - w.WriteHeader(http.StatusOK) - _ = json.NewEncoder(w).Encode(jobs) - }), - ), - mock.WithRequestMatchHandler( - mock.GetReposActionsJobsLogsByOwnerByRepoByJobID, - http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Location", "https://github.com/logs/job/"+r.URL.Path[len(r.URL.Path)-1:]) - w.WriteHeader(http.StatusFound) - }), - ), - ) + { + ID: github.Ptr(int64(2)), + Name: github.Ptr("test-job-2"), + Conclusion: github.Ptr("failure"), + }, + { + ID: github.Ptr(int64(3)), + Name: github.Ptr("test-job-3"), + Conclusion: github.Ptr("failure"), + }, + }, + } + w.WriteHeader(http.StatusOK) + _ = json.NewEncoder(w).Encode(jobs) + }), + GetReposActionsJobsLogsByOwnerByRepoByJobID: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Location", "https://github.com/logs/job/"+r.URL.Path[len(r.URL.Path)-1:]) + w.WriteHeader(http.StatusFound) + }), + }) client := github.NewClient(mockedClient) deps := BaseDeps{ @@ -2482,30 +2421,27 @@ func Test_ActionsGetJobLogs_FailedJobs(t *testing.T) { }) t.Run("no failed jobs found", func(t *testing.T) { - mockedClient := mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.GetReposActionsRunsJobsByOwnerByRepoByRunID, - http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { - jobs := &github.Jobs{ - TotalCount: github.Ptr(2), - Jobs: []*github.WorkflowJob{ - { - ID: github.Ptr(int64(1)), - Name: github.Ptr("test-job-1"), - Conclusion: github.Ptr("success"), - }, - { - ID: github.Ptr(int64(2)), - Name: github.Ptr("test-job-2"), - Conclusion: github.Ptr("success"), - }, + mockedClient := MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ + GetReposActionsRunsJobsByOwnerByRepoByRunID: http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + jobs := &github.Jobs{ + TotalCount: github.Ptr(2), + Jobs: []*github.WorkflowJob{ + { + ID: github.Ptr(int64(1)), + Name: github.Ptr("test-job-1"), + Conclusion: github.Ptr("success"), }, - } - w.WriteHeader(http.StatusOK) - _ = json.NewEncoder(w).Encode(jobs) - }), - ), - ) + { + ID: github.Ptr(int64(2)), + Name: github.Ptr("test-job-2"), + Conclusion: github.Ptr("success"), + }, + }, + } + w.WriteHeader(http.StatusOK) + _ = json.NewEncoder(w).Encode(jobs) + }), + }) client := github.NewClient(mockedClient) deps := BaseDeps{ diff --git a/pkg/github/issues_test.go b/pkg/github/issues_test.go index 813acd880..e5e11500d 100644 --- a/pkg/github/issues_test.go +++ b/pkg/github/issues_test.go @@ -22,10 +22,6 @@ import ( "github.com/stretchr/testify/require" ) -func newMockedHTTPClient(handlers map[string]http.HandlerFunc) *http.Client { - return MockHTTPClientWithHandlers(handlers) -} - var defaultGQLClient *githubv4.Client = githubv4.NewClient(newRepoAccessHTTPClient()) var repoAccessCache *lockdown.RepoAccessCache = stubRepoAccessCache(defaultGQLClient, 15*time.Minute) @@ -184,12 +180,9 @@ func Test_GetIssue(t *testing.T) { }{ { name: "successful issue retrieval", - mockedClient: mock.NewMockedHTTPClient( - mock.WithRequestMatch( - mock.GetReposIssuesByOwnerByRepoByIssueNumber, - mockIssue, - ), - ), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ + GetReposIssuesByOwnerByRepoByIssueNumber: mockResponse(t, http.StatusOK, mockIssue), + }), requestArgs: map[string]interface{}{ "method": "get", "owner": "owner2", @@ -200,12 +193,9 @@ func Test_GetIssue(t *testing.T) { }, { name: "issue not found", - mockedClient: mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.GetReposIssuesByOwnerByRepoByIssueNumber, - mockResponse(t, http.StatusNotFound, `{"message": "Issue not found"}`), - ), - ), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ + GetReposIssuesByOwnerByRepoByIssueNumber: mockResponse(t, http.StatusNotFound, `{"message": "Issue not found"}`), + }), requestArgs: map[string]interface{}{ "method": "get", "owner": "owner", @@ -217,12 +207,9 @@ func Test_GetIssue(t *testing.T) { }, { name: "lockdown enabled - private repository", - mockedClient: mock.NewMockedHTTPClient( - mock.WithRequestMatch( - mock.GetReposIssuesByOwnerByRepoByIssueNumber, - mockIssue2, - ), - ), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ + GetReposIssuesByOwnerByRepoByIssueNumber: mockResponse(t, http.StatusOK, mockIssue2), + }), gqlHTTPClient: githubv4mock.NewMockedHTTPClient( githubv4mock.NewQueryMatcher( struct { @@ -264,12 +251,9 @@ func Test_GetIssue(t *testing.T) { }, { name: "lockdown enabled - user lacks push access", - mockedClient: mock.NewMockedHTTPClient( - mock.WithRequestMatch( - mock.GetReposIssuesByOwnerByRepoByIssueNumber, - mockIssue, - ), - ), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ + GetReposIssuesByOwnerByRepoByIssueNumber: mockResponse(t, http.StatusOK, mockIssue), + }), gqlHTTPClient: githubv4mock.NewMockedHTTPClient( githubv4mock.NewQueryMatcher( struct { diff --git a/pkg/github/projects_test.go b/pkg/github/projects_test.go index 536e95b21..404ced19a 100644 --- a/pkg/github/projects_test.go +++ b/pkg/github/projects_test.go @@ -15,10 +15,6 @@ import ( "github.com/stretchr/testify/require" ) -func newMockedHTTPClient(handlers map[string]http.HandlerFunc) *http.Client { - return MockHTTPClientWithHandlers(handlers) -} - func Test_ListProjects(t *testing.T) { serverTool := ListProjects(translations.NullTranslationHelper) tool := serverTool.Tool From 5b386c8ee5c0471a38529750d5fd9cfdfb1f9388 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 5 Jan 2026 11:20:57 +0000 Subject: [PATCH 09/10] Initial plan From d249cc4b99fdb6359bfc39ae4f9d045031d98709 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 5 Jan 2026 12:05:37 +0000 Subject: [PATCH 10/10] Fix failing macOS test build - add missing endpoint constant and fix mock usage Co-authored-by: JoannaaKL <67866556+JoannaaKL@users.noreply.github.com> --- pkg/github/helper_test.go | 21 + pkg/github/issues_test.go | 806 ++++++++++++------------------------ pkg/github/projects_test.go | 515 ++++++++++------------- 3 files changed, 492 insertions(+), 850 deletions(-) diff --git a/pkg/github/helper_test.go b/pkg/github/helper_test.go index 24e2a598b..548d87dca 100644 --- a/pkg/github/helper_test.go +++ b/pkg/github/helper_test.go @@ -118,6 +118,7 @@ const ( GetReposActionsWorkflowsByOwnerByRepoByWorkflowID = "GET /repos/{owner}/{repo}/actions/workflows/{workflow_id}" PostReposActionsWorkflowsDispatchesByOwnerByRepoByWorkflowID = "POST /repos/{owner}/{repo}/actions/workflows/{workflow_id}/dispatches" GetReposActionsWorkflowsRunsByOwnerByRepoByWorkflowID = "GET /repos/{owner}/{repo}/actions/workflows/{workflow_id}/runs" + GetReposActionsRunsByOwnerByRepo = "GET /repos/{owner}/{repo}/actions/runs" GetReposActionsRunsByOwnerByRepoByRunID = "GET /repos/{owner}/{repo}/actions/runs/{run_id}" GetReposActionsRunsLogsByOwnerByRepoByRunID = "GET /repos/{owner}/{repo}/actions/runs/{run_id}/logs" GetReposActionsRunsJobsByOwnerByRepoByRunID = "GET /repos/{owner}/{repo}/actions/runs/{run_id}/jobs" @@ -129,6 +130,26 @@ const ( GetReposActionsJobsLogsByOwnerByRepoByJobID = "GET /repos/{owner}/{repo}/actions/jobs/{job_id}/logs" DeleteReposActionsRunsLogsByOwnerByRepoByRunID = "DELETE /repos/{owner}/{repo}/actions/runs/{run_id}/logs" + // Projects endpoints + GetOrgsProjectsV2ByOrg = "GET /orgs/{org}/projectsV2" + GetUsersProjectsV2ByUsername = "GET /users/{username}/projectsV2" + GetOrgsProjectsV2ByOrgByProjectNumber = "GET /orgs/{org}/projectsV2/{project_number}" + GetUsersProjectsV2ByUsernameByProjectNumber = "GET /users/{username}/projectsV2/{project_number}" + GetOrgsProjectsV2FieldsByOrgByProjectNumber = "GET /orgs/{org}/projectsV2/{project_number}/fields" + GetUsersProjectsV2FieldsByUsernameByProjectNumber = "GET /users/{username}/projectsV2/{project_number}/fields" + GetOrgsProjectsV2FieldsByOrgByProjectNumberByFieldID = "GET /orgs/{org}/projectsV2/{project_number}/fields/{field_id}" + GetUsersProjectsV2FieldsByUsernameByProjectNumberByFieldID = "GET /users/{username}/projectsV2/{project_number}/fields/{field_id}" + GetOrgsProjectsV2ItemsByOrgByProjectNumber = "GET /orgs/{org}/projectsV2/{project_number}/items" + GetUsersProjectsV2ItemsByUsernameByProjectNumber = "GET /users/{username}/projectsV2/{project_number}/items" + GetOrgsProjectsV2ItemsByOrgByProjectNumberByItemID = "GET /orgs/{org}/projectsV2/{project_number}/items/{item_id}" + GetUsersProjectsV2ItemsByUsernameByProjectNumberByItemID = "GET /users/{username}/projectsV2/{project_number}/items/{item_id}" + PostOrgsProjectsV2ItemsByOrgByProjectNumber = "POST /orgs/{org}/projectsV2/{project_number}/items" + PostUsersProjectsV2ItemsByUsernameByProjectNumber = "POST /users/{username}/projectsV2/{project_number}/items" + PatchOrgsProjectsV2ItemsByOrgByProjectNumberByItemID = "PATCH /orgs/{org}/projectsV2/{project_number}/items/{item_id}" + PatchUsersProjectsV2ItemsByUsernameByProjectNumberByItemID = "PATCH /users/{username}/projectsV2/{project_number}/items/{item_id}" + DeleteOrgsProjectsV2ItemsByOrgByProjectNumberByItemID = "DELETE /orgs/{org}/projectsV2/{project_number}/items/{item_id}" + DeleteUsersProjectsV2ItemsByUsernameByProjectNumberByItemID = "DELETE /users/{username}/projectsV2/{project_number}/items/{item_id}" + // Search endpoints GetSearchCode = "GET /search/code" GetSearchIssues = "GET /search/issues" diff --git a/pkg/github/issues_test.go b/pkg/github/issues_test.go index e5e11500d..87a7bacbe 100644 --- a/pkg/github/issues_test.go +++ b/pkg/github/issues_test.go @@ -392,13 +392,8 @@ func Test_AddIssueComment(t *testing.T) { expectedErrMsg string }{ { - name: "successful comment creation", - mockedClient: mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.PostReposIssuesCommentsByOwnerByRepoByIssueNumber, - mockResponse(t, http.StatusCreated, mockComment), - ), - ), + name: "successful comment creation", + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{PostReposIssuesCommentsByOwnerByRepoByIssueNumber: mockResponse(t, http.StatusCreated, mockComment)}), requestArgs: map[string]interface{}{ "owner": "owner", "repo": "repo", @@ -410,15 +405,10 @@ func Test_AddIssueComment(t *testing.T) { }, { name: "comment creation fails", - mockedClient: mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.PostReposIssuesCommentsByOwnerByRepoByIssueNumber, - http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { - w.WriteHeader(http.StatusUnprocessableEntity) - _, _ = w.Write([]byte(`{"message": "Invalid request"}`)) - }), - ), - ), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{PostReposIssuesCommentsByOwnerByRepoByIssueNumber: http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(http.StatusUnprocessableEntity) + _, _ = w.Write([]byte(`{"message": "Invalid request"}`)) + })}), requestArgs: map[string]interface{}{ "owner": "owner", "repo": "repo", @@ -533,23 +523,18 @@ func Test_SearchIssues(t *testing.T) { }{ { name: "successful issues search with all parameters", - mockedClient: mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.GetSearchIssues, - expectQueryParams( - t, - map[string]string{ - "q": "is:issue repo:owner/repo is:open", - "sort": "created", - "order": "desc", - "page": "1", - "per_page": "30", - }, - ).andThen( - mockResponse(t, http.StatusOK, mockSearchResult), - ), - ), - ), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{GetSearchIssues: expectQueryParams( + t, + map[string]string{ + "q": "is:issue repo:owner/repo is:open", + "sort": "created", + "order": "desc", + "page": "1", + "per_page": "30", + }, + ).andThen( + mockResponse(t, http.StatusOK, mockSearchResult), + )}), requestArgs: map[string]interface{}{ "query": "repo:owner/repo is:open", "sort": "created", @@ -562,23 +547,18 @@ func Test_SearchIssues(t *testing.T) { }, { name: "issues search with owner and repo parameters", - mockedClient: mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.GetSearchIssues, - expectQueryParams( - t, - map[string]string{ - "q": "repo:test-owner/test-repo is:issue is:open", - "sort": "created", - "order": "asc", - "page": "1", - "per_page": "30", - }, - ).andThen( - mockResponse(t, http.StatusOK, mockSearchResult), - ), - ), - ), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{GetSearchIssues: expectQueryParams( + t, + map[string]string{ + "q": "repo:test-owner/test-repo is:issue is:open", + "sort": "created", + "order": "asc", + "page": "1", + "per_page": "30", + }, + ).andThen( + mockResponse(t, http.StatusOK, mockSearchResult), + )}), requestArgs: map[string]interface{}{ "query": "is:open", "owner": "test-owner", @@ -591,21 +571,16 @@ func Test_SearchIssues(t *testing.T) { }, { name: "issues search with only owner parameter (should ignore it)", - mockedClient: mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.GetSearchIssues, - expectQueryParams( - t, - map[string]string{ - "q": "is:issue bug", - "page": "1", - "per_page": "30", - }, - ).andThen( - mockResponse(t, http.StatusOK, mockSearchResult), - ), - ), - ), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{GetSearchIssues: expectQueryParams( + t, + map[string]string{ + "q": "is:issue bug", + "page": "1", + "per_page": "30", + }, + ).andThen( + mockResponse(t, http.StatusOK, mockSearchResult), + )}), requestArgs: map[string]interface{}{ "query": "bug", "owner": "test-owner", @@ -615,21 +590,16 @@ func Test_SearchIssues(t *testing.T) { }, { name: "issues search with only repo parameter (should ignore it)", - mockedClient: mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.GetSearchIssues, - expectQueryParams( - t, - map[string]string{ - "q": "is:issue feature", - "page": "1", - "per_page": "30", - }, - ).andThen( - mockResponse(t, http.StatusOK, mockSearchResult), - ), - ), - ), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{GetSearchIssues: expectQueryParams( + t, + map[string]string{ + "q": "is:issue feature", + "page": "1", + "per_page": "30", + }, + ).andThen( + mockResponse(t, http.StatusOK, mockSearchResult), + )}), requestArgs: map[string]interface{}{ "query": "feature", "repo": "test-repo", @@ -638,13 +608,8 @@ func Test_SearchIssues(t *testing.T) { expectedResult: mockSearchResult, }, { - name: "issues search with minimal parameters", - mockedClient: mock.NewMockedHTTPClient( - mock.WithRequestMatch( - mock.GetSearchIssues, - mockSearchResult, - ), - ), + name: "issues search with minimal parameters", + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{GetSearchIssues: mockResponse(t, http.StatusOK, mockSearchResult)}), requestArgs: map[string]interface{}{ "query": "is:issue repo:owner/repo is:open", }, @@ -653,21 +618,16 @@ func Test_SearchIssues(t *testing.T) { }, { name: "query with existing is:issue filter - no duplication", - mockedClient: mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.GetSearchIssues, - expectQueryParams( - t, - map[string]string{ - "q": "repo:github/github-mcp-server is:issue is:open (label:critical OR label:urgent)", - "page": "1", - "per_page": "30", - }, - ).andThen( - mockResponse(t, http.StatusOK, mockSearchResult), - ), - ), - ), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{GetSearchIssues: expectQueryParams( + t, + map[string]string{ + "q": "repo:github/github-mcp-server is:issue is:open (label:critical OR label:urgent)", + "page": "1", + "per_page": "30", + }, + ).andThen( + mockResponse(t, http.StatusOK, mockSearchResult), + )}), requestArgs: map[string]interface{}{ "query": "repo:github/github-mcp-server is:issue is:open (label:critical OR label:urgent)", }, @@ -676,21 +636,16 @@ func Test_SearchIssues(t *testing.T) { }, { name: "query with existing repo: filter and conflicting owner/repo params - uses query filter", - mockedClient: mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.GetSearchIssues, - expectQueryParams( - t, - map[string]string{ - "q": "is:issue repo:github/github-mcp-server critical", - "page": "1", - "per_page": "30", - }, - ).andThen( - mockResponse(t, http.StatusOK, mockSearchResult), - ), - ), - ), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{GetSearchIssues: expectQueryParams( + t, + map[string]string{ + "q": "is:issue repo:github/github-mcp-server critical", + "page": "1", + "per_page": "30", + }, + ).andThen( + mockResponse(t, http.StatusOK, mockSearchResult), + )}), requestArgs: map[string]interface{}{ "query": "repo:github/github-mcp-server critical", "owner": "different-owner", @@ -701,21 +656,16 @@ func Test_SearchIssues(t *testing.T) { }, { name: "query with both is: and repo: filters already present", - mockedClient: mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.GetSearchIssues, - expectQueryParams( - t, - map[string]string{ - "q": "is:issue repo:octocat/Hello-World bug", - "page": "1", - "per_page": "30", - }, - ).andThen( - mockResponse(t, http.StatusOK, mockSearchResult), - ), - ), - ), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{GetSearchIssues: expectQueryParams( + t, + map[string]string{ + "q": "is:issue repo:octocat/Hello-World bug", + "page": "1", + "per_page": "30", + }, + ).andThen( + mockResponse(t, http.StatusOK, mockSearchResult), + )}), requestArgs: map[string]interface{}{ "query": "is:issue repo:octocat/Hello-World bug", }, @@ -724,21 +674,16 @@ func Test_SearchIssues(t *testing.T) { }, { name: "complex query with multiple OR operators and existing filters", - mockedClient: mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.GetSearchIssues, - expectQueryParams( - t, - map[string]string{ - "q": "repo:github/github-mcp-server is:issue (label:critical OR label:urgent OR label:high-priority OR label:blocker)", - "page": "1", - "per_page": "30", - }, - ).andThen( - mockResponse(t, http.StatusOK, mockSearchResult), - ), - ), - ), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{GetSearchIssues: expectQueryParams( + t, + map[string]string{ + "q": "repo:github/github-mcp-server is:issue (label:critical OR label:urgent OR label:high-priority OR label:blocker)", + "page": "1", + "per_page": "30", + }, + ).andThen( + mockResponse(t, http.StatusOK, mockSearchResult), + )}), requestArgs: map[string]interface{}{ "query": "repo:github/github-mcp-server is:issue (label:critical OR label:urgent OR label:high-priority OR label:blocker)", }, @@ -747,15 +692,10 @@ func Test_SearchIssues(t *testing.T) { }, { name: "search issues fails", - mockedClient: mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.GetSearchIssues, - http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { - w.WriteHeader(http.StatusBadRequest) - _, _ = w.Write([]byte(`{"message": "Validation Failed"}`)) - }), - ), - ), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{GetSearchIssues: http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(http.StatusBadRequest) + _, _ = w.Write([]byte(`{"message": "Validation Failed"}`)) + })}), requestArgs: map[string]interface{}{ "query": "invalid:query", }, @@ -855,21 +795,16 @@ func Test_CreateIssue(t *testing.T) { }{ { name: "successful issue creation with all fields", - mockedClient: mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.PostReposIssuesByOwnerByRepo, - expectRequestBody(t, map[string]any{ - "title": "Test Issue", - "body": "This is a test issue", - "labels": []any{"bug", "help wanted"}, - "assignees": []any{"user1", "user2"}, - "milestone": float64(5), - "type": "Bug", - }).andThen( - mockResponse(t, http.StatusCreated, mockIssue), - ), - ), - ), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{PostReposIssuesByOwnerByRepo: expectRequestBody(t, map[string]any{ + "title": "Test Issue", + "body": "This is a test issue", + "labels": []any{"bug", "help wanted"}, + "assignees": []any{"user1", "user2"}, + "milestone": float64(5), + "type": "Bug", + }).andThen( + mockResponse(t, http.StatusCreated, mockIssue), + )}), requestArgs: map[string]interface{}{ "method": "create", "owner": "owner", @@ -886,17 +821,12 @@ func Test_CreateIssue(t *testing.T) { }, { name: "successful issue creation with minimal fields", - mockedClient: mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.PostReposIssuesByOwnerByRepo, - mockResponse(t, http.StatusCreated, &github.Issue{ - Number: github.Ptr(124), - Title: github.Ptr("Minimal Issue"), - HTMLURL: github.Ptr("https://github.com/owner/repo/issues/124"), - State: github.Ptr("open"), - }), - ), - ), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{PostReposIssuesByOwnerByRepo: mockResponse(t, http.StatusCreated, &github.Issue{ + Number: github.Ptr(124), + Title: github.Ptr("Minimal Issue"), + HTMLURL: github.Ptr("https://github.com/owner/repo/issues/124"), + State: github.Ptr("open"), + })}), requestArgs: map[string]interface{}{ "method": "create", "owner": "owner", @@ -914,15 +844,10 @@ func Test_CreateIssue(t *testing.T) { }, { name: "issue creation fails", - mockedClient: mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.PostReposIssuesByOwnerByRepo, - http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { - w.WriteHeader(http.StatusUnprocessableEntity) - _, _ = w.Write([]byte(`{"message": "Validation failed"}`)) - }), - ), - ), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{PostReposIssuesByOwnerByRepo: http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(http.StatusUnprocessableEntity) + _, _ = w.Write([]byte(`{"message": "Validation failed"}`)) + })}), requestArgs: map[string]interface{}{ "method": "create", "owner": "owner", @@ -1414,18 +1339,13 @@ func Test_UpdateIssue(t *testing.T) { }{ { name: "partial update of non-state fields only", - mockedRESTClient: mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.PatchReposIssuesByOwnerByRepoByIssueNumber, - expectRequestBody(t, map[string]interface{}{ - "title": "Updated Title", - "body": "Updated Description", - }).andThen( - mockResponse(t, http.StatusOK, mockUpdatedIssue), - ), - ), - ), - mockedGQLClient: githubv4MockHTTPClientWithHandlers(map[string]http.HandlerFunc{}), + mockedRESTClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{PatchReposIssuesByOwnerByRepoByIssueNumber: expectRequestBody(t, map[string]interface{}{ + "title": "Updated Title", + "body": "Updated Description", + }).andThen( + mockResponse(t, http.StatusOK, mockUpdatedIssue), + )}), + mockedGQLClient: nil, requestArgs: map[string]interface{}{ "method": "update", "owner": "owner", @@ -1439,16 +1359,11 @@ func Test_UpdateIssue(t *testing.T) { }, { name: "issue not found when updating non-state fields only", - mockedRESTClient: mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.PatchReposIssuesByOwnerByRepoByIssueNumber, - http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { - w.WriteHeader(http.StatusNotFound) - _, _ = w.Write([]byte(`{"message": "Not Found"}`)) - }), - ), - ), - mockedGQLClient: githubv4MockHTTPClientWithHandlers(map[string]http.HandlerFunc{}), + mockedRESTClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{PatchReposIssuesByOwnerByRepoByIssueNumber: http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(http.StatusNotFound) + _, _ = w.Write([]byte(`{"message": "Not Found"}`)) + })}), + mockedGQLClient: nil, requestArgs: map[string]interface{}{ "method": "update", "owner": "owner", @@ -1460,13 +1375,8 @@ func Test_UpdateIssue(t *testing.T) { expectedErrMsg: "failed to update issue", }, { - name: "close issue as duplicate", - mockedRESTClient: mock.NewMockedHTTPClient( - mock.WithRequestMatch( - mock.PatchReposIssuesByOwnerByRepoByIssueNumber, - mockBaseIssue, - ), - ), + name: "close issue as duplicate", + mockedRESTClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{PatchReposIssuesByOwnerByRepoByIssueNumber: mockResponse(t, http.StatusOK, mockBaseIssue)}), mockedGQLClient: githubv4mock.NewMockedHTTPClient( githubv4mock.NewQueryMatcher( struct { @@ -1520,13 +1430,8 @@ func Test_UpdateIssue(t *testing.T) { expectedIssue: mockUpdatedIssue, }, { - name: "reopen issue", - mockedRESTClient: mock.NewMockedHTTPClient( - mock.WithRequestMatch( - mock.PatchReposIssuesByOwnerByRepoByIssueNumber, - mockBaseIssue, - ), - ), + name: "reopen issue", + mockedRESTClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{PatchReposIssuesByOwnerByRepoByIssueNumber: mockResponse(t, http.StatusOK, mockBaseIssue)}), mockedGQLClient: githubv4mock.NewMockedHTTPClient( githubv4mock.NewQueryMatcher( struct { @@ -1572,13 +1477,8 @@ func Test_UpdateIssue(t *testing.T) { expectedIssue: mockReopenedIssue, }, { - name: "main issue not found when trying to close it", - mockedRESTClient: mock.NewMockedHTTPClient( - mock.WithRequestMatch( - mock.PatchReposIssuesByOwnerByRepoByIssueNumber, - mockBaseIssue, - ), - ), + name: "main issue not found when trying to close it", + mockedRESTClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{PatchReposIssuesByOwnerByRepoByIssueNumber: mockResponse(t, http.StatusOK, mockBaseIssue)}), mockedGQLClient: githubv4mock.NewMockedHTTPClient( githubv4mock.NewQueryMatcher( struct { @@ -1608,13 +1508,8 @@ func Test_UpdateIssue(t *testing.T) { expectedErrMsg: "Failed to find issues", }, { - name: "duplicate issue not found when closing as duplicate", - mockedRESTClient: mock.NewMockedHTTPClient( - mock.WithRequestMatch( - mock.PatchReposIssuesByOwnerByRepoByIssueNumber, - mockBaseIssue, - ), - ), + name: "duplicate issue not found when closing as duplicate", + mockedRESTClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{PatchReposIssuesByOwnerByRepoByIssueNumber: mockResponse(t, http.StatusOK, mockBaseIssue)}), mockedGQLClient: githubv4mock.NewMockedHTTPClient( githubv4mock.NewQueryMatcher( struct { @@ -1650,31 +1545,26 @@ func Test_UpdateIssue(t *testing.T) { }, { name: "close as duplicate with combined non-state updates", - mockedRESTClient: mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.PatchReposIssuesByOwnerByRepoByIssueNumber, - expectRequestBody(t, map[string]interface{}{ - "title": "Updated Title", - "body": "Updated Description", - "labels": []any{"bug", "priority"}, - "assignees": []any{"assignee1", "assignee2"}, - "milestone": float64(5), - "type": "Bug", - }).andThen( - mockResponse(t, http.StatusOK, &github.Issue{ - Number: github.Ptr(123), - Title: github.Ptr("Updated Title"), - Body: github.Ptr("Updated Description"), - Labels: []*github.Label{{Name: github.Ptr("bug")}, {Name: github.Ptr("priority")}}, - Assignees: []*github.User{{Login: github.Ptr("assignee1")}, {Login: github.Ptr("assignee2")}}, - Milestone: &github.Milestone{Number: github.Ptr(5)}, - Type: &github.IssueType{Name: github.Ptr("Bug")}, - State: github.Ptr("open"), // Still open after REST update - HTMLURL: github.Ptr("https://github.com/owner/repo/issues/123"), - }), - ), - ), - ), + mockedRESTClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{PatchReposIssuesByOwnerByRepoByIssueNumber: expectRequestBody(t, map[string]interface{}{ + "title": "Updated Title", + "body": "Updated Description", + "labels": []any{"bug", "priority"}, + "assignees": []any{"assignee1", "assignee2"}, + "milestone": float64(5), + "type": "Bug", + }).andThen( + mockResponse(t, http.StatusOK, &github.Issue{ + Number: github.Ptr(123), + Title: github.Ptr("Updated Title"), + Body: github.Ptr("Updated Description"), + Labels: []*github.Label{{Name: github.Ptr("bug")}, {Name: github.Ptr("priority")}}, + Assignees: []*github.User{{Login: github.Ptr("assignee1")}, {Login: github.Ptr("assignee2")}}, + Milestone: &github.Milestone{Number: github.Ptr(5)}, + Type: &github.IssueType{Name: github.Ptr("Bug")}, + State: github.Ptr("open"), // Still open after REST update + HTMLURL: github.Ptr("https://github.com/owner/repo/issues/123"), + }), + )}), mockedGQLClient: githubv4mock.NewMockedHTTPClient( githubv4mock.NewQueryMatcher( struct { @@ -1736,7 +1626,7 @@ func Test_UpdateIssue(t *testing.T) { { name: "duplicate_of without duplicate state_reason should fail", mockedRESTClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{}), - mockedGQLClient: githubv4MockHTTPClientWithHandlers(map[string]http.HandlerFunc{}), + mockedGQLClient: nil, requestArgs: map[string]interface{}{ "method": "update", "owner": "owner", @@ -1896,13 +1786,8 @@ func Test_GetIssueComments(t *testing.T) { lockdownEnabled bool }{ { - name: "successful comments retrieval", - mockedClient: mock.NewMockedHTTPClient( - mock.WithRequestMatch( - mock.GetReposIssuesCommentsByOwnerByRepoByIssueNumber, - mockComments, - ), - ), + name: "successful comments retrieval", + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{GetReposIssuesCommentsByOwnerByRepoByIssueNumber: mockResponse(t, http.StatusOK, mockComments)}), requestArgs: map[string]interface{}{ "method": "get_comments", "owner": "owner", @@ -1914,17 +1799,12 @@ func Test_GetIssueComments(t *testing.T) { }, { name: "successful comments retrieval with pagination", - mockedClient: mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.GetReposIssuesCommentsByOwnerByRepoByIssueNumber, - expectQueryParams(t, map[string]string{ - "page": "2", - "per_page": "10", - }).andThen( - mockResponse(t, http.StatusOK, mockComments), - ), - ), - ), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{GetReposIssuesCommentsByOwnerByRepoByIssueNumber: expectQueryParams(t, map[string]string{ + "page": "2", + "per_page": "10", + }).andThen( + mockResponse(t, http.StatusOK, mockComments), + )}), requestArgs: map[string]interface{}{ "method": "get_comments", "owner": "owner", @@ -1937,13 +1817,8 @@ func Test_GetIssueComments(t *testing.T) { expectedComments: mockComments, }, { - name: "issue not found", - mockedClient: mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.GetReposIssuesCommentsByOwnerByRepoByIssueNumber, - mockResponse(t, http.StatusNotFound, `{"message": "Issue not found"}`), - ), - ), + name: "issue not found", + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{GetReposIssuesCommentsByOwnerByRepoByIssueNumber: mockResponse(t, http.StatusNotFound, `{"message": "Issue not found"}`)}), requestArgs: map[string]interface{}{ "method": "get_comments", "owner": "owner", @@ -1955,23 +1830,18 @@ func Test_GetIssueComments(t *testing.T) { }, { name: "lockdown enabled filters comments without push access", - mockedClient: mock.NewMockedHTTPClient( - mock.WithRequestMatch( - mock.GetReposIssuesCommentsByOwnerByRepoByIssueNumber, - []*github.IssueComment{ - { - ID: github.Ptr(int64(789)), - Body: github.Ptr("Maintainer comment"), - User: &github.User{Login: github.Ptr("maintainer")}, - }, - { - ID: github.Ptr(int64(790)), - Body: github.Ptr("External user comment"), - User: &github.User{Login: github.Ptr("testuser")}, - }, - }, - ), - ), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{GetReposIssuesCommentsByOwnerByRepoByIssueNumber: mockResponse(t, http.StatusOK, []*github.IssueComment{ + { + ID: github.Ptr(int64(789)), + Body: github.Ptr("Maintainer comment"), + User: &github.User{Login: github.Ptr("maintainer")}, + }, + { + ID: github.Ptr(int64(790)), + Body: github.Ptr("External user comment"), + User: &github.User{Login: github.Ptr("testuser")}, + }, + })}), gqlHTTPClient: newRepoAccessHTTPClient(), requestArgs: map[string]interface{}{ "method": "get_comments", @@ -2617,13 +2487,8 @@ func Test_AddSubIssue(t *testing.T) { expectedErrMsg string }{ { - name: "successful sub-issue addition with all parameters", - mockedClient: mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.PostReposIssuesSubIssuesByOwnerByRepoByIssueNumber, - mockResponse(t, http.StatusCreated, mockIssue), - ), - ), + name: "successful sub-issue addition with all parameters", + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{PostReposIssuesSubIssuesByOwnerByRepoByIssueNumber: mockResponse(t, http.StatusCreated, mockIssue)}), requestArgs: map[string]interface{}{ "method": "add", "owner": "owner", @@ -2636,13 +2501,8 @@ func Test_AddSubIssue(t *testing.T) { expectedIssue: mockIssue, }, { - name: "successful sub-issue addition with minimal parameters", - mockedClient: mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.PostReposIssuesSubIssuesByOwnerByRepoByIssueNumber, - mockResponse(t, http.StatusCreated, mockIssue), - ), - ), + name: "successful sub-issue addition with minimal parameters", + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{PostReposIssuesSubIssuesByOwnerByRepoByIssueNumber: mockResponse(t, http.StatusCreated, mockIssue)}), requestArgs: map[string]interface{}{ "method": "add", "owner": "owner", @@ -2654,13 +2514,8 @@ func Test_AddSubIssue(t *testing.T) { expectedIssue: mockIssue, }, { - name: "successful sub-issue addition with replace_parent false", - mockedClient: mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.PostReposIssuesSubIssuesByOwnerByRepoByIssueNumber, - mockResponse(t, http.StatusCreated, mockIssue), - ), - ), + name: "successful sub-issue addition with replace_parent false", + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{PostReposIssuesSubIssuesByOwnerByRepoByIssueNumber: mockResponse(t, http.StatusCreated, mockIssue)}), requestArgs: map[string]interface{}{ "method": "add", "owner": "owner", @@ -2673,13 +2528,8 @@ func Test_AddSubIssue(t *testing.T) { expectedIssue: mockIssue, }, { - name: "parent issue not found", - mockedClient: mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.PostReposIssuesSubIssuesByOwnerByRepoByIssueNumber, - mockResponse(t, http.StatusNotFound, `{"message": "Parent issue not found"}`), - ), - ), + name: "parent issue not found", + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{PostReposIssuesSubIssuesByOwnerByRepoByIssueNumber: mockResponse(t, http.StatusNotFound, `{"message": "Parent issue not found"}`)}), requestArgs: map[string]interface{}{ "method": "add", "owner": "owner", @@ -2691,13 +2541,8 @@ func Test_AddSubIssue(t *testing.T) { expectedErrMsg: "failed to add sub-issue", }, { - name: "sub-issue not found", - mockedClient: mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.PostReposIssuesSubIssuesByOwnerByRepoByIssueNumber, - mockResponse(t, http.StatusNotFound, `{"message": "Sub-issue not found"}`), - ), - ), + name: "sub-issue not found", + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{PostReposIssuesSubIssuesByOwnerByRepoByIssueNumber: mockResponse(t, http.StatusNotFound, `{"message": "Sub-issue not found"}`)}), requestArgs: map[string]interface{}{ "method": "add", "owner": "owner", @@ -2709,13 +2554,8 @@ func Test_AddSubIssue(t *testing.T) { expectedErrMsg: "failed to add sub-issue", }, { - name: "validation failed - sub-issue cannot be parent of itself", - mockedClient: mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.PostReposIssuesSubIssuesByOwnerByRepoByIssueNumber, - mockResponse(t, http.StatusUnprocessableEntity, `{"message": "Validation failed", "errors": [{"message": "Sub-issue cannot be a parent of itself"}]}`), - ), - ), + name: "validation failed - sub-issue cannot be parent of itself", + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{PostReposIssuesSubIssuesByOwnerByRepoByIssueNumber: mockResponse(t, http.StatusUnprocessableEntity, `{"message": "Validation failed", "errors": [{"message": "Sub-issue cannot be a parent of itself"}]}`)}), requestArgs: map[string]interface{}{ "method": "add", "owner": "owner", @@ -2727,13 +2567,8 @@ func Test_AddSubIssue(t *testing.T) { expectedErrMsg: "failed to add sub-issue", }, { - name: "insufficient permissions", - mockedClient: mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.PostReposIssuesSubIssuesByOwnerByRepoByIssueNumber, - mockResponse(t, http.StatusForbidden, `{"message": "Must have write access to repository"}`), - ), - ), + name: "insufficient permissions", + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{PostReposIssuesSubIssuesByOwnerByRepoByIssueNumber: mockResponse(t, http.StatusForbidden, `{"message": "Must have write access to repository"}`)}), requestArgs: map[string]interface{}{ "method": "add", "owner": "owner", @@ -2746,9 +2581,7 @@ func Test_AddSubIssue(t *testing.T) { }, { name: "missing required parameter owner", - mockedClient: mock.NewMockedHTTPClient( - // No mocked requests needed since validation fails before HTTP call - ), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{}), requestArgs: map[string]interface{}{ "method": "add", "repo": "repo", @@ -2760,9 +2593,7 @@ func Test_AddSubIssue(t *testing.T) { }, { name: "missing required parameter sub_issue_id", - mockedClient: mock.NewMockedHTTPClient( - // No mocked requests needed since validation fails before HTTP call - ), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{}), requestArgs: map[string]interface{}{ "method": "add", "owner": "owner", @@ -2881,13 +2712,8 @@ func Test_GetSubIssues(t *testing.T) { expectedErrMsg string }{ { - name: "successful sub-issues listing with minimal parameters", - mockedClient: mock.NewMockedHTTPClient( - mock.WithRequestMatch( - mock.GetReposIssuesSubIssuesByOwnerByRepoByIssueNumber, - mockSubIssues, - ), - ), + name: "successful sub-issues listing with minimal parameters", + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{GetReposIssuesSubIssuesByOwnerByRepoByIssueNumber: mockResponse(t, http.StatusOK, mockSubIssues)}), requestArgs: map[string]interface{}{ "method": "get_sub_issues", "owner": "owner", @@ -2899,17 +2725,12 @@ func Test_GetSubIssues(t *testing.T) { }, { name: "successful sub-issues listing with pagination", - mockedClient: mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.GetReposIssuesSubIssuesByOwnerByRepoByIssueNumber, - expectQueryParams(t, map[string]string{ - "page": "2", - "per_page": "10", - }).andThen( - mockResponse(t, http.StatusOK, mockSubIssues), - ), - ), - ), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{GetReposIssuesSubIssuesByOwnerByRepoByIssueNumber: expectQueryParams(t, map[string]string{ + "page": "2", + "per_page": "10", + }).andThen( + mockResponse(t, http.StatusOK, mockSubIssues), + )}), requestArgs: map[string]interface{}{ "method": "get_sub_issues", "owner": "owner", @@ -2922,13 +2743,8 @@ func Test_GetSubIssues(t *testing.T) { expectedSubIssues: mockSubIssues, }, { - name: "successful sub-issues listing with empty result", - mockedClient: mock.NewMockedHTTPClient( - mock.WithRequestMatch( - mock.GetReposIssuesSubIssuesByOwnerByRepoByIssueNumber, - []*github.Issue{}, - ), - ), + name: "successful sub-issues listing with empty result", + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{GetReposIssuesSubIssuesByOwnerByRepoByIssueNumber: mockResponse(t, http.StatusOK, []*github.Issue{})}), requestArgs: map[string]interface{}{ "method": "get_sub_issues", "owner": "owner", @@ -2939,13 +2755,8 @@ func Test_GetSubIssues(t *testing.T) { expectedSubIssues: []*github.Issue{}, }, { - name: "parent issue not found", - mockedClient: mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.GetReposIssuesSubIssuesByOwnerByRepoByIssueNumber, - mockResponse(t, http.StatusNotFound, `{"message": "Not Found"}`), - ), - ), + name: "parent issue not found", + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{GetReposIssuesSubIssuesByOwnerByRepoByIssueNumber: mockResponse(t, http.StatusNotFound, `{"message": "Not Found"}`)}), requestArgs: map[string]interface{}{ "method": "get_sub_issues", "owner": "owner", @@ -2956,13 +2767,8 @@ func Test_GetSubIssues(t *testing.T) { expectedErrMsg: "failed to list sub-issues", }, { - name: "repository not found", - mockedClient: mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.GetReposIssuesSubIssuesByOwnerByRepoByIssueNumber, - mockResponse(t, http.StatusNotFound, `{"message": "Not Found"}`), - ), - ), + name: "repository not found", + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{GetReposIssuesSubIssuesByOwnerByRepoByIssueNumber: mockResponse(t, http.StatusNotFound, `{"message": "Not Found"}`)}), requestArgs: map[string]interface{}{ "method": "get_sub_issues", "owner": "nonexistent", @@ -2973,13 +2779,8 @@ func Test_GetSubIssues(t *testing.T) { expectedErrMsg: "failed to list sub-issues", }, { - name: "sub-issues feature gone/deprecated", - mockedClient: mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.GetReposIssuesSubIssuesByOwnerByRepoByIssueNumber, - mockResponse(t, http.StatusGone, `{"message": "This feature has been deprecated"}`), - ), - ), + name: "sub-issues feature gone/deprecated", + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{GetReposIssuesSubIssuesByOwnerByRepoByIssueNumber: mockResponse(t, http.StatusGone, `{"message": "This feature has been deprecated"}`)}), requestArgs: map[string]interface{}{ "method": "get_sub_issues", "owner": "owner", @@ -2991,9 +2792,7 @@ func Test_GetSubIssues(t *testing.T) { }, { name: "missing required parameter owner", - mockedClient: mock.NewMockedHTTPClient( - // No mocked requests needed since validation fails before HTTP call - ), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{}), requestArgs: map[string]interface{}{ "method": "get_sub_issues", "repo": "repo", @@ -3004,9 +2803,7 @@ func Test_GetSubIssues(t *testing.T) { }, { name: "missing required parameter issue_number", - mockedClient: mock.NewMockedHTTPClient( - // No mocked requests needed since validation fails before HTTP call - ), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{}), requestArgs: map[string]interface{}{ "method": "get_sub_issues", "owner": "owner", @@ -3121,13 +2918,8 @@ func Test_RemoveSubIssue(t *testing.T) { expectedErrMsg string }{ { - name: "successful sub-issue removal", - mockedClient: mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.DeleteReposIssuesSubIssueByOwnerByRepoByIssueNumber, - mockResponse(t, http.StatusOK, mockIssue), - ), - ), + name: "successful sub-issue removal", + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{DeleteReposIssuesSubIssueByOwnerByRepoByIssueNumber: mockResponse(t, http.StatusOK, mockIssue)}), requestArgs: map[string]interface{}{ "method": "remove", "owner": "owner", @@ -3139,13 +2931,8 @@ func Test_RemoveSubIssue(t *testing.T) { expectedIssue: mockIssue, }, { - name: "parent issue not found", - mockedClient: mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.DeleteReposIssuesSubIssueByOwnerByRepoByIssueNumber, - mockResponse(t, http.StatusNotFound, `{"message": "Not Found"}`), - ), - ), + name: "parent issue not found", + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{DeleteReposIssuesSubIssueByOwnerByRepoByIssueNumber: mockResponse(t, http.StatusNotFound, `{"message": "Not Found"}`)}), requestArgs: map[string]interface{}{ "method": "remove", "owner": "owner", @@ -3157,13 +2944,8 @@ func Test_RemoveSubIssue(t *testing.T) { expectedErrMsg: "failed to remove sub-issue", }, { - name: "sub-issue not found", - mockedClient: mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.DeleteReposIssuesSubIssueByOwnerByRepoByIssueNumber, - mockResponse(t, http.StatusNotFound, `{"message": "Sub-issue not found"}`), - ), - ), + name: "sub-issue not found", + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{DeleteReposIssuesSubIssueByOwnerByRepoByIssueNumber: mockResponse(t, http.StatusNotFound, `{"message": "Sub-issue not found"}`)}), requestArgs: map[string]interface{}{ "method": "remove", "owner": "owner", @@ -3175,13 +2957,8 @@ func Test_RemoveSubIssue(t *testing.T) { expectedErrMsg: "failed to remove sub-issue", }, { - name: "bad request - invalid sub_issue_id", - mockedClient: mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.DeleteReposIssuesSubIssueByOwnerByRepoByIssueNumber, - mockResponse(t, http.StatusBadRequest, `{"message": "Invalid sub_issue_id"}`), - ), - ), + name: "bad request - invalid sub_issue_id", + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{DeleteReposIssuesSubIssueByOwnerByRepoByIssueNumber: mockResponse(t, http.StatusBadRequest, `{"message": "Invalid sub_issue_id"}`)}), requestArgs: map[string]interface{}{ "method": "remove", "owner": "owner", @@ -3193,13 +2970,8 @@ func Test_RemoveSubIssue(t *testing.T) { expectedErrMsg: "failed to remove sub-issue", }, { - name: "repository not found", - mockedClient: mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.DeleteReposIssuesSubIssueByOwnerByRepoByIssueNumber, - mockResponse(t, http.StatusNotFound, `{"message": "Not Found"}`), - ), - ), + name: "repository not found", + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{DeleteReposIssuesSubIssueByOwnerByRepoByIssueNumber: mockResponse(t, http.StatusNotFound, `{"message": "Not Found"}`)}), requestArgs: map[string]interface{}{ "method": "remove", "owner": "nonexistent", @@ -3211,13 +2983,8 @@ func Test_RemoveSubIssue(t *testing.T) { expectedErrMsg: "failed to remove sub-issue", }, { - name: "insufficient permissions", - mockedClient: mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.DeleteReposIssuesSubIssueByOwnerByRepoByIssueNumber, - mockResponse(t, http.StatusForbidden, `{"message": "Must have write access to repository"}`), - ), - ), + name: "insufficient permissions", + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{DeleteReposIssuesSubIssueByOwnerByRepoByIssueNumber: mockResponse(t, http.StatusForbidden, `{"message": "Must have write access to repository"}`)}), requestArgs: map[string]interface{}{ "method": "remove", "owner": "owner", @@ -3230,9 +2997,7 @@ func Test_RemoveSubIssue(t *testing.T) { }, { name: "missing required parameter owner", - mockedClient: mock.NewMockedHTTPClient( - // No mocked requests needed since validation fails before HTTP call - ), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{}), requestArgs: map[string]interface{}{ "method": "remove", "repo": "repo", @@ -3244,9 +3009,7 @@ func Test_RemoveSubIssue(t *testing.T) { }, { name: "missing required parameter sub_issue_id", - mockedClient: mock.NewMockedHTTPClient( - // No mocked requests needed since validation fails before HTTP call - ), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{}), requestArgs: map[string]interface{}{ "method": "remove", "owner": "owner", @@ -3351,13 +3114,8 @@ func Test_ReprioritizeSubIssue(t *testing.T) { expectedErrMsg string }{ { - name: "successful reprioritization with after_id", - mockedClient: mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.PatchReposIssuesSubIssuesPriorityByOwnerByRepoByIssueNumber, - mockResponse(t, http.StatusOK, mockIssue), - ), - ), + name: "successful reprioritization with after_id", + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{PatchReposIssuesSubIssuesPriorityByOwnerByRepoByIssueNumber: mockResponse(t, http.StatusOK, mockIssue)}), requestArgs: map[string]interface{}{ "method": "reprioritize", "owner": "owner", @@ -3370,13 +3128,8 @@ func Test_ReprioritizeSubIssue(t *testing.T) { expectedIssue: mockIssue, }, { - name: "successful reprioritization with before_id", - mockedClient: mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.PatchReposIssuesSubIssuesPriorityByOwnerByRepoByIssueNumber, - mockResponse(t, http.StatusOK, mockIssue), - ), - ), + name: "successful reprioritization with before_id", + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{PatchReposIssuesSubIssuesPriorityByOwnerByRepoByIssueNumber: mockResponse(t, http.StatusOK, mockIssue)}), requestArgs: map[string]interface{}{ "method": "reprioritize", "owner": "owner", @@ -3390,9 +3143,7 @@ func Test_ReprioritizeSubIssue(t *testing.T) { }, { name: "validation error - neither after_id nor before_id specified", - mockedClient: mock.NewMockedHTTPClient( - // No mocked requests needed since validation fails before HTTP call - ), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{}), requestArgs: map[string]interface{}{ "method": "reprioritize", "owner": "owner", @@ -3405,9 +3156,7 @@ func Test_ReprioritizeSubIssue(t *testing.T) { }, { name: "validation error - both after_id and before_id specified", - mockedClient: mock.NewMockedHTTPClient( - // No mocked requests needed since validation fails before HTTP call - ), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{}), requestArgs: map[string]interface{}{ "method": "reprioritize", "owner": "owner", @@ -3421,13 +3170,8 @@ func Test_ReprioritizeSubIssue(t *testing.T) { expectedErrMsg: "only one of after_id or before_id should be specified, not both", }, { - name: "parent issue not found", - mockedClient: mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.PatchReposIssuesSubIssuesPriorityByOwnerByRepoByIssueNumber, - mockResponse(t, http.StatusNotFound, `{"message": "Not Found"}`), - ), - ), + name: "parent issue not found", + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{PatchReposIssuesSubIssuesPriorityByOwnerByRepoByIssueNumber: mockResponse(t, http.StatusNotFound, `{"message": "Not Found"}`)}), requestArgs: map[string]interface{}{ "method": "reprioritize", "owner": "owner", @@ -3440,13 +3184,8 @@ func Test_ReprioritizeSubIssue(t *testing.T) { expectedErrMsg: "failed to reprioritize sub-issue", }, { - name: "sub-issue not found", - mockedClient: mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.PatchReposIssuesSubIssuesPriorityByOwnerByRepoByIssueNumber, - mockResponse(t, http.StatusNotFound, `{"message": "Sub-issue not found"}`), - ), - ), + name: "sub-issue not found", + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{PatchReposIssuesSubIssuesPriorityByOwnerByRepoByIssueNumber: mockResponse(t, http.StatusNotFound, `{"message": "Sub-issue not found"}`)}), requestArgs: map[string]interface{}{ "method": "reprioritize", "owner": "owner", @@ -3459,13 +3198,8 @@ func Test_ReprioritizeSubIssue(t *testing.T) { expectedErrMsg: "failed to reprioritize sub-issue", }, { - name: "validation failed - positioning sub-issue not found", - mockedClient: mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.PatchReposIssuesSubIssuesPriorityByOwnerByRepoByIssueNumber, - mockResponse(t, http.StatusUnprocessableEntity, `{"message": "Validation failed", "errors": [{"message": "Positioning sub-issue not found"}]}`), - ), - ), + name: "validation failed - positioning sub-issue not found", + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{PatchReposIssuesSubIssuesPriorityByOwnerByRepoByIssueNumber: mockResponse(t, http.StatusUnprocessableEntity, `{"message": "Validation failed", "errors": [{"message": "Positioning sub-issue not found"}]}`)}), requestArgs: map[string]interface{}{ "method": "reprioritize", "owner": "owner", @@ -3478,13 +3212,8 @@ func Test_ReprioritizeSubIssue(t *testing.T) { expectedErrMsg: "failed to reprioritize sub-issue", }, { - name: "insufficient permissions", - mockedClient: mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.PatchReposIssuesSubIssuesPriorityByOwnerByRepoByIssueNumber, - mockResponse(t, http.StatusForbidden, `{"message": "Must have write access to repository"}`), - ), - ), + name: "insufficient permissions", + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{PatchReposIssuesSubIssuesPriorityByOwnerByRepoByIssueNumber: mockResponse(t, http.StatusForbidden, `{"message": "Must have write access to repository"}`)}), requestArgs: map[string]interface{}{ "method": "reprioritize", "owner": "owner", @@ -3497,13 +3226,8 @@ func Test_ReprioritizeSubIssue(t *testing.T) { expectedErrMsg: "failed to reprioritize sub-issue", }, { - name: "service unavailable", - mockedClient: mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.PatchReposIssuesSubIssuesPriorityByOwnerByRepoByIssueNumber, - mockResponse(t, http.StatusServiceUnavailable, `{"message": "Service Unavailable"}`), - ), - ), + name: "service unavailable", + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{PatchReposIssuesSubIssuesPriorityByOwnerByRepoByIssueNumber: mockResponse(t, http.StatusServiceUnavailable, `{"message": "Service Unavailable"}`)}), requestArgs: map[string]interface{}{ "method": "reprioritize", "owner": "owner", @@ -3517,9 +3241,7 @@ func Test_ReprioritizeSubIssue(t *testing.T) { }, { name: "missing required parameter owner", - mockedClient: mock.NewMockedHTTPClient( - // No mocked requests needed since validation fails before HTTP call - ), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{}), requestArgs: map[string]interface{}{ "method": "reprioritize", "repo": "repo", @@ -3532,9 +3254,7 @@ func Test_ReprioritizeSubIssue(t *testing.T) { }, { name: "missing required parameter sub_issue_id", - mockedClient: mock.NewMockedHTTPClient( - // No mocked requests needed since validation fails before HTTP call - ), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{}), requestArgs: map[string]interface{}{ "method": "reprioritize", "owner": "owner", @@ -3632,15 +3352,9 @@ func Test_ListIssueTypes(t *testing.T) { }{ { name: "successful issue types retrieval", - mockedClient: mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.EndpointPattern{ - Pattern: "/orgs/testorg/issue-types", - Method: "GET", - }, - mockResponse(t, http.StatusOK, mockIssueTypes), - ), - ), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ + "GET /orgs/testorg/issue-types": mockResponse(t, http.StatusOK, mockIssueTypes), + }), requestArgs: map[string]interface{}{ "owner": "testorg", }, @@ -3649,15 +3363,9 @@ func Test_ListIssueTypes(t *testing.T) { }, { name: "organization not found", - mockedClient: mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.EndpointPattern{ - Pattern: "/orgs/nonexistent/issue-types", - Method: "GET", - }, - mockResponse(t, http.StatusNotFound, `{"message": "Organization not found"}`), - ), - ), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ + "GET /orgs/nonexistent/issue-types": mockResponse(t, http.StatusNotFound, `{"message": "Organization not found"}`), + }), requestArgs: map[string]interface{}{ "owner": "nonexistent", }, @@ -3666,15 +3374,9 @@ func Test_ListIssueTypes(t *testing.T) { }, { name: "missing owner parameter", - mockedClient: mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.EndpointPattern{ - Pattern: "/orgs/testorg/issue-types", - Method: "GET", - }, - mockResponse(t, http.StatusOK, mockIssueTypes), - ), - ), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ + "GET /orgs/testorg/issue-types": mockResponse(t, http.StatusOK, mockIssueTypes), + }), requestArgs: map[string]interface{}{}, expectError: false, // This should be handled by parameter validation, error returned in result expectedErrMsg: "missing required parameter: owner", diff --git a/pkg/github/projects_test.go b/pkg/github/projects_test.go index 404ced19a..c0ce5dcf5 100644 --- a/pkg/github/projects_test.go +++ b/pkg/github/projects_test.go @@ -44,15 +44,13 @@ func Test_ListProjects(t *testing.T) { }{ { name: "success organization", - mockedClient: mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.EndpointPattern{Pattern: "/orgs/{org}/projectsV2", Method: http.MethodGet}, - http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { - w.WriteHeader(http.StatusOK) - _, _ = w.Write(mock.MustMarshal(orgProjects)) - }), - ), - ), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ + GetOrgsProjectsV2ByOrg: http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(http.StatusOK) + data, _ := json.Marshal(orgProjects) + _, _ = w.Write(data) + }), + }), requestArgs: map[string]interface{}{ "owner": "octo-org", "owner_type": "org", @@ -62,15 +60,13 @@ func Test_ListProjects(t *testing.T) { }, { name: "success user", - mockedClient: mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.EndpointPattern{Pattern: "/users/{username}/projectsV2", Method: http.MethodGet}, - http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { - w.WriteHeader(http.StatusOK) - _, _ = w.Write(mock.MustMarshal(userProjects)) - }), - ), - ), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ + GetUsersProjectsV2ByUsername: http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(http.StatusOK) + data, _ := json.Marshal(userProjects) + _, _ = w.Write(data) + }), + }), requestArgs: map[string]interface{}{ "owner": "octocat", "owner_type": "user", @@ -80,21 +76,19 @@ func Test_ListProjects(t *testing.T) { }, { name: "success organization with pagination & query", - mockedClient: mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.EndpointPattern{Pattern: "/orgs/{org}/projectsV2", Method: http.MethodGet}, - http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - q := r.URL.Query() - if q.Get("per_page") == "50" && q.Get("q") == "roadmap" { - w.WriteHeader(http.StatusOK) - _, _ = w.Write(mock.MustMarshal(orgProjects)) - return - } - w.WriteHeader(http.StatusBadRequest) - _, _ = w.Write([]byte(`{"message":"unexpected query params"}`)) - }), - ), - ), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ + GetOrgsProjectsV2ByOrg: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + q := r.URL.Query() + if q.Get("per_page") == "50" && q.Get("q") == "roadmap" { + w.WriteHeader(http.StatusOK) + data, _ := json.Marshal(orgProjects) + _, _ = w.Write(data) + return + } + w.WriteHeader(http.StatusBadRequest) + _, _ = w.Write([]byte(`{"message":"unexpected query params"}`)) + }), + }), requestArgs: map[string]interface{}{ "owner": "octo-org", "owner_type": "org", @@ -106,12 +100,9 @@ func Test_ListProjects(t *testing.T) { }, { name: "api error", - mockedClient: mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.EndpointPattern{Pattern: "/orgs/{org}/projectsV2", Method: http.MethodGet}, - mockResponse(t, http.StatusInternalServerError, map[string]string{"message": "boom"}), - ), - ), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ + GetOrgsProjectsV2ByOrg: mockResponse(t, http.StatusInternalServerError, map[string]string{"message": "boom"}), + }), requestArgs: map[string]interface{}{ "owner": "octo-org", "owner_type": "org", @@ -203,12 +194,9 @@ func Test_GetProject(t *testing.T) { }{ { name: "success organization project fetch", - mockedClient: mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.EndpointPattern{Pattern: "/orgs/{org}/projectsV2/123", Method: http.MethodGet}, - mockResponse(t, http.StatusOK, project), - ), - ), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ + GetOrgsProjectsV2ByOrgByProjectNumber: mockResponse(t, http.StatusOK, project), + }), requestArgs: map[string]interface{}{ "project_number": float64(123), "owner": "octo-org", @@ -218,12 +206,9 @@ func Test_GetProject(t *testing.T) { }, { name: "success user project fetch", - mockedClient: mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.EndpointPattern{Pattern: "/users/{username}/projectsV2/456", Method: http.MethodGet}, - mockResponse(t, http.StatusOK, project), - ), - ), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ + GetUsersProjectsV2ByUsernameByProjectNumber: mockResponse(t, http.StatusOK, project), + }), requestArgs: map[string]interface{}{ "project_number": float64(456), "owner": "octocat", @@ -233,12 +218,9 @@ func Test_GetProject(t *testing.T) { }, { name: "api error", - mockedClient: mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.EndpointPattern{Pattern: "/orgs/{org}/projectsV2/999", Method: http.MethodGet}, - mockResponse(t, http.StatusInternalServerError, map[string]string{"message": "boom"}), - ), - ), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ + GetOrgsProjectsV2ByOrgByProjectNumber: mockResponse(t, http.StatusInternalServerError, map[string]string{"message": "boom"}), + }), requestArgs: map[string]interface{}{ "project_number": float64(999), "owner": "octo-org", @@ -342,15 +324,13 @@ func Test_ListProjectFields(t *testing.T) { }{ { name: "success organization fields", - mockedClient: mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.EndpointPattern{Pattern: "/orgs/{org}/projectsV2/{project}/fields", Method: http.MethodGet}, - http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { - w.WriteHeader(http.StatusOK) - _, _ = w.Write(mock.MustMarshal(orgFields)) - }), - ), - ), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ + GetOrgsProjectsV2FieldsByOrgByProjectNumber: http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(http.StatusOK) + data, _ := json.Marshal(orgFields) + _, _ = w.Write(data) + }), + }), requestArgs: map[string]interface{}{ "owner": "octo-org", "owner_type": "org", @@ -360,21 +340,19 @@ func Test_ListProjectFields(t *testing.T) { }, { name: "success user fields with per_page override", - mockedClient: mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.EndpointPattern{Pattern: "/users/{user}/projectsV2/{project}/fields", Method: http.MethodGet}, - http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - q := r.URL.Query() - if q.Get("per_page") == "50" { - w.WriteHeader(http.StatusOK) - _, _ = w.Write(mock.MustMarshal(userFields)) - return - } - w.WriteHeader(http.StatusBadRequest) - _, _ = w.Write([]byte(`{"message":"unexpected query params"}`)) - }), - ), - ), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ + GetUsersProjectsV2FieldsByUsernameByProjectNumber: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + q := r.URL.Query() + if q.Get("per_page") == "50" { + w.WriteHeader(http.StatusOK) + data, _ := json.Marshal(userFields) + _, _ = w.Write(data) + return + } + w.WriteHeader(http.StatusBadRequest) + _, _ = w.Write([]byte(`{"message":"unexpected query params"}`)) + }), + }), requestArgs: map[string]interface{}{ "owner": "octocat", "owner_type": "user", @@ -385,12 +363,9 @@ func Test_ListProjectFields(t *testing.T) { }, { name: "api error", - mockedClient: mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.EndpointPattern{Pattern: "/orgs/{org}/projectsV2/{project}/fields", Method: http.MethodGet}, - mockResponse(t, http.StatusInternalServerError, map[string]string{"message": "boom"}), - ), - ), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ + GetOrgsProjectsV2FieldsByOrgByProjectNumber: mockResponse(t, http.StatusInternalServerError, map[string]string{"message": "boom"}), + }), requestArgs: map[string]interface{}{ "owner": "octo-org", "owner_type": "org", @@ -499,12 +474,9 @@ func Test_GetProjectField(t *testing.T) { }{ { name: "success organization field", - mockedClient: mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.EndpointPattern{Pattern: "/orgs/{org}/projectsV2/{project}/fields/{field_id}", Method: http.MethodGet}, - mockResponse(t, http.StatusOK, orgField), - ), - ), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ + GetOrgsProjectsV2FieldsByOrgByProjectNumberByFieldID: mockResponse(t, http.StatusOK, orgField), + }), requestArgs: map[string]any{ "owner": "octo-org", "owner_type": "org", @@ -515,12 +487,9 @@ func Test_GetProjectField(t *testing.T) { }, { name: "success user field", - mockedClient: mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.EndpointPattern{Pattern: "/users/{user}/projectsV2/{project}/fields/{field_id}", Method: http.MethodGet}, - mockResponse(t, http.StatusOK, userField), - ), - ), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ + GetUsersProjectsV2FieldsByUsernameByProjectNumberByFieldID: mockResponse(t, http.StatusOK, userField), + }), requestArgs: map[string]any{ "owner": "octocat", "owner_type": "user", @@ -531,12 +500,9 @@ func Test_GetProjectField(t *testing.T) { }, { name: "api error", - mockedClient: mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.EndpointPattern{Pattern: "/orgs/{org}/projectsV2/{project}/fields/{field_id}", Method: http.MethodGet}, - mockResponse(t, http.StatusInternalServerError, map[string]string{"message": "boom"}), - ), - ), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ + GetOrgsProjectsV2FieldsByOrgByProjectNumberByFieldID: mockResponse(t, http.StatusInternalServerError, map[string]string{"message": "boom"}), + }), requestArgs: map[string]any{ "owner": "octo-org", "owner_type": "org", @@ -670,12 +636,9 @@ func Test_ListProjectItems(t *testing.T) { }{ { name: "success organization items", - mockedClient: mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.EndpointPattern{Pattern: "/orgs/{org}/projectsV2/{project}/items", Method: http.MethodGet}, - mockResponse(t, http.StatusOK, orgItems), - ), - ), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ + GetOrgsProjectsV2ItemsByOrgByProjectNumber: mockResponse(t, http.StatusOK, orgItems), + }), requestArgs: map[string]interface{}{ "owner": "octo-org", "owner_type": "org", @@ -685,21 +648,19 @@ func Test_ListProjectItems(t *testing.T) { }, { name: "success organization items with fields", - mockedClient: mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.EndpointPattern{Pattern: "/orgs/{org}/projectsV2/{project}/items", Method: http.MethodGet}, - http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - q := r.URL.Query() - if q.Get("fields") == "123,456,789" { - w.WriteHeader(http.StatusOK) - _, _ = w.Write(mock.MustMarshal(orgItems)) - return - } - w.WriteHeader(http.StatusBadRequest) - _, _ = w.Write([]byte(`{"message":"unexpected query params"}`)) - }), - ), - ), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ + GetOrgsProjectsV2ItemsByOrgByProjectNumber: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + q := r.URL.Query() + if q.Get("fields") == "123,456,789" { + w.WriteHeader(http.StatusOK) + data, _ := json.Marshal(orgItems) + _, _ = w.Write(data) + return + } + w.WriteHeader(http.StatusBadRequest) + _, _ = w.Write([]byte(`{"message":"unexpected query params"}`)) + }), + }), requestArgs: map[string]interface{}{ "owner": "octo-org", "owner_type": "org", @@ -710,12 +671,9 @@ func Test_ListProjectItems(t *testing.T) { }, { name: "success user items", - mockedClient: mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.EndpointPattern{Pattern: "/users/{user}/projectsV2/{project}/items", Method: http.MethodGet}, - mockResponse(t, http.StatusOK, userItems), - ), - ), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ + GetUsersProjectsV2ItemsByUsernameByProjectNumber: mockResponse(t, http.StatusOK, userItems), + }), requestArgs: map[string]interface{}{ "owner": "octocat", "owner_type": "user", @@ -725,21 +683,19 @@ func Test_ListProjectItems(t *testing.T) { }, { name: "success with pagination and query", - mockedClient: mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.EndpointPattern{Pattern: "/orgs/{org}/projectsV2/{project}/items", Method: http.MethodGet}, - http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - q := r.URL.Query() - if q.Get("per_page") == "50" && q.Get("q") == "bug" { - w.WriteHeader(http.StatusOK) - _, _ = w.Write(mock.MustMarshal(orgItems)) - return - } - w.WriteHeader(http.StatusBadRequest) - _, _ = w.Write([]byte(`{"message":"unexpected query params"}`)) - }), - ), - ), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ + GetOrgsProjectsV2ItemsByOrgByProjectNumber: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + q := r.URL.Query() + if q.Get("per_page") == "50" && q.Get("q") == "bug" { + w.WriteHeader(http.StatusOK) + data, _ := json.Marshal(orgItems) + _, _ = w.Write(data) + return + } + w.WriteHeader(http.StatusBadRequest) + _, _ = w.Write([]byte(`{"message":"unexpected query params"}`)) + }), + }), requestArgs: map[string]interface{}{ "owner": "octo-org", "owner_type": "org", @@ -751,12 +707,9 @@ func Test_ListProjectItems(t *testing.T) { }, { name: "api error", - mockedClient: mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.EndpointPattern{Pattern: "/orgs/{org}/projectsV2/{project}/items", Method: http.MethodGet}, - mockResponse(t, http.StatusInternalServerError, map[string]string{"message": "boom"}), - ), - ), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ + GetOrgsProjectsV2ItemsByOrgByProjectNumber: mockResponse(t, http.StatusInternalServerError, map[string]string{"message": "boom"}), + }), requestArgs: map[string]interface{}{ "owner": "octo-org", "owner_type": "org", @@ -876,12 +829,9 @@ func Test_GetProjectItem(t *testing.T) { }{ { name: "success organization item", - mockedClient: mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.EndpointPattern{Pattern: "/orgs/{org}/projectsV2/{project}/items/{item_id}", Method: http.MethodGet}, - mockResponse(t, http.StatusOK, orgItem), - ), - ), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ + GetOrgsProjectsV2ItemsByOrgByProjectNumberByItemID: mockResponse(t, http.StatusOK, orgItem), + }), requestArgs: map[string]any{ "owner": "octo-org", "owner_type": "org", @@ -892,21 +842,19 @@ func Test_GetProjectItem(t *testing.T) { }, { name: "success organization item with fields", - mockedClient: mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.EndpointPattern{Pattern: "/orgs/{org}/projectsV2/{project}/items/{item_id}", Method: http.MethodGet}, - http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - q := r.URL.Query() - if q.Get("fields") == "123,456" { - w.WriteHeader(http.StatusOK) - _, _ = w.Write(mock.MustMarshal(orgItem)) - return - } - w.WriteHeader(http.StatusBadRequest) - _, _ = w.Write([]byte(`{"message":"unexpected query params"}`)) - }), - ), - ), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ + GetOrgsProjectsV2ItemsByOrgByProjectNumberByItemID: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + q := r.URL.Query() + if q.Get("fields") == "123,456" { + w.WriteHeader(http.StatusOK) + data, _ := json.Marshal(orgItem) + _, _ = w.Write(data) + return + } + w.WriteHeader(http.StatusBadRequest) + _, _ = w.Write([]byte(`{"message":"unexpected query params"}`)) + }), + }), requestArgs: map[string]any{ "owner": "octo-org", "owner_type": "org", @@ -918,12 +866,9 @@ func Test_GetProjectItem(t *testing.T) { }, { name: "success user item", - mockedClient: mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.EndpointPattern{Pattern: "/users/{user}/projectsV2/{project}/items/{item_id}", Method: http.MethodGet}, - mockResponse(t, http.StatusOK, userItem), - ), - ), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ + GetUsersProjectsV2ItemsByUsernameByProjectNumberByItemID: mockResponse(t, http.StatusOK, userItem), + }), requestArgs: map[string]any{ "owner": "octocat", "owner_type": "user", @@ -934,12 +879,9 @@ func Test_GetProjectItem(t *testing.T) { }, { name: "api error", - mockedClient: mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.EndpointPattern{Pattern: "/orgs/{org}/projectsV2/{project}/items/{item_id}", Method: http.MethodGet}, - mockResponse(t, http.StatusInternalServerError, map[string]string{"message": "boom"}), - ), - ), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ + GetOrgsProjectsV2ItemsByOrgByProjectNumberByItemID: mockResponse(t, http.StatusInternalServerError, map[string]string{"message": "boom"}), + }), requestArgs: map[string]any{ "owner": "octo-org", "owner_type": "org", @@ -1085,24 +1027,22 @@ func Test_AddProjectItem(t *testing.T) { }{ { name: "success organization issue", - mockedClient: mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.EndpointPattern{Pattern: "/orgs/{org}/projectsV2/{project}/items", Method: http.MethodPost}, - http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - body, err := io.ReadAll(r.Body) - assert.NoError(t, err) - var payload struct { - Type string `json:"type"` - ID int `json:"id"` - } - assert.NoError(t, json.Unmarshal(body, &payload)) - assert.Equal(t, "Issue", payload.Type) - assert.Equal(t, 9876, payload.ID) - w.WriteHeader(http.StatusCreated) - _, _ = w.Write(mock.MustMarshal(orgItem)) - }), - ), - ), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ + PostOrgsProjectsV2ItemsByOrgByProjectNumber: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + body, err := io.ReadAll(r.Body) + assert.NoError(t, err) + var payload struct { + Type string `json:"type"` + ID int `json:"id"` + } + assert.NoError(t, json.Unmarshal(body, &payload)) + assert.Equal(t, "Issue", payload.Type) + assert.Equal(t, 9876, payload.ID) + w.WriteHeader(http.StatusCreated) + data, _ := json.Marshal(orgItem) + _, _ = w.Write(data) + }), + }), requestArgs: map[string]any{ "owner": "octo-org", "owner_type": "org", @@ -1116,24 +1056,22 @@ func Test_AddProjectItem(t *testing.T) { }, { name: "success user pull request", - mockedClient: mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.EndpointPattern{Pattern: "/users/{user}/projectsV2/{project}/items", Method: http.MethodPost}, - http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - body, err := io.ReadAll(r.Body) - assert.NoError(t, err) - var payload struct { - Type string `json:"type"` - ID int `json:"id"` - } - assert.NoError(t, json.Unmarshal(body, &payload)) - assert.Equal(t, "PullRequest", payload.Type) - assert.Equal(t, 7654, payload.ID) - w.WriteHeader(http.StatusCreated) - _, _ = w.Write(mock.MustMarshal(userItem)) - }), - ), - ), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ + PostUsersProjectsV2ItemsByUsernameByProjectNumber: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + body, err := io.ReadAll(r.Body) + assert.NoError(t, err) + var payload struct { + Type string `json:"type"` + ID int `json:"id"` + } + assert.NoError(t, json.Unmarshal(body, &payload)) + assert.Equal(t, "PullRequest", payload.Type) + assert.Equal(t, 7654, payload.ID) + w.WriteHeader(http.StatusCreated) + data, _ := json.Marshal(userItem) + _, _ = w.Write(data) + }), + }), requestArgs: map[string]any{ "owner": "octocat", "owner_type": "user", @@ -1147,12 +1085,9 @@ func Test_AddProjectItem(t *testing.T) { }, { name: "api error", - mockedClient: mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.EndpointPattern{Pattern: "/orgs/{org}/projectsV2/{project}/items", Method: http.MethodPost}, - mockResponse(t, http.StatusInternalServerError, map[string]string{"message": "boom"}), - ), - ), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ + PostOrgsProjectsV2ItemsByOrgByProjectNumber: mockResponse(t, http.StatusInternalServerError, map[string]string{"message": "boom"}), + }), requestArgs: map[string]any{ "owner": "octo-org", "owner_type": "org", @@ -1309,27 +1244,25 @@ func Test_UpdateProjectItem(t *testing.T) { }{ { name: "success organization update", - mockedClient: mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.EndpointPattern{Pattern: "/orgs/{org}/projectsV2/{project}/items/{item_id}", Method: http.MethodPatch}, - http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - body, err := io.ReadAll(r.Body) - assert.NoError(t, err) - var payload struct { - Fields []struct { - ID int `json:"id"` - Value interface{} `json:"value"` - } `json:"fields"` - } - assert.NoError(t, json.Unmarshal(body, &payload)) - require.Len(t, payload.Fields, 1) - assert.Equal(t, 101, payload.Fields[0].ID) - assert.Equal(t, "Done", payload.Fields[0].Value) - w.WriteHeader(http.StatusOK) - _, _ = w.Write(mock.MustMarshal(orgUpdatedItem)) - }), - ), - ), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ + PatchOrgsProjectsV2ItemsByOrgByProjectNumberByItemID: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + body, err := io.ReadAll(r.Body) + assert.NoError(t, err) + var payload struct { + Fields []struct { + ID int `json:"id"` + Value interface{} `json:"value"` + } `json:"fields"` + } + assert.NoError(t, json.Unmarshal(body, &payload)) + require.Len(t, payload.Fields, 1) + assert.Equal(t, 101, payload.Fields[0].ID) + assert.Equal(t, "Done", payload.Fields[0].Value) + w.WriteHeader(http.StatusOK) + data, _ := json.Marshal(orgUpdatedItem) + _, _ = w.Write(data) + }), + }), requestArgs: map[string]any{ "owner": "octo-org", "owner_type": "org", @@ -1344,27 +1277,25 @@ func Test_UpdateProjectItem(t *testing.T) { }, { name: "success user update", - mockedClient: mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.EndpointPattern{Pattern: "/users/{user}/projectsV2/{project}/items/{item_id}", Method: http.MethodPatch}, - http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - body, err := io.ReadAll(r.Body) - assert.NoError(t, err) - var payload struct { - Fields []struct { - ID int `json:"id"` - Value interface{} `json:"value"` - } `json:"fields"` - } - assert.NoError(t, json.Unmarshal(body, &payload)) - require.Len(t, payload.Fields, 1) - assert.Equal(t, 202, payload.Fields[0].ID) - assert.Equal(t, 42.0, payload.Fields[0].Value) - w.WriteHeader(http.StatusOK) - _, _ = w.Write(mock.MustMarshal(userUpdatedItem)) - }), - ), - ), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ + PatchUsersProjectsV2ItemsByUsernameByProjectNumberByItemID: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + body, err := io.ReadAll(r.Body) + assert.NoError(t, err) + var payload struct { + Fields []struct { + ID int `json:"id"` + Value interface{} `json:"value"` + } `json:"fields"` + } + assert.NoError(t, json.Unmarshal(body, &payload)) + require.Len(t, payload.Fields, 1) + assert.Equal(t, 202, payload.Fields[0].ID) + assert.Equal(t, 42.0, payload.Fields[0].Value) + w.WriteHeader(http.StatusOK) + data, _ := json.Marshal(userUpdatedItem) + _, _ = w.Write(data) + }), + }), requestArgs: map[string]any{ "owner": "octocat", "owner_type": "user", @@ -1379,12 +1310,9 @@ func Test_UpdateProjectItem(t *testing.T) { }, { name: "api error", - mockedClient: mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.EndpointPattern{Pattern: "/orgs/{org}/projectsV2/{project}/items/{item_id}", Method: http.MethodPatch}, - mockResponse(t, http.StatusInternalServerError, map[string]string{"message": "boom"}), - ), - ), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ + PatchOrgsProjectsV2ItemsByOrgByProjectNumberByItemID: mockResponse(t, http.StatusInternalServerError, map[string]string{"message": "boom"}), + }), requestArgs: map[string]any{ "owner": "octo-org", "owner_type": "org", @@ -1579,14 +1507,11 @@ func Test_DeleteProjectItem(t *testing.T) { }{ { name: "success organization delete", - mockedClient: mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.EndpointPattern{Pattern: "/orgs/{org}/projectsV2/{project}/items/{item_id}", Method: http.MethodDelete}, - http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { - w.WriteHeader(http.StatusNoContent) - }), - ), - ), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ + DeleteOrgsProjectsV2ItemsByOrgByProjectNumberByItemID: http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(http.StatusNoContent) + }), + }), requestArgs: map[string]any{ "owner": "octo-org", "owner_type": "org", @@ -1597,14 +1522,11 @@ func Test_DeleteProjectItem(t *testing.T) { }, { name: "success user delete", - mockedClient: mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.EndpointPattern{Pattern: "/users/{user}/projectsV2/{project}/items/{item_id}", Method: http.MethodDelete}, - http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { - w.WriteHeader(http.StatusNoContent) - }), - ), - ), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ + DeleteUsersProjectsV2ItemsByUsernameByProjectNumberByItemID: http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(http.StatusNoContent) + }), + }), requestArgs: map[string]any{ "owner": "octocat", "owner_type": "user", @@ -1615,12 +1537,9 @@ func Test_DeleteProjectItem(t *testing.T) { }, { name: "api error", - mockedClient: mock.NewMockedHTTPClient( - mock.WithRequestMatchHandler( - mock.EndpointPattern{Pattern: "/orgs/{org}/projectsV2/{project}/items/{item_id}", Method: http.MethodDelete}, - mockResponse(t, http.StatusInternalServerError, map[string]string{"message": "boom"}), - ), - ), + mockedClient: MockHTTPClientWithHandlers(map[string]http.HandlerFunc{ + DeleteOrgsProjectsV2ItemsByOrgByProjectNumberByItemID: mockResponse(t, http.StatusInternalServerError, map[string]string{"message": "boom"}), + }), requestArgs: map[string]any{ "owner": "octo-org", "owner_type": "org",