Skip to content

Commit 53cb8d2

Browse files
olaservoclaude
andcommitted
test: prove real handler output conforms to every declared output schema
Extends TestIssueReadOutputValidatesAgainstDeclaredSchema to the remaining six tools, taking method coverage from 3 of 28 to 28 of 28. Each tool gets its own file; each subtest runs the real handler against the existing mock harness, then validates the emitted payload against the schema the tool advertises. Validating the text block is equivalent to validating what a client receives, because the structured-content mirror publishes those exact bytes as structuredContent. Beyond the happy path, the tests cover the cases that actually exercise a schema's required sets: empty collections, sparse objects with every optional field absent, and — for actions_get — a different single surviving key per method, so a non-first branch of each anyOf is exercised rather than always the first. Fixes a latent bug the tests surfaced. go-github decodes GetWorkflowRunByID, GetWorkflowJobByID and GetWorkflowRunUsageByID into a *T, so a 200 carrying a null body leaves the pointer nil and json.Marshal emits the literal `null`, which cannot validate against an object-rooted schema. Same class as the sub-issues bug already fixed here; these three now report the anomaly instead of returning a payload no caller can use. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1 parent 9b7d725 commit 53cb8d2

8 files changed

Lines changed: 2620 additions & 0 deletions

pkg/github/actions.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -802,6 +802,12 @@ func getWorkflowRun(ctx context.Context, client *github.Client, owner, repo stri
802802
return ghErrors.NewGitHubAPIErrorResponse(ctx, "failed to get workflow run", resp, err), nil, nil
803803
}
804804
defer func() { _ = resp.Body.Close() }()
805+
// go-github decodes into a *WorkflowRun, so a 200 carrying a null body
806+
// leaves it nil and json.Marshal would emit the literal `null`. Report the
807+
// anomaly rather than returning a payload no caller can use.
808+
if workflowRun == nil {
809+
return utils.NewToolResultError("workflow run response was empty"), nil, nil
810+
}
805811
r, err := json.Marshal(workflowRun)
806812
if err != nil {
807813
return nil, nil, fmt.Errorf("failed to marshal workflow run: %w", err)
@@ -815,6 +821,9 @@ func getWorkflowJob(ctx context.Context, client *github.Client, owner, repo stri
815821
return ghErrors.NewGitHubAPIErrorResponse(ctx, "failed to get workflow job", resp, err), nil, nil
816822
}
817823
defer func() { _ = resp.Body.Close() }()
824+
if workflowJob == nil {
825+
return utils.NewToolResultError("workflow job response was empty"), nil, nil
826+
}
818827
r, err := json.Marshal(workflowJob)
819828
if err != nil {
820829
return nil, nil, fmt.Errorf("failed to marshal workflow job: %w", err)
@@ -1007,6 +1016,9 @@ func getWorkflowRunUsage(ctx context.Context, client *github.Client, owner, repo
10071016
}
10081017
defer func() { _ = resp.Body.Close() }()
10091018

1019+
if usage == nil {
1020+
return utils.NewToolResultError("workflow run usage response was empty"), nil, nil
1021+
}
10101022
r, err := json.Marshal(usage)
10111023
if err != nil {
10121024
return nil, nil, fmt.Errorf("failed to marshal response: %w", err)

0 commit comments

Comments
 (0)