@@ -1595,78 +1595,10 @@ func Test_IssueWrite_MCPAppsFeature_UIGate(t *testing.T) {
15951595 "non-UI client should execute directly" )
15961596 })
15971597
1598- t .Run ("UI client with state change skips form and executes directly" , func (t * testing.T ) {
1599- mockBaseIssue := & github.Issue {
1600- Number : github .Ptr (1 ),
1601- Title : github .Ptr ("Test" ),
1602- State : github .Ptr ("open" ),
1603- HTMLURL : github .Ptr ("https://github.com/owner/repo/issues/1" ),
1604- }
1605- issueIDQueryResponse := githubv4mock .DataResponse (map [string ]any {
1606- "repository" : map [string ]any {
1607- "issue" : map [string ]any {
1608- "id" : "I_kwDOA0xdyM50BPaO" ,
1609- },
1610- },
1611- })
1612- closeSuccessResponse := githubv4mock .DataResponse (map [string ]any {
1613- "closeIssue" : map [string ]any {
1614- "issue" : map [string ]any {
1615- "id" : "I_kwDOA0xdyM50BPaO" ,
1616- "number" : 1 ,
1617- "url" : "https://github.com/owner/repo/issues/1" ,
1618- "state" : "CLOSED" ,
1619- },
1620- },
1621- })
1622- completedReason := IssueClosedStateReasonCompleted
1623-
1624- closeClient := mustNewGHClient (t , MockHTTPClientWithHandlers (map [string ]http.HandlerFunc {
1625- PatchReposIssuesByOwnerByRepoByIssueNumber : mockResponse (t , http .StatusOK , mockBaseIssue ),
1626- }))
1627- closeGQLClient := githubv4 .NewClient (githubv4mock .NewMockedHTTPClient (
1628- githubv4mock .NewQueryMatcher (
1629- struct {
1630- Repository struct {
1631- Issue struct {
1632- ID githubv4.ID
1633- } `graphql:"issue(number: $issueNumber)"`
1634- } `graphql:"repository(owner: $owner, name: $repo)"`
1635- }{},
1636- map [string ]any {
1637- "owner" : githubv4 .String ("owner" ),
1638- "repo" : githubv4 .String ("repo" ),
1639- "issueNumber" : githubv4 .Int (1 ),
1640- },
1641- issueIDQueryResponse ,
1642- ),
1643- githubv4mock .NewMutationMatcher (
1644- struct {
1645- CloseIssue struct {
1646- Issue struct {
1647- ID githubv4.ID
1648- Number githubv4.Int
1649- URL githubv4.String
1650- State githubv4.String
1651- }
1652- } `graphql:"closeIssue(input: $input)"`
1653- }{},
1654- CloseIssueInput {
1655- IssueID : "I_kwDOA0xdyM50BPaO" ,
1656- StateReason : & completedReason ,
1657- },
1658- nil ,
1659- closeSuccessResponse ,
1660- ),
1661- ))
1662-
1663- closeDeps := BaseDeps {
1664- Client : closeClient ,
1665- GQLClient : closeGQLClient ,
1666- featureChecker : featureCheckerFor (MCPAppsFeatureFlag ),
1667- }
1668- closeHandler := serverTool .Handler (closeDeps )
1669-
1598+ t .Run ("UI client with state change routes through UI form" , func (t * testing.T ) {
1599+ // state/state_reason/duplicate_of are form params (the issue-write view
1600+ // renders close/reopen controls), so a call carrying them must go to
1601+ // the form rather than execute directly.
16701602 request := createMCPRequestWithSession (t , ClientNameVSCodeInsiders , true , map [string ]any {
16711603 "method" : "update" ,
16721604 "owner" : "owner" ,
@@ -1675,14 +1607,12 @@ func Test_IssueWrite_MCPAppsFeature_UIGate(t *testing.T) {
16751607 "state" : "closed" ,
16761608 "state_reason" : "completed" ,
16771609 })
1678- result , err := closeHandler (ContextWithDeps (context .Background (), closeDeps ), & request )
1610+ result , err := handler (ContextWithDeps (context .Background (), deps ), & request )
16791611 require .NoError (t , err )
16801612
16811613 textContent := getTextResult (t , result )
1682- assert .NotContains (t , textContent .Text , "Ready to update issue" ,
1683- "state change should skip UI form" )
1684- assert .Contains (t , textContent .Text , "https://github.com/owner/repo/issues/1" ,
1685- "state change should execute directly and return issue URL" )
1614+ assert .Contains (t , textContent .Text , "Ready to update issue #1" ,
1615+ "state change should route through UI form" )
16861616 })
16871617
16881618 t .Run ("UI client update without state change returns form message" , func (t * testing.T ) {
@@ -1701,61 +1631,10 @@ func Test_IssueWrite_MCPAppsFeature_UIGate(t *testing.T) {
17011631 "update without state should show UI form" )
17021632 })
17031633
1704- t .Run ("UI client with issue_fields skips form and executes directly" , func (t * testing.T ) {
1705- // The MCP App form does not collect or re-send issue_fields, so a call
1706- // carrying them must bypass the form and apply the values directly.
1707- fieldsClient := mustNewGHClient (t , MockHTTPClientWithHandlers (map [string ]http.HandlerFunc {
1708- PostReposIssuesByOwnerByRepo : expectRequestBody (t , map [string ]any {
1709- "title" : "Issue with fields" ,
1710- "body" : "" ,
1711- "labels" : []any {},
1712- "assignees" : []any {},
1713- "issue_field_values" : []any {
1714- map [string ]any {"field_id" : float64 (101 ), "value" : "P1" },
1715- },
1716- }).andThen (
1717- mockResponse (t , http .StatusCreated , & github.Issue {
1718- Number : github .Ptr (125 ),
1719- Title : github .Ptr ("Issue with fields" ),
1720- HTMLURL : github .Ptr ("https://github.com/owner/repo/issues/125" ),
1721- State : github .Ptr ("open" ),
1722- }),
1723- ),
1724- }))
1725- fieldsGQLClient := githubv4 .NewClient (githubv4mock .NewMockedHTTPClient (
1726- githubv4mock .NewQueryMatcher (
1727- issueFieldWriteMetadataQuery {},
1728- map [string ]any {
1729- "owner" : githubv4 .String ("owner" ),
1730- "repo" : githubv4 .String ("repo" ),
1731- },
1732- githubv4mock .DataResponse (map [string ]any {
1733- "repository" : map [string ]any {
1734- "issueFields" : map [string ]any {
1735- "nodes" : []any {
1736- map [string ]any {
1737- "__typename" : "IssueFieldSingleSelect" ,
1738- "fullDatabaseId" : "101" ,
1739- "name" : "Priority" ,
1740- "dataType" : "single_select" ,
1741- "options" : []any {
1742- map [string ]any {"fullDatabaseId" : "9001" , "name" : "P1" },
1743- },
1744- },
1745- },
1746- },
1747- },
1748- }),
1749- ),
1750- ))
1751-
1752- fieldsDeps := BaseDeps {
1753- Client : fieldsClient ,
1754- GQLClient : fieldsGQLClient ,
1755- featureChecker : featureCheckerFor (MCPAppsFeatureFlag ),
1756- }
1757- fieldsHandler := serverTool .Handler (fieldsDeps )
1758-
1634+ t .Run ("UI client with issue_fields routes through UI form" , func (t * testing.T ) {
1635+ // issue_fields is now a form param (the issue-write view renders a
1636+ // per-field editor), so a call carrying it must go to the form rather
1637+ // than execute directly.
17591638 request := createMCPRequestWithSession (t , ClientNameVSCodeInsiders , true , map [string ]any {
17601639 "method" : "create" ,
17611640 "owner" : "owner" ,
@@ -1765,14 +1644,12 @@ func Test_IssueWrite_MCPAppsFeature_UIGate(t *testing.T) {
17651644 map [string ]any {"field_name" : "Priority" , "field_option_name" : "P1" },
17661645 },
17671646 })
1768- result , err := fieldsHandler (ContextWithDeps (context .Background (), fieldsDeps ), & request )
1647+ result , err := handler (ContextWithDeps (context .Background (), deps ), & request )
17691648 require .NoError (t , err )
17701649
17711650 textContent := getTextResult (t , result )
1772- assert .NotContains (t , textContent .Text , "Ready to create an issue" ,
1773- "issue_fields should skip UI form" )
1774- assert .Contains (t , textContent .Text , "https://github.com/owner/repo/issues/125" ,
1775- "issue_fields call should execute directly and return issue URL" )
1651+ assert .Contains (t , textContent .Text , "Ready to create an issue" ,
1652+ "issue_fields should route through UI form" )
17761653 })
17771654
17781655 t .Run ("UI client with labels skips form and executes directly" , func (t * testing.T ) {
@@ -1810,10 +1687,10 @@ func Test_issueWriteHasNonFormParams(t *testing.T) {
18101687 {name : "assignees present" , args : map [string ]any {"title" : "t" , "assignees" : []any {"octocat" }}, want : true },
18111688 {name : "milestone present" , args : map [string ]any {"title" : "t" , "milestone" : float64 (2 )}, want : true },
18121689 {name : "type present" , args : map [string ]any {"title" : "t" , "type" : "Bug" }, want : true },
1813- {name : "issue_fields present" , args : map [string ]any {"issue_fields" : []any {map [string ]any {"field_name" : "Priority" }}}, want : true },
1814- {name : "state present" , args : map [string ]any {"state" : "closed" }, want : true },
1815- {name : "state_reason present" , args : map [string ]any {"state_reason" : "completed" }, want : true },
1816- {name : "duplicate_of present" , args : map [string ]any {"duplicate_of" : float64 (7 )}, want : true },
1690+ {name : "issue_fields present" , args : map [string ]any {"issue_fields" : []any {map [string ]any {"field_name" : "Priority" }}}, want : false },
1691+ {name : "state present" , args : map [string ]any {"state" : "closed" }, want : false },
1692+ {name : "state_reason present" , args : map [string ]any {"state_reason" : "completed" }, want : false },
1693+ {name : "duplicate_of present" , args : map [string ]any {"duplicate_of" : float64 (7 )}, want : false },
18171694 {name : "nil value is ignored" , args : map [string ]any {"issue_fields" : nil }, want : false },
18181695 }
18191696
0 commit comments