|
8 | 8 |
|
9 | 9 | "github.com/github/github-mcp-server/internal/githubv4mock" |
10 | 10 | "github.com/github/github-mcp-server/internal/toolsnaps" |
| 11 | + "github.com/github/github-mcp-server/pkg/http/headers" |
| 12 | + transportpkg "github.com/github/github-mcp-server/pkg/http/transport" |
11 | 13 | "github.com/github/github-mcp-server/pkg/inventory" |
12 | 14 | "github.com/github/github-mcp-server/pkg/translations" |
13 | 15 | gogithub "github.com/google/go-github/v87/github" |
@@ -1802,4 +1804,101 @@ func TestGranularSetIssueFields(t *testing.T) { |
1802 | 1804 | require.NoError(t, err) |
1803 | 1805 | assert.False(t, result.IsError) |
1804 | 1806 | }) |
| 1807 | + |
| 1808 | + t.Run("sends GraphQL-Features: update_issue_suggestions header on mutation", func(t *testing.T) { |
| 1809 | + matchers := []githubv4mock.Matcher{ |
| 1810 | + githubv4mock.NewQueryMatcher( |
| 1811 | + struct { |
| 1812 | + Repository struct { |
| 1813 | + Issue struct { |
| 1814 | + ID githubv4.ID |
| 1815 | + } `graphql:"issue(number: $issueNumber)"` |
| 1816 | + } `graphql:"repository(owner: $owner, name: $repo)"` |
| 1817 | + }{}, |
| 1818 | + map[string]any{ |
| 1819 | + "owner": githubv4.String("owner"), |
| 1820 | + "repo": githubv4.String("repo"), |
| 1821 | + "issueNumber": githubv4.Int(5), |
| 1822 | + }, |
| 1823 | + githubv4mock.DataResponse(map[string]any{ |
| 1824 | + "repository": map[string]any{ |
| 1825 | + "issue": map[string]any{"id": "ISSUE_123"}, |
| 1826 | + }, |
| 1827 | + }), |
| 1828 | + ), |
| 1829 | + githubv4mock.NewMutationMatcher( |
| 1830 | + struct { |
| 1831 | + SetIssueFieldValue struct { |
| 1832 | + Issue struct { |
| 1833 | + ID githubv4.ID |
| 1834 | + Number githubv4.Int |
| 1835 | + URL githubv4.String |
| 1836 | + } |
| 1837 | + IssueFieldValues []struct { |
| 1838 | + TextValue struct { |
| 1839 | + Value string |
| 1840 | + } `graphql:"... on IssueFieldTextValue"` |
| 1841 | + SingleSelectValue struct { |
| 1842 | + Name string |
| 1843 | + } `graphql:"... on IssueFieldSingleSelectValue"` |
| 1844 | + DateValue struct { |
| 1845 | + Value string |
| 1846 | + } `graphql:"... on IssueFieldDateValue"` |
| 1847 | + NumberValue struct { |
| 1848 | + Value float64 |
| 1849 | + } `graphql:"... on IssueFieldNumberValue"` |
| 1850 | + } |
| 1851 | + } `graphql:"setIssueFieldValue(input: $input)"` |
| 1852 | + }{}, |
| 1853 | + SetIssueFieldValueInput{ |
| 1854 | + IssueID: githubv4.ID("ISSUE_123"), |
| 1855 | + IssueFields: []IssueFieldCreateOrUpdateInput{ |
| 1856 | + { |
| 1857 | + FieldID: githubv4.ID("FIELD_1"), |
| 1858 | + TextValue: githubv4.NewString(githubv4.String("hello")), |
| 1859 | + }, |
| 1860 | + }, |
| 1861 | + }, |
| 1862 | + nil, |
| 1863 | + githubv4mock.DataResponse(map[string]any{ |
| 1864 | + "setIssueFieldValue": map[string]any{ |
| 1865 | + "issue": map[string]any{ |
| 1866 | + "id": "ISSUE_123", |
| 1867 | + "number": 5, |
| 1868 | + "url": "https://github.com/owner/repo/issues/5", |
| 1869 | + }, |
| 1870 | + }, |
| 1871 | + }), |
| 1872 | + ), |
| 1873 | + } |
| 1874 | + |
| 1875 | + // Build a transport chain matching production: GraphQLFeaturesTransport |
| 1876 | + // wraps a header-capturing spy, which forwards to the mock's RoundTripper. |
| 1877 | + // This verifies the mutation request sets the update_issue_suggestions |
| 1878 | + // feature flag so the rationale/suggest input fields are accepted. |
| 1879 | + mockClient := githubv4mock.NewMockedHTTPClient(matchers...) |
| 1880 | + spy := &headerCaptureTransport{inner: mockClient.Transport} |
| 1881 | + httpClient := &http.Client{ |
| 1882 | + Transport: &transportpkg.GraphQLFeaturesTransport{Transport: spy}, |
| 1883 | + } |
| 1884 | + gqlClient := githubv4.NewClient(httpClient) |
| 1885 | + deps := BaseDeps{GQLClient: gqlClient} |
| 1886 | + serverTool := GranularSetIssueFields(translations.NullTranslationHelper) |
| 1887 | + handler := serverTool.Handler(deps) |
| 1888 | + |
| 1889 | + request := createMCPRequest(map[string]any{ |
| 1890 | + "owner": "owner", |
| 1891 | + "repo": "repo", |
| 1892 | + "issue_number": float64(5), |
| 1893 | + "fields": []any{ |
| 1894 | + map[string]any{"field_id": "FIELD_1", "text_value": "hello"}, |
| 1895 | + }, |
| 1896 | + }) |
| 1897 | + result, err := handler(ContextWithDeps(context.Background(), deps), &request) |
| 1898 | + require.NoError(t, err) |
| 1899 | + require.False(t, result.IsError, getTextResult(t, result).Text) |
| 1900 | + // The last request captured is the mutation; the preceding issue ID |
| 1901 | + // query does not require the feature flag. |
| 1902 | + assert.Equal(t, "update_issue_suggestions", spy.captured.Get(headers.GraphQLFeaturesHeader)) |
| 1903 | + }) |
1805 | 1904 | } |
0 commit comments