Skip to content

Commit 77c6971

Browse files
committed
Address atomic issue creation review
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: f71d9868-eef8-4fb0-84c6-df7c9a6a0ade
1 parent 51ff1a9 commit 77c6971

3 files changed

Lines changed: 84 additions & 3 deletions

File tree

e2e/e2e_test.go

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,86 @@ func TestToolsets(t *testing.T) {
307307
require.False(t, toolsContains("pull_request_read"), "expected not to find 'pull_request_read' tool")
308308
}
309309

310+
func TestCreateIssueWithParent(t *testing.T) {
311+
t.Parallel()
312+
313+
mcpClient := setupMCPClient(t)
314+
ctx := context.Background()
315+
316+
t.Log("Getting current user...")
317+
resp, err := mcpClient.CallTool(ctx, &mcp.CallToolParams{Name: "get_me"})
318+
require.NoError(t, err, "expected to call 'get_me' tool successfully")
319+
require.False(t, resp.IsError, fmt.Sprintf("expected result not to be an error: %+v", resp))
320+
require.Len(t, resp.Content, 1, "expected content to have one item")
321+
322+
textContent, ok := resp.Content[0].(*mcp.TextContent)
323+
require.True(t, ok, "expected content to be of type TextContent")
324+
325+
var trimmedGetMeText struct {
326+
Login string `json:"login"`
327+
}
328+
err = json.Unmarshal([]byte(textContent.Text), &trimmedGetMeText)
329+
require.NoError(t, err, "expected to unmarshal text content successfully")
330+
currentOwner := trimmedGetMeText.Login
331+
332+
repoName := fmt.Sprintf("github-mcp-server-e2e-%s-%d", t.Name(), time.Now().UnixMilli())
333+
t.Logf("Creating repository %s/%s...", currentOwner, repoName)
334+
resp, err = mcpClient.CallTool(ctx, &mcp.CallToolParams{
335+
Name: "create_repository",
336+
Arguments: map[string]any{
337+
"name": repoName,
338+
"private": true,
339+
"autoInit": true,
340+
},
341+
})
342+
require.NoError(t, err, "expected to call 'create_repository' tool successfully")
343+
require.False(t, resp.IsError, fmt.Sprintf("expected result not to be an error: %+v", resp))
344+
345+
t.Cleanup(func() {
346+
ghClient := getRESTClient(t)
347+
t.Logf("Deleting repository %s/%s...", currentOwner, repoName)
348+
_, err := ghClient.Repositories.Delete(context.Background(), currentOwner, repoName)
349+
require.NoError(t, err, "expected to delete repository successfully")
350+
})
351+
352+
t.Logf("Creating parent issue in %s/%s...", currentOwner, repoName)
353+
resp, err = mcpClient.CallTool(ctx, &mcp.CallToolParams{
354+
Name: "issue_write",
355+
Arguments: map[string]any{
356+
"method": "create",
357+
"owner": currentOwner,
358+
"repo": repoName,
359+
"title": "Parent issue",
360+
},
361+
})
362+
require.NoError(t, err, "expected to call 'issue_write' tool successfully")
363+
require.False(t, resp.IsError, fmt.Sprintf("expected result not to be an error: %+v", resp))
364+
365+
t.Logf("Creating child issue under %s/%s#1...", currentOwner, repoName)
366+
resp, err = mcpClient.CallTool(ctx, &mcp.CallToolParams{
367+
Name: "issue_write",
368+
Arguments: map[string]any{
369+
"method": "create",
370+
"owner": currentOwner,
371+
"repo": repoName,
372+
"title": "Child issue",
373+
"parent_issue_number": 1,
374+
},
375+
})
376+
require.NoError(t, err, "expected to call 'issue_write' tool successfully")
377+
require.False(t, resp.IsError, fmt.Sprintf("expected result not to be an error: %+v", resp))
378+
379+
ghClient := getRESTClient(t)
380+
parentIssue, parentResponse, err := ghClient.Issues.Get(ctx, currentOwner, repoName, 1)
381+
require.NoError(t, err, "expected to get parent issue successfully")
382+
require.Equal(t, http.StatusOK, parentResponse.StatusCode, "expected to get parent issue successfully")
383+
384+
childIssue, childResponse, err := ghClient.Issues.Get(ctx, currentOwner, repoName, 2)
385+
require.NoError(t, err, "expected to get child issue successfully")
386+
require.Equal(t, http.StatusOK, childResponse.StatusCode, "expected to get child issue successfully")
387+
require.Equal(t, parentIssue.GetURL(), childIssue.GetParentIssueURL(), "expected child issue to reference its parent")
388+
}
389+
310390
func TestTags(t *testing.T) {
311391
t.Parallel()
312392

pkg/github/issues.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2678,7 +2678,7 @@ Options are:
26782678
switch method {
26792679
case "create":
26802680
if parentProvided {
2681-
result, err := createIssueWithParent(ctx, client, gqlClient, owner, repo, title, body, assignees, labels, milestoneNum, issueType, parentIssueNumber, parentOwner, parentRepo)
2681+
result, err := CreateIssueWithParent(ctx, client, gqlClient, owner, repo, title, body, assignees, labels, milestoneNum, issueType, parentIssueNumber, parentOwner, parentRepo)
26822682
return result, nil, err
26832683
}
26842684

@@ -2737,7 +2737,8 @@ type createIssueParentMetadataQuery struct {
27372737
} `graphql:"parentRepository: repository(owner: $parentOwner, name: $parentRepo)"`
27382738
}
27392739

2740-
func createIssueWithParent(
2740+
// CreateIssueWithParent creates an issue and attaches it to its parent in one GraphQL mutation.
2741+
func CreateIssueWithParent(
27412742
ctx context.Context,
27422743
client *github.Client,
27432744
gqlClient *githubv4.Client,

pkg/github/issues_granular.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ func GranularCreateIssue(t translations.TranslationHelperFunc) inventory.ServerT
214214
if err != nil {
215215
return utils.NewToolResultErrorFromErr("failed to get GitHub GraphQL client", err), nil, nil
216216
}
217-
result, err := createIssueWithParent(ctx, client, gqlClient, owner, repo, title, body, nil, nil, 0, "", parentIssueNumber, parentOwner, parentRepo)
217+
result, err := CreateIssueWithParent(ctx, client, gqlClient, owner, repo, title, body, nil, nil, 0, "", parentIssueNumber, parentOwner, parentRepo)
218218
return result, nil, err
219219
}
220220

0 commit comments

Comments
 (0)