@@ -2090,6 +2090,112 @@ func Test_CreateIssue(t *testing.T) {
20902090 }
20912091}
20922092
2093+ func TestIssueWriteReportsUnappliedLabels (t * testing.T ) {
2094+ tests := []struct {
2095+ name string
2096+ method string
2097+ requestedLabels []string
2098+ appliedLabels []string
2099+ expectedMissing string
2100+ expectedUnexpected string
2101+ }{
2102+ {
2103+ name : "create reports partially applied labels" ,
2104+ method : "create" ,
2105+ requestedLabels : []string {"bug" , "enhancement" },
2106+ appliedLabels : []string {"bug" },
2107+ expectedMissing : `missing=["enhancement"]` ,
2108+ expectedUnexpected : "unexpected=[]" ,
2109+ },
2110+ {
2111+ name : "update reports silently dropped labels" ,
2112+ method : "update" ,
2113+ requestedLabels : []string {"enhancement" },
2114+ appliedLabels : []string {"existing" },
2115+ expectedMissing : `missing=["enhancement"]` ,
2116+ expectedUnexpected : `unexpected=["existing"]` ,
2117+ },
2118+ {
2119+ name : "update reports labels that were not cleared" ,
2120+ method : "update" ,
2121+ requestedLabels : []string {},
2122+ appliedLabels : []string {"existing" },
2123+ expectedMissing : "missing=[]" ,
2124+ expectedUnexpected : `unexpected=["existing"]` ,
2125+ },
2126+ }
2127+
2128+ for _ , tc := range tests {
2129+ t .Run (tc .name , func (t * testing.T ) {
2130+ responseLabels := make ([]* github.Label , 0 , len (tc .appliedLabels ))
2131+ for _ , label := range tc .appliedLabels {
2132+ responseLabels = append (responseLabels , & github.Label {Name : label })
2133+ }
2134+ responseIssue := & github.Issue {
2135+ ID : github .Ptr (int64 (123 )),
2136+ Number : github .Ptr (123 ),
2137+ HTMLURL : github .Ptr ("https://github.com/owner/repo/issues/123" ),
2138+ Labels : responseLabels ,
2139+ }
2140+
2141+ endpoint := PostReposIssuesByOwnerByRepo
2142+ status := http .StatusCreated
2143+ if tc .method == "update" {
2144+ endpoint = PatchReposIssuesByOwnerByRepoByIssueNumber
2145+ status = http .StatusOK
2146+ }
2147+ restHTTPClient := MockHTTPClientWithHandlers (map [string ]http.HandlerFunc {
2148+ endpoint : mockResponse (t , status , responseIssue ),
2149+ })
2150+ restRequests := & requestCountingTransport {inner : restHTTPClient .Transport }
2151+ restHTTPClient .Transport = restRequests
2152+
2153+ gqlHTTPClient := githubv4mock .NewMockedHTTPClient ()
2154+ gqlRequests := & requestCountingTransport {inner : gqlHTTPClient .Transport }
2155+ gqlHTTPClient .Transport = gqlRequests
2156+
2157+ requestLabels := make ([]any , len (tc .requestedLabels ))
2158+ for i , label := range tc .requestedLabels {
2159+ requestLabels [i ] = label
2160+ }
2161+ requestArgs := map [string ]any {
2162+ "method" : tc .method ,
2163+ "owner" : "owner" ,
2164+ "repo" : "repo" ,
2165+ "labels" : requestLabels ,
2166+ }
2167+ if tc .method == "create" {
2168+ requestArgs ["title" ] = "Test issue"
2169+ } else {
2170+ requestArgs ["issue_number" ] = float64 (123 )
2171+ }
2172+
2173+ deps := BaseDeps {
2174+ Client : mustNewGHClient (t , restHTTPClient ),
2175+ GQLClient : githubv4 .NewClient (gqlHTTPClient ),
2176+ }
2177+ serverTool := IssueWrite (translations .NullTranslationHelper )
2178+ handler := serverTool .Handler (deps )
2179+ request := createMCPRequest (requestArgs )
2180+
2181+ result , err := handler (ContextWithDeps (context .Background (), deps ), & request )
2182+ require .NoError (t , err )
2183+ require .True (t , result .IsError )
2184+ assert .Equal (t , 1 , restRequests .count , "label verification must use the write response without a readback" )
2185+ assert .Zero (t , gqlRequests .count , "label verification must not make a GraphQL readback" )
2186+
2187+ resultText := getErrorResult (t , result ).Text
2188+ assert .Contains (t , resultText , "issue " + tc .method + "d but requested labels were not fully applied" )
2189+ assert .Contains (t , resultText , fmt .Sprintf ("requested=%q" , tc .requestedLabels ))
2190+ assert .Contains (t , resultText , fmt .Sprintf ("applied=%q" , tc .appliedLabels ))
2191+ assert .Contains (t , resultText , tc .expectedMissing )
2192+ assert .Contains (t , resultText , tc .expectedUnexpected )
2193+ assert .Contains (t , resultText , `issue_url="https://github.com/owner/repo/issues/123"` )
2194+ assert .Contains (t , resultText , "AddLabelsToLabelable permission" )
2195+ })
2196+ }
2197+ }
2198+
20932199// Test_IssueWrite_MCPAppsFeature_UIGate verifies the MCP Apps feature UI gate
20942200// behavior: UI clients get a form message, non-UI clients execute directly.
20952201func Test_IssueWrite_MCPAppsFeature_UIGate (t * testing.T ) {
0 commit comments