Skip to content

Commit 0702d8e

Browse files
committed
fix state reason validation logic and update tests
1 parent a48e5b6 commit 0702d8e

File tree

2 files changed

+31
-8
lines changed

2 files changed

+31
-8
lines changed

pkg/github/issues.go

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1226,18 +1226,26 @@ func UpdateIssue(getClient GetClientFn, getGQLClient GetGQLClientFn, t translati
12261226
return mcp.NewToolResultError(err.Error()), nil
12271227
}
12281228

1229-
// If closing as duplicate, use GraphQL API, otherwise use REST API for state changes
1230-
if stateReason == "duplicate" {
1229+
// Validate state_reason usage
1230+
if stateReason != "" && state == "" {
1231+
return mcp.NewToolResultError("state_reason can only be used when state is also provided"), nil
1232+
}
1233+
if state == "open" && stateReason != "" && stateReason != "reopened" {
1234+
return mcp.NewToolResultError("when state is 'open', state_reason can only be 'reopened'"), nil
1235+
}
1236+
if state == "closed" && stateReason != "" && stateReason != "completed" && stateReason != "not_planned" && stateReason != "duplicate" {
1237+
return mcp.NewToolResultError("when state is 'closed', state_reason can only be 'completed', 'not_planned', or 'duplicate'"), nil
1238+
}
1239+
1240+
// Use GraphQL for duplicate closure, REST for everything else
1241+
if state == "closed" && stateReason == "duplicate" {
12311242
gqlUpdateNeeded = true
1232-
} else {
1233-
if state != "" {
1234-
issueRequest.State = github.Ptr(state)
1235-
restUpdateNeeded = true
1236-
}
1243+
} else if state != "" {
1244+
issueRequest.State = github.Ptr(state)
12371245
if stateReason != "" {
12381246
issueRequest.StateReason = github.Ptr(stateReason)
1239-
restUpdateNeeded = true
12401247
}
1248+
restUpdateNeeded = true
12411249
}
12421250

12431251
duplicateOf, err := OptionalIntParam(request, "duplicate_of")

pkg/github/issues_test.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1234,6 +1234,19 @@ func Test_UpdateIssue(t *testing.T) {
12341234
expectError: false, // Error is returned in the result, not as Go error
12351235
expectedErrMsg: "No update parameters provided.",
12361236
},
1237+
{
1238+
name: "state_reason without state should fail",
1239+
mockedClient: mock.NewMockedHTTPClient(), // No API calls expected
1240+
requestArgs: map[string]interface{}{
1241+
"owner": "owner",
1242+
"repo": "repo",
1243+
"issue_number": float64(123),
1244+
// No state provided
1245+
"state_reason": "not_planned",
1246+
},
1247+
expectError: true,
1248+
expectedErrMsg: "state_reason can only be used when state is also provided",
1249+
},
12371250
}
12381251

12391252
for _, tc := range tests {
@@ -1379,6 +1392,7 @@ func Test_UpdateIssue_CloseAsDuplicate(t *testing.T) {
13791392
"owner": "owner",
13801393
"repo": "repo",
13811394
"issue_number": float64(123),
1395+
"state": "closed",
13821396
"state_reason": "duplicate",
13831397
// Missing duplicate_of
13841398
},
@@ -1412,6 +1426,7 @@ func Test_UpdateIssue_CloseAsDuplicate(t *testing.T) {
14121426
"owner": "owner",
14131427
"repo": "repo",
14141428
"issue_number": float64(123),
1429+
"state": "closed",
14151430
"state_reason": "duplicate",
14161431
"duplicate_of": float64(999),
14171432
},

0 commit comments

Comments
 (0)